{"criteria_id":"payability/v1","criteria_revision":"1.0.0","family":"payability","op":"diff","ops_not_offered":[],"corpus":{"id":"quote-diff-corpus/v1","sha256":"98cf171eb6d485a6f37b4fbcf2603e197f2b8d6ef0b19543618a3c348969235c"},"corpus_pin_definition":"The corpus pin is the SHA-256 of the corpus's canonical serialization (the corpus object excluding its sha256 field), computed and verifiable by the published harness (harness.mjs). Expected outputs inside corpus cases are derived from the criteria text by an independent implementation in gen-corpus.mjs (never the engine's code path), and each case labels its provenance (constructed from on-disk fleet records vs synthetic).","price":{"usdc_units_per_call":10000,"label":"$0.01","basis":"per pair"},"free_tier":{"per_day":100,"min_interval_ms":5000,"on_exhaustion":"402"},"input_contract":{"body":"One JSON request body: { first, second, caller? (free pre-check only) }. Each challenge is { status? (number), body (JSON object | JSON string | null), payment_required_header (base64 string | null) } with at least one carrier present.","caps":{"body_bytes":262144},"over_cap":"A request body over 262,144 bytes is a free pre-gate refusal naming the cap.","validation":"The pair is validated FREE, before any payment: shape, artefact validity, and pair-ness under the published pair rule. A malformed submission, a non-402 artefact, or a not-a-pair submission fails free and never consumes a payment. The diff itself is computed only after payment verification (or a verified-marker resume)."},"submission_form":"Each challenge: { status?: 402, body: <object|string|null>, payment_required_header: <base64 string|null> }. The body leg is the parsed body; the header leg is the base64/JSON decoding of payment_required_header.","fields":["amount","asset","network","payTo","token_account","timeout_seconds","description","memo"],"grades":["SAME","CHANGED","PRESENT-ONLY-IN-FIRST","PRESENT-ONLY-IN-SECOND","UNPARSEABLE"],"network_canon":{"solana":"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp","solana-devnet":"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1","solana-testnet":"solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z","base":"eip155:8453","base-sepolia":"eip155:84532","ethereum":"eip155:1","polygon":"eip155:137","arbitrum":"eip155:42161","optimism":"eip155:10","avalanche":"eip155:43114"},"normalisation_rule":"Normalisation (applied per challenge, before comparison): each challenge is read from its body leg (the parsed body object) and its header leg (the base64/JSON decoding of the submitted PAYMENT-REQUIRED header). Per field, if exactly one leg carries it, that is the value; if both legs carry it with equal normalised values, that is the value; if both legs carry it and the normalised values differ, the field is divergent and grades UNPARSEABLE. Field paths per leg: amount = accepts[0].amount falling back to accepts[0].maxAmountRequired; asset = accepts[0].asset; network = accepts[0].network; payTo = accepts[0].payTo; token account = accepts[0].extra.usdc_token_account, then extra.token_account, then extra.tokenAccount; timeout = accepts[0].maxTimeoutSeconds; description = resource.description, then accepts[0].description, then the body leg's top-level description; memo = accepts[0].extra.memo, then the body leg's top-level memo; resource URL = resource.url, then a string accepts[0].resource. Amounts compare by numeric value (digit strings or non-negative integers; leading zeros ignored). Networks canonicalise through the published slug-to-CAIP-2 table (solana, solana-devnet, solana-testnet, base, base-sepolia, ethereum, polygon, arbitrum, optimism, avalanche); a CAIP-2 value or an unlisted slug passes through as-is, and the report prints the normalised network form. Timeouts compare as integers. String fields compare byte for byte; a present non-string value is UNPARSEABLE. Memo values are never compared (a fresh memo differs on every legitimate re-issue): the memo field grades presence and form class only — absent, short (length < 8), prefixed-opaque, or opaque. Printed field values longer than 512 chars are printed as the first 512 chars plus a (+N more chars) suffix; grades are always computed on full values.","grade_rule":"Grades, one per field: SAME — both challenges carry the field with equal normalised values (memo: both carry a memo of the same form class, or neither carries one). CHANGED — both carry it and the normalised values differ; the report prints old to new (memo: old form class to new form class). PRESENT-ONLY-IN-FIRST / PRESENT-ONLY-IN-SECOND — exactly one challenge carries the field (memo: exactly one carries a memo). UNPARSEABLE — the field is carried but cannot be normalised under the published rules, or the two legs of one challenge diverge on it. The verdict line is a census of the eight grades (changed / same / presence-only / unparseable counts); it is not a score.","pair_rule":"Pair-ness, validated FREE before any payment, in this order: (1) a request body over 262,144 bytes is a free refusal naming the cap; (2) the body must be one JSON object { first, second, caller? (free pre-check only) } with both challenges in the published submission form (status optional; body as a JSON object, a JSON string, or null; payment_required_header as a base64 string or null; at least one carrier present) — otherwise a free refusal (pair_shape_invalid); (3) a challenge is a 402 artefact when at least one carrier decodes to a JSON object carrying an accepts array with at least one entry, and its status, when present, is 402 — otherwise a free refusal (not_a_402_artefact) naming the side; (4) both challenges must carry a resource URL and the two must be equal after stripping one trailing slash — a missing resource URL refuses free as not_a_pair / resource_unverifiable, and differing URLs refuse free as not_a_pair / different_resources.","description_flag":{"threshold_chars":500,"rule":"description_overlong_first / description_overlong_second are true when that side's description is longer than 500 characters; a risk factor only, never changing a grade."},"print_value_cap_chars":512,"verdict_grammar":{"line":"QUOTE PAIR DIFF — changed: <n> · same: <n> · presence-only: <n> · unparseable: <n> — <diff-end UTC>","vocabulary":"The line carries a census of the eight field grades. There is no composite score, grade, or band anywhere in this op."},"free_precheck":"The free pre-check (POST /precheck) validates the submitted pair under the published pair rule and returns assessability ONLY: the common signed fields, whether the pair is assessable (or the free refusal naming the failure), and the disclosure/honesty/retention lines. It returns no diff, no grade, and no field value. Free allowance: 100 per day per caller wallet, minimum interval 5,000 ms, 402 on exhaustion.","custody":{"construction":"In-memory processing within a single request lifetime; the submitted challenges persist nowhere, on any path including errors. No hash of a submitted challenge appears anywhere.","ledger_closed_fields":["payer wallet","timestamp/day","price (USDC units) with the CAD fair-market-value block (rate, rate date, basis, cad_pending, refund)","payment tx id + Solana fee (lamports)","criteria version","verdict line","changed-field count","same-field count","house flag"],"ledger_note":"The ledger list above is closed: adding a field is a criteria/handling version change. The ledger NEVER carries any submitted field value, any challenge fragment, or any hash of a challenge.","errors":"Error handling is closed-form: static strings plus numeric facts only (lengths against caps, rule ids, side names). No error path interpolates any part of a submitted challenge."},"forbidden":"Forbidden in every surface of this op (report, pre-check, errors, ledger, metrics): any composite score or grade beyond the eight published per-field grades and their census; any fetched data (this op fetches nothing); any submitted challenge content in the ledger, metrics, pre-check, or errors; any claim about the endpoint's live terms or payability.","boundary_statement":"Boundary vs C3 Breaking-Change Diff (/toll/apidiff, breaking-change-diff/v1): C3 diffs two API documents and grades breaking versus non-breaking change under its own criteria; this op diffs two 402 payment challenges for one endpoint and grades only the eight payment-term fields — C3 asks whether an API contract broke, A5 asks whether a price quote moved between two captures. Boundary vs A1 Challenge Payability Verdict (payability/v1, op check): A1 fetches one endpoint's live 402 and grades whether that single challenge is payable at all; A5 never fetches and never grades payability — it compares two already-captured challenges the caller supplies, and a pair that diffs all-SAME can still be unpayable, which A5 does not grade.","disclosure_sentence":"Submitting two captured challenges to any third party, this service included, discloses those captures to it; the challenges are processed in memory only and never stored, but the diff does not undo the disclosure.","honesty_line":"The diff grades the two captures exactly as submitted; it states what differs between those two artefacts and nothing about any challenge the endpoint serves now — a seller can change its terms again after either capture, and neither capture is fetched or verified against the live endpoint by this op.","retention":"Submitted challenges are processed in memory only and are never stored — not in KV, not in logs, not in the payments ledger, not in metrics, and not in error records. No field value, fragment, or hash of a submitted challenge is recorded anywhere. The paid report carries the grades and the submitted pair's own old-to-new values because that report is the delivery the buyer paid for. The payments ledger records only the closed field list published in the criteria. Because nothing is kept, a paid retry re-submits the pair.","payment_includes":"$0.01 per pair quote-pair diff. The fee is per pair: each submitted pair of captured 402 challenges is charged at $0.01. The payment includes: one quote-pair diff — the two challenges you submit are normalised under the published rules of payability/v1 (header-carried and body-carried forms are normalised before comparison) and compared field by field across the eight published fields (price, asset, network, payTo, token account, timeout, description, and memo presence/form), each graded SAME, CHANGED (old to new), PRESENT-ONLY-IN-FIRST, PRESENT-ONLY-IN-SECOND, or UNPARSEABLE, returned as one signed report with the pair-identity check, the description-length factors, and an as-of timestamp. No composite score is computed. A free pre-check validates the pair (artefact validity and pair-ness) before any payment and returns no diff. Both submitted challenges are processed in memory only and are never stored; the payments ledger records only the payer, the time, the price, the grade census counts, and the verdict line.","kill_line":"Kill line (M-18 / A5, under the operator's 2026-11-03 build-bank-hold doctrine): build, bank, and hold; judged at the fleet's periodic reviews. There is NO demand kill line for this item. Day-0 = first outside paid settle (accounting only; house proofs never count). Tripwires, all stop-and-review: (1) refunds >5% in any 30-day window; (2) free use >10x paid use; (3) a wrong grade from undisclosed stale or partial data (the error enters the corpus first as a regression case, corrected rules publish as a new criteria version, the corpus re-passes at 100% before serving resumes; issued reports are never amended); (4) custody: any surface retaining a submitted challenge beyond the buyer's own paid report.","never":"This op never fetches any URL; never stores a submitted challenge; never computes a composite score; never grades payability (that is A1's ground) or API-document breaking change (that is C3's ground).","service":"quote-diff","version":"1.0.0","endpoints":{"criteria":"GET /criteria (free)","precheck":"POST /precheck (free, pair validation + assessability only: no diff, no grades, no field values)","diff":"POST /diff (paid, $0.01 per pair: signed quote-pair diff)","metrics":"GET /metrics","not_offered":"Any other POST path answers NOT COVERED — this worker serves op diff only."}}