Repository navigation
[finding] check-dispatcher-error-vocabulary's delegation map is short one recognizer after the ||/?? widening, and does not record who owns a runtime-limbed chain #10762
Description
Activity
huangyiirene commented
on Aug 21, 2026 CollaboratorMore actionsTriage (finding round): promoted →
pm:queue, type Task, S-tier mechanical — comment-only edits inscripts/check-dispatcher-error-vocabulary.mjs(add the fifth recognizer spelling to the three enumerations; one clause recording that a runtime-limbed||/??chain is delegated tocheck:error-code-casing, not dropped). The card's own acceptance criterion is the dispatch clause: no self-test case may move — if one does, the delegation is not what the comments claim.
Generated by Claude Code
Independent measurement of this from the #10716 seat (the services-lane card that renames the two codes #10658 revealed), on
origin/mainat5f2e54cc6. Adding it because it is a positive control, so the zero it produces is a real zero rather than a broken probe — the same discipline #10658 used on the casing gate.Probe. Both SSO literals in
packages/plugins/plugin-auth/src/register-sso-provider.tsrenamed to unregistered SCREAMING spellings (REQUEST_DOMAIN_VERIFICATION_FAILED,VERIFY_DOMAIN_FAILED), each still in the||fallback slot:node scripts/check-dispatcher-error-vocabulary.mjs -> exit 0 OK — 21 unregistered code-stamping site(s), all classified; 1 awaiting a ledger entry (#8846). scope: 1875 non-test source files under packages/; 291 registered codes; 21 found; 21 classified.Baseline before the probe: also 21. Two unregistered codes were added and the count did not move.
Positive control, to separate "blind to the shape" from "file not in scan" — one DIRECT (non-fallback) unregistered code in that same file:
node scripts/check-dispatcher-error-vocabulary.mjs -> exit 1 [unclassified-site] packages/plugins/plugin-auth/src/register-sso-provider.ts stamps unregistered code 'ZZ_POSITIVE_CONTROL_UNREGISTERED' (objlit) and packages/runtime/src/dispatcher-error-vocabulary.ts does not classify it. scope: … 22 unregistered code-stamping site(s) found; 21 classified.The file is in scan and the gate fires on it — 21 → 22 — for the direct spelling and not for the fallback one. Both mutations were reverted;
git hash-objectconfirms the file is byte-identical to its pre-probe blob (a30734337b6aa7a60a7d8a5ec833f8b1dcbd65b1).Why it was load-bearing for #10716. That card had to choose between reusing an already-registered code and inventing a new SCREAMING spelling. This measurement closed the second door: a new spelling in the fallback slot is unregistered and invisible to both error-code gates, so it would have shipped as exactly the silent fourth state ADR-0112 D3 exists to prevent — a visible casing violation traded for an invisible registration one. PR #10858 reuses
DOMAIN_VERIFICATION_FAILED(already in the ledger for@objectstack/plugin-auth) instead, and touches no spec file.That also means this gap currently has no live victim in this file — after #10858 both codes there are registered. The gap itself is unchanged.
Generated by Claude Code
claude commented
on Aug 21, 2026 claudeboton Aug 21, 2026 – with ClaudeContributorAuthorMore actionsPM claim — dispatching now.
- branch:
claude/issue-10762-delegation-map-fallback-spelling
Checked before dispatch that this does not collide with anything in flight: it is confined to
scripts/check-dispatcher-error-vocabulary.mjs, which no open PR touches. The card asks for a comment-only change and says so; the agent is told that if a self-test case moves, the delegation is not what the comments claim — which is the card's own falsification test, not a nuisance.- session:
c970724d-303c-5614-9d20-a3f92205cfad - seat:
domain:devx
If an earlier claim comment with a different session ID appears above this one, that claim wins and this one is void.
Generated by Claude Code
- branch:
claude commented
on Aug 21, 2026 claudeboton Aug 21, 2026 – with ClaudeContributorAuthorMore actionsos-dev-report
{ "issue": 10762, "status": "done", "branch": "claude/issue-10762-delegation-map-fallback-spelling", "pr": "https://github.com/objectstack-ai/objectstack/pull/10898", "premise_still_valid": true, "summary": "The enumerations were TWO, not three: the header's #9460 declared bound (lines 90-92) and the jsdoc on keep() (lines 802-804, the function the card calls isMine). Swept for a third via QUOTED, each of the four spellings, and every delegat/owns mention -- there is none; the neighbouring delegation notes (628, 1323) talk about anchoring on the token 'code' and stay accurate after #10760. Both now carry the fifth spelling as the owning gate spells it (code: parsed?.code || 'x' / .code = e?.code ?? 'x' -- both positions and both operators, which the card's one-form paraphrase left half-stated). NO self-test case moved: 8 shapes + 102 assertions OK and 21 sites/21 classified before and after, with diff of the captured outputs empty in both directions. One discrepancy with the card, resolved in the gates' favour: the prescribed clause ('a runtime-limbed chain reduces to nothing here and is therefore delegated ... rather than by nobody') is true at the stamp site and FALSE in a local's initializer, measured on both gates' own exported entry points. At the stamp site no reduction happens at all (objlit/assign need the quote immediately, so this gate never had a shape) and the casing gate's fallback pattern now owns it -- that is where the two live SSO codes actually sit, checked on disk, so the card's exemplar belongs to that row rather than to the #9568 rule it was filed under. In a local's initializer (const code = p?.code || 'lit') the all-or-nothing rule declines it here and the casing gate anchors on code:/code?:/.code =, so it is still owned by nobody. The clause records both branches rather than the card's unqualified one, which would have installed the very 'two gates, each assuming the other' error the map exists to prevent.", "tests": "All at 278c9c6856 (final commit), clean tree, redirect-then-capture so no pipe eats an exit code. FALSIFICATION TEST (the card's acceptance criterion), before vs after the edit: `node scripts/check-dispatcher-error-vocabulary.mjs --self-test` -> EXIT=0, verdict 'check-dispatcher-error-vocabulary --self-test: 8 shapes + 102 assertions OK (vocabulary + #9098 door typing)'; `node scripts/check-dispatcher-error-vocabulary.mjs` -> EXIT=0, verdict 'OK - 21 unregistered code-stamping site(s), all classified; 1 awaiting a ledger entry (#8846)' with scope '1883 non-test source files under packages/; 291 registered codes; 21 found; 21 classified'. `diff` of the before/after captures is EMPTY for both -- byte-identical, not merely the same counts. COMMENT-ONLY, proven twice: no non-comment line appears in the diff, and the file's non-comment byte stream is md5-identical to HEAD (2e9cfa29e4d03764361b277839eff427 both sides). ON-DISK PROOF of the edit by anchored counts, each asserted to hit exactly once before writing (the applier aborts rather than making a partial write): header anchor 1->0 (replaced), keep() anchor 1->0 (replaced), injected \"[#10760] code: parsed?.code || 'x'\" 0->2, injected '[#10762] WHO owns a runtime-limbed chain' 0->1, and the #9568 ALL-OR-NOTHING line held at 1. PREMISE PROBES (why the card's clause was rewritten), driving both gates' exported entry points findViolations/deriveSites: stamp-site '{ code: p?.code || \"stamp_lower_failed\" }' -> dispatcher 0 sites, casing 1 (fallback); stamp-site 'err.code = p?.code || ...' -> 0 / 1 (fallback); local-init 'const code = p?.code || \"local_lower_failed\"' -> 0 / 0; CONTROL local-init 'const code = flag ? \"TERN_A\" : \"TERN_B\"' -> dispatcher 2 sites (assignconst), which is what makes the zeros a real zero rather than a broken probe. Tree sweep for the uncovered shape: 1885 non-test files under packages/, comments masked with the gates' own maskComments, 0 hits. DERIVED GATES: `node scripts/pm/dispatch-gates.mjs` with no paths derived 1 path from the merge base and named six families; all six green -- check:dispatcher-error-vocabulary (above); check:cross-package-test-inputs 'All 104 self-test cases passed.' + 'OK: 13 package(s) read outside themselves, all declared'; check:entry-guard '129 scripts/ file(s) - every entry guard goes through invoked-as.mjs; 87 export bindings, 77 of them inert on import'; check:parse-guard self-test + sweep clean; check:nul-bytes 'OK (scanned 6298 text file(s) ... no raw ASCII control bytes)'; check-ci-filter-parity 'OK: all 82 declared cross-package glob(s) (71 unique) are covered'. DECLARED NARROWING: no pnpm install was run in this worktree -- both gates this card concerns import only node builtins plus two local .mjs. The one derived family needing a dependency (check-ci-filter-parity wants yaml) ran against a single temporary symlink to an already-resolved yaml@2.9.0, removed immediately after; a broken link would have thrown ERR_MODULE_NOT_FOUND loudly rather than passing quietly, so its green is a real run. skip-changeset derived from pr-automation.yml's own words ('this PR edits a CI-internal script' is the textbook case), applied additively by POST and read back: ['size/s', 'skip-changeset'] -- the bot's size label survived.", "open_questions": [], "out_of_scope_findings": [ "filed as #10897: check-error-code-casing's ||/?? fallback recognizer anchors on the stamp site only, so our authored default in a `const code = p?.code || 'lit'` initializer is seen by neither gate -- measured with a live positive control on both sides; live extent in the tree today is 0, so it understates coverage exactly as #10762 does. Unassigned, labelled finding/tooling/domain:devx, no pm:queue." ] }
Generated by Claude Code
- added a commit that references this issue
on Aug 21, 2026 - added a commit that references this issue
on Aug 23, 2026
Observed while widening
check:error-code-casing's emission recognizer for #10658 (PR #10760). Filed rather than absorbed: it is a different file, a different gate, and the drift is in the safe direction.What is now inaccurate
scripts/check-dispatcher-error-vocabulary.mjsdocuments its delegation contract withcheck:error-code-casingin three places (around the header notes andisMine), each enumerating that gate's recognizers:After #10760 there is a fifth: our default reached through an
||/??fallback chain,code: parsed?.code || 'x'. The enumeration is now short by one.Why it is worth a line
The drift understates coverage, so nothing goes unreported today — this is not a live hole. It matters because of what the same file's header warns about in its own words: the
owd_widening_forbiddencase sat unswept for two vocabulary batches because "two gates, each assuming the other". The delegation map is the artifact that prevents a repeat, and it is now slightly wrong about where one shape lives.There is a second, related fact the map does not record. Under #9568 a
||/??chain reduces ALL-OR-NOTHING, so a chain with one runtime limb (parsed?.code || 'lit') reduces to nothing and this gate contributes no site for it. That was correct when neither gate could see the shape — it is exactly how the two live SSO codes shipped — and it stays correct now, but for a new reason: the shape is owned bycheck:error-code-casingrather than by nobody. Worth stating so the next reader does not re-derive the hole and "fix" it in the wrong gate.Suggested shape
Comment-only, in
scripts/check-dispatcher-error-vocabulary.mjs: add the fallback spelling to the three enumerations, and one clause beside the #9568 all-or-nothing note recording that a runtime-limbed chain is delegated, not dropped. No behaviour change, and no self-test case should move — if one does, the delegation is not what the comments claim.Refs
#10658 · PR #10760 (the widening) · #9460 / #9568 (the delegation contract and the all-or-nothing reduction) · #10716 (the two codes themselves, services lane) · ADR-0112 D1
Generated by Claude Code