Corrections

This is the newest record of its correction, and its words are the ones the corrections log carries.

3 Sep 2026

corr_2026-09-03_002 · resolved · Restates corr_2026-09-03_001

Claim

Nobody raised this. We found it in our own archive: probe402 published 509 observations of 136 MPP endpoints as `Answered, no quote` - alive, asking to be paid, not saying how much - when every one of those endpoints had said exactly how much, in the same response header, one base64url hop away. The price was inside the challenge's `request` parameter and our parser only read the flat parameters beside it. MPP is the rail no competitor archives, and we were blind on it from our first tick to this one.

Finding

Measured 2026-09-03, read-only, against our own retained captures. Of 525 `WWW-Authenticate: Payment` challenges across 384 archived capture blobs and 162 hosts, 519 carry the charge inside a base64url `request` parameter and 4 carry the flat `amount`/`recipient` auth-params `parseMppChallenge` was written for. Nine payment providers publish the encoded form - tempo (412 challenges), evm (68), stripe (21), solana (4), x402 (4), exact (4), lightning (2), asterpay (2), usdc (2) - and every one of the 519 decodes to an object carrying an amount. The parser looked in the one place the ecosystem does not use, found nothing, and filed `no-amount-or-recipient`. EVERY affected row was then re-read against its own archived capture: all 509 of them, on 136 endpoints across 42 hosts, yield a quote - the transition is uniformly `Answered, no quote` -> `Quoted`/R14d, and none of them remains unreadable. The population is bounded by the archive's own record: a row can move only if its stored `quote_parse.mpp` is true, because the changed function returns early when no `Payment` challenge is present. THERE IS A SECOND, LARGER CLASS AND IT IS NAMED HERE RATHER THAN LEFT OUT. When a quote parses, the classifier returns early with a fixed sub-code list, so a row whose x402 arm quoted and whose MPP arm failed carries NO trace of the failed MPP parse at all. Measured: 4,188 further rows on 881 endpoints. Their outcome and their price are correct and unchanged - x402 stays the primary quote - but they record the endpoint as serving ONE rail when it serves two (rule R14a/R14b/R14c where the corrected reading gives R14e, dual-rail). On an evenly spaced sample of 188 of those 881 endpoints, 188 of 188 promote and none is unchanged. They are NOT in this record's scope because their exact enumeration needs the full re-derivation of the archive, which is queued; scoping 881 endpoints from a sample would publish an inference as a measurement. A second record follows that run.

Remedy

FIXED FORWARD, NOT REWRITTEN. The parser now reads the `request` parameter in the one place a quote is read, and the payment method the challenge names - tempo, stripe, lightning and six others - is recorded as the quote's scheme. A `request` we cannot decode is recorded as an ATTEMPTED parse that failed, never as an endpoint that published no price: the distinction is the whole point, because reading an unreadable challenge as an absent one is how this defect published a statement about 136 operators that was really a statement about us. The rubric version moves 5 -> 6 so the boundary is visible in the archive itself and not only in a changelog; the decision behind the change is recorded there, as what it decided, and is named in this record's method-change reference. NO STORED OBSERVATION IS EDITED and the archive is not migrated, under the standing rule that a stored observation is never edited and a correction annotates it: the affected rows still say what they said, under the rubric version that actually graded them, and this record is how they should be read. Re-deriving the archive under the corrected parser reproduces every other row byte-for-byte; these rows are the declared exception.

How to read the affected rows

Read these rows as `Quoted`. The endpoint answered with a payment challenge carrying a price, and our instrument could not see it. The grade understates the endpoint; it is not a fact about the endpoint at all. Rows on these endpoints from the corrected collector onward carry the quote directly and need no annotation, which is why the range closes rather than staying open. The second class described in `finding` is not annotated here: those rows are correct about the outcome and the price, and what they understate - the rail set - needs the queued re-derivation to enumerate exactly.

136 endpoints

The sealed record

The sealed object at object_key is this record with contact_ref restored, written as canonical JSON followed by one newline byte. Canonical JSON is the output of JSON.stringify for the value with every object's keys sorted: no whitespace, UTF-8. On a record raised by probe402, contact_ref is null, so this record with "contact_ref":null added rebuilds the sealed bytes: their SHA-256 and length are sealed_sha256 and sealed_bytes, the entry the manifest of manifest_date lists for object_key.

Object
raw/corrections/2026-09-03/corr_2026-09-03_002.json
Digest
a124fd7f038ee7e09fb77370cd18bcabacc29ca1af1634c3cf457492256ef91c
Bytes
8248
Listed in
/chain/2026-09-03

How to dispute a reading →

Corrections →