fix(tooling): check-error-code-casing — reach our default through an ||/?? fallback chain, and stop printing an unqualified clean sweep - #10760
Merged
Conversation
…, and qualify its verdict The four CODE_POSITION_PATTERNS all anchor the string literal immediately after the position token, so an intervening expression makes the literal invisible to the whole set. Two live wire-visible codes shipped through that gap while the gate printed an unqualified 'no lowercase error codes'. - fifth pattern reaching our default through an ||/?? chain, bounded so a match cannot leap into a neighbouring property's fallback; - KNOWN_LOWERCASE_CODES, shrink-only, carrying the two codes whose rename is owned by #10716 (services lane) — a stale entry fails; - the verdict now states what the run could not read (#10501 precedent). Part of #10658
os-zhuang
marked this pull request as ready for review
August 21, 2026 10:57
This was referenced Aug 21, 2026
Merged
os-zhuang
pushed a commit
that referenced
this pull request
Aug 21, 2026
… runtime-limbed chain `scripts/check-dispatcher-error-vocabulary.mjs` documents its delegation contract with `check:error-code-casing` by enumerating that gate's recognizers. PR #10760 added a fifth — the OUR-DEFAULT slot of a `||`/`??` fallback chain — and the enumerations were left short by one. Comment-only. Both enumerations now carry the fifth spelling as the owning gate spells it, and the #9568 ALL-OR-NOTHING bound now records who owns a runtime-limbed chain, split by position because the two answers differ: delegated to `check:error-code-casing` at the stamp site, owned by nobody in a local's initializer (filed as #10897). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_c970724d-303c-5614-9d20-a3f92205cfad
os-zhuang
pushed a commit
that referenced
this pull request
Aug 21, 2026
`check-error-code-casing`'s fifth recognizer (#10760) anchors on the POSITION token — `code:` / `code?:` / `.code =` — so it reaches an `||`/`??` fallback chain only where the chain sits AT the stamp site. The identical chain one indirection earlier, in a local's initializer, matched nothing: const code = parsed?.code || 'lower_thing'; // our authored default err.code = code; `const code =` is neither spelling, and a type annotation does not rescue it: `const code: string = …` does match `code:`, but then the gap has to cross an `=`, which that character class refuses on purpose. Nothing else saw it either. `check:dispatcher-error-vocabulary` reaches the local (`err.code = code` is its `codehelper`/`assignconst` shape) but its `resolveConstant` reduction is ALL-OR-NOTHING by design (#9568): one runtime limb reduces the whole chain to nothing, because half an expression's values is a finding wrong in both directions at once. That bound is deliberate and is untouched here — its verdict line is byte-identical before and after. So the literal half is this gate's, on exactly the reasoning #10760 published for the stamp site: the capture is still only ever a STRING LITERAL, and a literal in our source is by construction the default WE author. Where we write the chain does not change whose default it is; the asymmetry was an artifact of where the recognizer anchored, not a decision anyone took. Adds a sixth recognizer, `local-fallback`, with the annotation gap spelled `[^=;\n]` (the same spelling `classfield` uses in the sibling gate) and the fifth pattern's own gap class and tail verbatim. The delegation runs the OTHER way for an ALL-literal initializer — a bare literal, a ternary, a chain — which IS reducible and stays the dispatcher gate's site under `assignconst`, lowercase included; the quote refusal keeps this pattern off those, so one literal never gets two reporters. Also updates the three delegation enumerations in `check-dispatcher-error-vocabulary.mjs` that #10762 had just corrected: they recorded this position as owned by NOBODY and named #10897 as the open half. Comment-only there; 8 shapes + 102 assertions and the full-run verdict all unchanged. Fixes #10897 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_c970724d-303c-5614-9d20-a3f92205cfad
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 23, 2026
… runtime-limbed chain (objectstack-ai#10898) `scripts/check-dispatcher-error-vocabulary.mjs` documents its delegation contract with `check:error-code-casing` by enumerating that gate's recognizers. PR objectstack-ai#10760 added a fifth — the OUR-DEFAULT slot of a `||`/`??` fallback chain — and the enumerations were left short by one. Comment-only. Both enumerations now carry the fifth spelling as the owning gate spells it, and the objectstack-ai#9568 ALL-OR-NOTHING bound now records who owns a runtime-limbed chain, split by position because the two answers differ: delegated to `check:error-code-casing` at the stamp site, owned by nobody in a local's initializer (filed as objectstack-ai#10897). Claude-Session: https://claude.ai/code/session_c970724d-303c-5614-9d20-a3f92205cfad Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 23, 2026
…too (objectstack-ai#10914) `check-error-code-casing`'s fifth recognizer (objectstack-ai#10760) anchors on the POSITION token — `code:` / `code?:` / `.code =` — so it reaches an `||`/`??` fallback chain only where the chain sits AT the stamp site. The identical chain one indirection earlier, in a local's initializer, matched nothing: const code = parsed?.code || 'lower_thing'; // our authored default err.code = code; `const code =` is neither spelling, and a type annotation does not rescue it: `const code: string = …` does match `code:`, but then the gap has to cross an `=`, which that character class refuses on purpose. Nothing else saw it either. `check:dispatcher-error-vocabulary` reaches the local (`err.code = code` is its `codehelper`/`assignconst` shape) but its `resolveConstant` reduction is ALL-OR-NOTHING by design (objectstack-ai#9568): one runtime limb reduces the whole chain to nothing, because half an expression's values is a finding wrong in both directions at once. That bound is deliberate and is untouched here — its verdict line is byte-identical before and after. So the literal half is this gate's, on exactly the reasoning objectstack-ai#10760 published for the stamp site: the capture is still only ever a STRING LITERAL, and a literal in our source is by construction the default WE author. Where we write the chain does not change whose default it is; the asymmetry was an artifact of where the recognizer anchored, not a decision anyone took. Adds a sixth recognizer, `local-fallback`, with the annotation gap spelled `[^=;\n]` (the same spelling `classfield` uses in the sibling gate) and the fifth pattern's own gap class and tail verbatim. The delegation runs the OTHER way for an ALL-literal initializer — a bare literal, a ternary, a chain — which IS reducible and stays the dispatcher gate's site under `assignconst`, lowercase included; the quote refusal keeps this pattern off those, so one literal never gets two reporters. Also updates the three delegation enumerations in `check-dispatcher-error-vocabulary.mjs` that objectstack-ai#10762 had just corrected: they recorded this position as owned by NOBODY and named objectstack-ai#10897 as the open half. Comment-only there; 8 shapes + 102 assertions and the full-run verdict all unchanged. Fixes objectstack-ai#10897 Claude-Session: https://claude.ai/code/session_c970724d-303c-5614-9d20-a3f92205cfad Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10658. Verified at
613fc4363b; baseorigin/main=8163a1c8e2.One file:
scripts/check-error-code-casing.mjs. No changeset — gate tooling, publishes nothing (skip-changeset).The defect, reproduced before anything changed
All four
CODE_POSITION_PATTERNSanchor the string literal immediately after the position token, so an intervening expression makes the literal invisible to the whole set — silently, and then the run prints a total that reads as complete.That tree has two, both inside the scanned set. Positive control, so the zero is read as a broken probe rather than a clean tree — the gate's own emission regex on both spellings:
Re-derived extent: 2, and it is still 2
Swept
packages/**.ts/.tsx(comments masked, the gate's own walk) with a deliberately broad probe —code:then any non-quote gap, then||/??, then a lower_snake literal:packages/plugins/plugin-auth/src/register-sso-provider.tsrequest_domain_verification_failedpackages/plugins/plugin-auth/src/register-sso-provider.tsverify_domain_failed2 hits in 4356 files — matches the card, so the known-list below is exactly two rows. The sibling shape
.code = <expr> || '<lower>'measures 0 occurrences; it is covered anyway (below), so the class is closed rather than the instance.origin/mainadvanced to78ac958552while this was in flight and touched 0 paths underpackages/, so the scanned population — and the extent — are unchanged.The landing hazard, and how it is resolved
Triage split the two wire-visible renames to #10716 (services lane), so this PR must not rename them — but a widened recognizer finds them on
mainand exits 1. Option 2 was checked, not assumed: #10716 is open and both codes are still live at 411/465 on the base, so it has not landed.Resolved by option 1, the
KNOWN_IMPORT_UNSAFEshape from #10665: a ⛔ SHRINK-ONLYKNOWN_LOWERCASE_CODEScarrying exactly those two rows, each naming the owning card. Keyed<file>::<literal>rather than by line, so a line shift cannot quietly invalidate an entry. Nothing new can join: a fresh finding is never absorbed by the list, and the author-facing remedy says so and refuses that route rather than offering it. When #10716 lands, each entry goes stale and the gate fails until the line is deleted — the list only ever shrinks. Both directions are pinned in--self-test.Where the line is drawn on vendor codes
The widened pattern still only ever captures a string literal, and a literal in our source is by construction ours. A vendor code passing through is a runtime value (
parsed?.code,err.code, a variable) — it has no literal for any pattern here to capture, before or after this change. So widening cannot start flagging a pass-through; it reaches only the default we author, which is exactly the operand ADR-0112 D1 governs (the code our failing request answers with). A vendor's spelling hard-coded as our default is a real violation, not a false positive.The second half of the line is the gap class, and it is what keeps a match inside one property's value. It admits an operand chain (identifiers, member/optional access, calls, indexes, further
||/??) and refuses, ; : { } =and every quote. The real false positive here is a neighbour's fallback —— and the comma is what stops it. Pinned as a reject case. The length bound is a runaway guard, also pinned.
Reject side, at tree scale
Differential sweep of the four base patterns vs. the five, per pattern, over the same 4356 files. The new pattern is additive — the other four are byte-identical to the base and unchanged in what they match:
Zero false positives repo-wide: the new pattern's only two raw matches are the two known rows.
check:dispatcher-error-vocabularyreports neither code on the base (a chain with one runtime limb reduces to nothing under #9568's all-or-nothing rule), so this shape was owned by no gate at all — which is the card.Both spellings pinned, as a pair
--self-testgrows 12 recognizer cases and 4 registry cases (17 -> 29, plus 4). The pair matters: the direct spelling of the same code is pinned beside the fallback spelling, because a recognizer that reached the new shape by breaking the old one would pass a self-test that only pinned the new one.Ablation
Prediction stated before running: reverting the widening reddens exactly 4 recognizer cases — the four expecting a hit through the fallback pattern — while the reject-side cases (expecting 0) and the registry cases stay green.
Mutation confirmed on disk before measuring: the two anchors
name: 'fallback',and itsre:line both went 1 -> 0, 151 bytes removed,git diff --statshowing 4 deletions. Observed, on the mutated tree:4 predicted, 4 observed, and the direct-spelling half stayed green — the pair did its job. The full scan independently flipped 0 -> 1 with 2 stale
KNOWN_LOWERCASE_CODESentries, which exercises the shrink-only limb from the other side. Restore leg proven too: anchors back to 1, tree clean againstHEAD, both runs green again. No build/distis involved —package.jsonruns this script from source — so there is no stale-artifact risk in either leg.The verdict shape: yes, it now says what it could not read
The card asks this to be weighed either way. Decided yes, on the #10501 precedent, because the reporting defect is the point of the card: an unqualified total renders a bounded read exactly like a complete one, and that is what shipped over a tree carrying two.
Two deliberate choices about what it counts:
adr0112-ok:comments present in the tree (20) — a comment that sits next to nothing suppresses nothing, and counting it would overstate the blindness.code:positions with no literal, but that number would be dishonest in this gate's mouth: most arecode: numberexit-code annotations and status fields, and the genuinely code-shaped remainder is delegated, not a hole —check:dispatcher-error-vocabularyowns constants, templates, ternaries and helper parameters ([finding]check:dispatcher-error-vocabularycannot see a lowercase thrown code —plugin-security's liveowd_widening_forbiddenwas never swept #9460/[finding]check:dispatcher-error-vocabularystill cannot see a code held in a local ternary —sys-metadata-repository's live 403NOT_CREATABLE/NOT_OVERRIDABLE#9568) and prints its own scope line. Printing a large uncovered-looking count for a population another gate covers would trade one misleading number for another. So the boundary is stated and its owner named.The qualifier prints on the failure path too: a partial finding list reads as complete for the same reason a partial total does.
Gates
node scripts/pm/dispatch-gates.mjswith no paths, at613fc4363b, clean tree — it derived the change set from the merge base as 1 path (a two-dotorigin/maindiff shows 5 files here, four of them sibling PRs that landed after this branch was cut). It named 3 families; the other three below were run because they were reasoned, not named:check:error-code-casingno unlisted lowercase error codes in 4356 scanned file(s)check:cross-package-test-inputsOK: 13 package(s) read outside themselves, all declaredcheck:ratchet-remedy-authority111 scripts swept; 7 mark ... 3 turn it down outright, 101 hand out no ratchet-expanding remedycheck:entry-guard125 scripts/ file(s) ... 83 export bindings, 73 of them inert on import (10 known-unsafe, SHRINK-ONLY)check:parse-guardcheck:nul-bytesOK (scanned 6234 text file(s) ... no raw ASCII control bytes)Two of those were run because a new shrink-only registry is exactly the shape they police, and both were verified directly rather than inferred from a green exit:
check:ratchet-remedy-authorityhand-classifies this very file asexcluded. A shrink-only registry whose remedy offers expansion would flip it tounmarked— a violation. Its ownclassify()run against this branch's source returnsexcluded, 0 live offers, 0 refused, 0 anchors, so the control entry stays correct and untouched.check:entry-guard— this file gains a second export (partitionKnown), and fix(scripts): burn 25 of the 35 KNOWN_IMPORT_UNSAFE entry-guard debts down #10704'sisEntrypointguard on it is untouched. It stays off theKNOWN_IMPORT_UNSAFESHRINK-ONLY ledger (0 occurrences), and importing it for its exports alone runs nothing, which is how the recognizer was proven against the real file.Out of scope, filed separately
The two renames are not addressed here — #10716 remains open and owns them. One follow-up filed for a documentation drift this change creates in the sibling gate's delegation map; see the report comment on #10658.
Generated by Claude Code
Generated by Claude Code