Measured while correcting the owd_widening_forbidden row's why under #9958, in the same file. Filed unassigned, unlabelled for triage. Not fixed there: #9958's scope fence is that row's why prose, and this is a verdict definition, which governs how every future row gets classified.
The contradiction, both halves inside packages/runtime/src/dispatcher-error-vocabulary.ts
The pending-registration member of CodeVerdict documents itself as:
Reaches a wire error.codeverbatim and the ledger does not know it. The body cannot parse. ⇒ #8846's registration input.
The file's own module header, updated for #9106, rules the opposite on both clauses:
(Since #9106 the door NARROWS — error.code takes the resolver's closed code, the unregistered spelling rides the wire's declaredCode — so such a body now parses; what an unswept producer loses instead is its semantic code, silently demoted off error.code until registered. The sweep below is what notices.)
scripts/check-dispatcher-error-vocabulary.mjs's header carries the same post-#9106 statement, so the gate and the module header agree with each other and the verdict doc comment is the lone holdout.
Why it is not cosmetic
why is specified as "the evidence, not a restatement of the verdict", so a row that records its evidence honestly now reads as contradicting the verdict it declares. The only live pending-registration row (owd_widening_forbidden) is exactly that case after #9958: its token reaches the wire in declaredCode, code carries PERMISSION_DENIED, and the body parses. The verdict is still correct — the token is live on a wire, outside the vocabulary, and registering it ratchets the row out — but a reader checking it against the doc comment sees two failed clauses and can reasonably conclude the row is misclassified.
The forward risk is the classification decision, not the reading: the next unswept producer arrives as an unclassified-site finding and someone picks a verdict from these doc comments. A declaredCode-demoted producer fails both clauses as written, and the nearest verdict whose doc comment does describe the demote is sandbox-authored ("since #9106 it is demoted to the wire's declaredCode at the door rather than reaching error.code") — which is wrong for a first-party producer and would park a real emitter outside the registration path.
Fix shape (a lead, not a spec)
Restate the pending-registration doc comment in post-#9106 terms — reaches a wire, the vocabulary does not carry it, so it is demoted off error.code onto declaredCode until registered — and keep ⇒ #8846's registration input and the ratchet, which are unchanged. Purely a doc comment; the gate parses verdict: textually and masks comments (parseDeclaration, maskComments), so no gate reads this prose.
Re-check commands:
git grep -n "The body cannot parse" -- packages/runtime/src/dispatcher-error-vocabulary.tsgit grep -n "such a body now parses" -- packages/runtime/src/dispatcher-error-vocabulary.ts scripts/check-dispatcher-error-vocabulary.mjs
Refs: #9958 (where this was measured) · #9106 · #9232 · ADR-0112.
Generated by Claude Code
Measured while correcting the
owd_widening_forbiddenrow'swhyunder #9958, in the same file. Filed unassigned, unlabelled for triage. Not fixed there: #9958's scope fence is that row'swhyprose, and this is a verdict definition, which governs how every future row gets classified.The contradiction, both halves inside
packages/runtime/src/dispatcher-error-vocabulary.tsThe
pending-registrationmember ofCodeVerdictdocuments itself as:The file's own module header, updated for #9106, rules the opposite on both clauses:
scripts/check-dispatcher-error-vocabulary.mjs's header carries the same post-#9106 statement, so the gate and the module header agree with each other and the verdict doc comment is the lone holdout.Why it is not cosmetic
whyis specified as "the evidence, not a restatement of the verdict", so a row that records its evidence honestly now reads as contradicting the verdict it declares. The only livepending-registrationrow (owd_widening_forbidden) is exactly that case after #9958: its token reaches the wire indeclaredCode,codecarriesPERMISSION_DENIED, and the body parses. The verdict is still correct — the token is live on a wire, outside the vocabulary, and registering it ratchets the row out — but a reader checking it against the doc comment sees two failed clauses and can reasonably conclude the row is misclassified.The forward risk is the classification decision, not the reading: the next unswept producer arrives as an
unclassified-sitefinding and someone picks a verdict from these doc comments. AdeclaredCode-demoted producer fails both clauses as written, and the nearest verdict whose doc comment does describe the demote issandbox-authored("since #9106 it is demoted to the wire'sdeclaredCodeat the door rather than reachingerror.code") — which is wrong for a first-party producer and would park a real emitter outside the registration path.Fix shape (a lead, not a spec)
Restate the
pending-registrationdoc comment in post-#9106 terms — reaches a wire, the vocabulary does not carry it, so it is demoted offerror.codeontodeclaredCodeuntil registered — and keep⇒ #8846's registration inputand the ratchet, which are unchanged. Purely a doc comment; the gate parsesverdict:textually and masks comments (parseDeclaration,maskComments), so no gate reads this prose.Re-check commands:
git grep -n "The body cannot parse" -- packages/runtime/src/dispatcher-error-vocabulary.tsgit grep -n "such a body now parses" -- packages/runtime/src/dispatcher-error-vocabulary.ts scripts/check-dispatcher-error-vocabulary.mjsRefs: #9958 (where this was measured) · #9106 · #9232 · ADR-0112.
Generated by Claude Code