Uh oh!
There was an error while loading. Please reload this page.
fix(tooling): dispatcher error-vocabulary scan reports lowercase codes and the three stamp positions it could not see (#9460) - #9573
Conversation
…s and the stamp positions it could not see (#9460) The gate's published bound said "only SCREAMING_SNAKE literals" and handed lowercase to check:error-code-casing. Half that delegation was real and half was a hole — and the hole is where plugin-security's live 403 `owd_widening_forbidden` sat through two ADR-0112 sweeps, read by both gates and reported by neither. Lowercase is now reported except in the two positions where check:error-code-casing reads the identical characters (`code: 'x'`, `.code = 'x'`), where it carries the D6/D6b/D6c discrimination this gate does not have. Measured first: reporting every lowercase stamp took the scan from 12 sites to 94, and all 82 new findings were D6/D6b/D6c or Zod's own issue codes. Three unseen stamp positions, all widenings: `codehelper` (a factory whose parameter carries the code, so the literal lives at the call site with no `code` token beside it — the parameter INDEX is derived, not assumed zero), `assignconst` (the assign position's constant sibling, the gap #9223 closed for object literals), and `assign` with a cast on the left. 12 -> 18 classified sites; 0 -> 2 awaiting a ledger entry. No allowlist, no narrowed pattern, no raised ceiling. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WKSnqAaoqtW3QX7SSf1Vk
#9460) The two new stamp positions the widened scan reports need members in the declaration table's own union, or every row carrying them is a tsc error. Caught by `pnpm --filter @objectstack/runtime typecheck`, not by the gate — the gate reads the table textually and never typechecks it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WKSnqAaoqtW3QX7SSf1Vk
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 40e00b64bbc424e8b7ae33ebd79b65b26be1c0fd && git checkout 40e00b64bbc424e8b7ae33ebd79b65b26be1c0fd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e6ee690999b5e47e88eece047a6b9edc311d0729 7c1840e46599a7ab74b561d61807cc575aad558b && git checkout -B drift-repro e6ee690999b5e47e88eece047a6b9edc311d0729 && git merge --no-ff 7c1840e46599a7ab74b561d61807cc575aad558b
node scripts/docs-audit/affected-docs.mjs --json e6ee690999b5e47e88eece047a6b9edc311d0729
|
Uh oh!
There was an error while loading. Please reload this page.
…on row (#9567) #9460 landed on main (PR #9573) while this branch's registration PR was in the merge queue, adding a pending-registration row for FLOW_CONVERSION_CONFLICT to dispatcher-error-vocabulary.ts's UNREGISTERED_CODE_SITES. That collided with this branch's ledger admission of the same code -- a registered code with a pending-registration row still on file fails the #8087 conformance gate in the direction the row's own docs describe ("a pending-registration row whose code is registered fails the gate in the other direction"). Ratchets the row out following the #8846/#9246 precedent exactly: delete the discharged row, fold a one-line note into the running log at the top of UNREGISTERED_CODE_SITES. owd_widening_forbidden -- the #9460 batch's other new row -- stays pending; its lowercase spelling is a naming decision for the maintainer (#9567 half 2), not a plain admission, and is untouched. node scripts/check-dispatcher-error-vocabulary.mjs: PENDING_LEDGER_REGISTRATION 2 -> 1 (only owd_widening_forbidden remains). error-envelope.conformance.test.ts (the #8087 pin): 51/51 passing. error-code-ledger.test.ts: 17/17 passing. check:generated: all 13 artifacts still up to date after the merge. Part of #9567
Fixes#9460
Widens
check:dispatcher-error-vocabularyso it measures "is this code outside the vocabulary" instead of "is this code SCREAMING_SNAKE" — scope half (1) only, per triage. ⛔ No ledger edit, no allowlist entry, no narrowed pattern, no raised ceiling.The card's premise was wrong, and the real shape is bigger
The issue diagnosed a case-sensitive pattern. It is not —
assign,classfieldandobjlitalready matched[A-Za-z]. Two explicit post-match filters dropped the value with a named delegate:And the card's own motivating producer was invisible for a completely different reason, so the prescribed one-line widening would not have found it. Measured, not assumed:
owd_widening_forbiddenis not among themowd_widening_forbiddenAll 82 of the naive widening's new findings were D6/D6b/D6c neighbours or Zod's own issue codes — it reports
ctx.addIssue({ code: 'custom' })as an unregistered ObjectStack error code, which is simply false.What was actually broken: a hole between two gates
check:error-code-casingneeds a quoted lowercase literal beside the tokencode(code: 'x',.code = 'x',code === 'x',code?: 'x' | 'y'). In those positions the delegation is genuine — that gate reads the identical characters and carries the D6/D6b/D6c discrimination this one does not have, soobjlitandassignkeep delegating.Everywhere else it was a hole, not a hand-off. A code arriving through a constant, a template, or a helper parameter has no quoted literal at the stamp site, so that gate is structurally blind — and this gate dropping it for its casing meant nobody reported it. Both gates read
object-posture-gate.tsand both reported nothing, each leaving it to the other. Those shapes now carrylowercase: 'here'.Three stamp positions the scan could not see
codehelper— a file declares one factory and throws through it everywhere. The stamp(err as any).code = codeknows the tokencodebut not the value; the call site knows the value and never writes the token. The join is the parameter, so its index names the argument to read — derived, never assumed zero, becausemakeError(status, code, message)andexposureError(message, code, status)both put it second and a first-argument rule reads a number and an English sentence as error codes. Constructors count, and a call-site argument that is itself a constant goes through the existingresolveConstant.assignconst—err.code = DENY_CODE. check-dispatcher-error-vocabulary is blind to a non-literalcode:in an object literal — a constant or template produces no finding, silently #9223 closed exactly this gap for object literals; the assign position kept it.assignwith a cast on the left — the old anchor demanded a bare identifier where(err as any).code = 'X'puts a).Predicted, then observed
Predicted: the pre-change scan cannot see the six sites this one reports, so run against the same tree with the same declaration table it must call every one of them stale.
Observed —
origin/main's script, this tree:Six producers sitting in source that the old scan reports as "moved or went away". A zero-hit control is not a finding, so the widened pattern was also fed the known lowercase stamp directly and reported it by name.
Every new find: fixed or filed, none silently classified
Six new sites, all classified with evidence in
dispatcher-error-vocabulary.ts, and the two substantive decisions filed rather than taken here:FLOW_CONVERSION_CONFLICT— live 409 from the metadata write path →pending-registration, doorrest. Filed Two live wire codes are outside the ADR-0112 vocabulary, newly measured:FLOW_CONVERSION_CONFLICT(409) andowd_widening_forbidden(403) #9567 for the spec lane.owd_widening_forbidden— live 403, wire-verbatim →pending-registration, doorrest. Filed Two live wire codes are outside the ADR-0112 vocabulary, newly measured:FLOW_CONVERSION_CONFLICT(409) andowd_widening_forbidden(403) #9567. ⛔ The producer's spelling is untouched and the ledger is untouched: half (2) is settled and registering or renaming is thepackages/speclane's call.pending-registrationis the only honest verdict available (foreign-vocabularywould be false — it is a request refusal on a wire;boot-refusalwould be false — it crosses HTTP), and it records a measurement, it does not prescribe a remedy.NO_SUCH_RUN,NOT_COMPENSABLE,PLAN_CHANGED,PREFLIGHT_FAILED—MigrationJournalRefusal, whose only consumers are two CLI commands with no HTTP boundary →boot-refusal, doornone, the [finding]MONGODB_MULTI_TENANT_UNSUPPORTEDmay be registered-but-unemittable in the error-code ledger — a boot refusal never reaches a wire envelope #8035 precedent.PENDING_LEDGER_REGISTRATIONgoes 0 → 2 and classified sites 12 → 18 — the ratchet moving in the direction it exists to move.Residual gap found while measuring and filed, not fixed: #9568 — a code held in a local ternary of literals (
sys-metadata-repository.ts's live 403NOT_CREATABLE/NOT_OVERRIDABLE) is still unresolved. Named in the published bounds too.origin/mainat 65d4fff it does not —packages/rest/src/meta-object-owd-gate.test.tsasserts the raw lowercase code on the wire, and no commit carrying that change is in the history. Recorded in #9567; out of scope here.Verification — all at
7c1840e46, the final commitThe self-test is pinned capable of failing, in both directions:
Restored byte-identical after each (empty
git diff), and--self-testgreen again.Lint & Repo Gatesis the CI job that carries this gate family — the local runs above are not that job green.File-surface note for the PM: the claim named
scripts/check-dispatcher-error-vocabulary.mjsonly.packages/runtime/src/dispatcher-error-vocabulary.tsis also edited — it is the gate's declaration half, and a widened scan that reports 6 more sites leaves the gate red until they are classified there. Same defect class, same gate family, no other claim on the file, and the row shape is pinned by the 12 rows already present. Flagging it rather than assuming it was covered.Generated by Claude Code
Generated by Claude Code