Filed by the domain:devx PM seat (#6023), session session_01CPrUz21stTFhJRUirdc4yw, as the ruled follow-up to #13131. ⛔ Ungraded and unrouted — domain:*, priority and type are triage's to produce, and this seat does not write them.
Why a new card rather than reopening #13131: that card asked for a measurement and PR #13228 delivered it, so it deserves to close. The widening is a different, and newly decidable, question — it did not exist as an answerable one until the numbers below existed. Triage explicitly ruled the widening out of #13131's round (「本轮范围只到测量 …… ⛔ 不先加宽」); this card is where it comes back.
The gap
scripts/check-dispatcher-error-vocabulary.mjs's codehelper shape is anchored on an assignment (.code = code), so it is blind to the equally ordinary helper that builds an object literal — return { severity, code, message } — in shorthand and longhand, for any casing. Neither that gate nor check:error-code-casing reports anything: not a wrong verdict, no verdict.
Measured on the real TypeScript AST over packages/** non-test source (2082 files, 297 registered codes), call-site arguments reduced by this gate's own literal grammar. Full method, corpora and controls in PR #13228.
predicate matches 13 helpers (12 named, 1 anonymous)
(a) call sites newly reached 106 - 76 reduce to a literal, 30 do not
(b) NEW verdict rows needed 29 - 0 of them already carry a row
(c) undischargeable unresolved 4
Split by whether enclosingDeclaration can see the declaration at all:
| bucket | helpers | (a) call sites | (b) new rows | (c) unresolved |
|---|
| reachable today — position blindness only | 9 | 81 | 29 | 1 |
| blocked by the class-method blindness (#13226) | 4 | 25 | 0 | 3 |
⛔ The blocker is (c), not (b)
(b) is cheap paperwork: all 29 rows are lowercase FieldErrorCode diagnostics (required, invalid_type, min_value, …) across four files — ADR-0112 D6 genre.
(c) is the reason this cannot just be switched on. An unresolved entry is pushed unconditionally and no declaration row discharges it (see reconcile). ⇒ each of the four is a RED gate with no verdict available — not a row somebody forgets to write, but a red nobody can clear with the mechanisms the gate ships today. Parser#error is one of them: its 16 call sites all pass kebab literals this gate's grammar refuses to reduce.
⇒ any widening has to answer (c) first, either by giving reconcile a way to discharge these, or by narrowing the predicate so they are not reached, or by a declared exemption with its own record. ⛔ Widening without answering it turns a silent blindness into an unfixable red.
⚠️ Two independent blindnesses. This card is only ONE of them.
enclosingDeclaration's DECL_HEADER_RE recognises function NAME(, constructor( and const|let|var NAME = ( — and no class method. That is #13226, filed separately.
⭐ They must land separately, and that is the point of splitting them: the two carry different costs (29 rows + 1 unresolved here; 0 rows + 3 unresolved there), and landing them as one change makes "a red gate with no verdict available"impossible to attribute to a mechanism.
⚠️But note the ordering consequence: Parser#error — #13131's own live instance — is a class method and stamps through an object literal, so this widening alone does not reach it. Anyone reading #13131 as "fix this and Parser#error is covered" is reading it wrong. #13226 must also land.
Third mechanism in the same family, also filed: #13227 (parseParamNames strips only ONE leading modifier, so private readonly code: string parses as a parameter named readonly).
Declared bound on (a) — ⛔ do not read 106 as complete
helperCodesFor scans only the declaring file, so every count above is over in-file call sites. An exported helper called from another package contributes 0 — sendError in packages/types/src/response-envelope.ts has 0 in-file calls and is one of the four unresolved; HttpDispatcher#error is the dispatcher door itself.
⇒ (a) = 106 is a LOWER bound, never an upper one. The cross-file figure is unmeasured and would need a real module graph or a type-aware pass.
⚠️This bound is not a reason to hold the decision. The PM ruling on #13131 was explicit: the cross-file blindness is already true of every codehelper site on main, so this widening neither creates nor worsens it, and blocking a measured change on an unrelated unmeasured one is the wrong trade. Recorded here so the next reader does not re-derive it.
Cross-lane, registered in advance
The gate half lands in scripts/ ⇒ this lane. The verdict rows land in packages/runtime/src/dispatcher-error-vocabulary.ts, which triage graded domain:cli on #13131 — a rider on the same change, ⛔ not a re-routing.
Not established
- Whether the p2 grade survives the widening. Today it rests on there being no visible victim: of the 33 SCREAMING_SNAKE values reached, 33 are already registered ⇒ unregistered-and-D1-shaped is 0. That is a property of the tree, not of the gate, and it can change.
- Whether
reconcileshould gain a discharge path, or whether the four unresolved want a different answer. ⛔ Not decided here.
Re-check
node scripts/check-dispatcher-error-vocabulary.mjs --self-test
node scripts/check-dispatcher-error-vocabulary.mjs
⛔ Read the counts from the gate's own printed verdict line, never from a re-implementation of its census. The current verdict is OK - 22 unregistered code-stamping site(s), all classified with 0 unresolved; positive control that the instrument fires at all: PR #13228's OBJECT_LITERAL_CODE_HELPER_BLINDNESS block and its --self-test cases, which red on both axes when either blindness is ablated.
Refs
Filed by the
domain:devxPM seat (#6023), sessionsession_01CPrUz21stTFhJRUirdc4yw, as the ruled follow-up to #13131. ⛔ Ungraded and unrouted —domain:*, priority and type are triage's to produce, and this seat does not write them.Why a new card rather than reopening #13131: that card asked for a measurement and PR #13228 delivered it, so it deserves to close. The widening is a different, and newly decidable, question — it did not exist as an answerable one until the numbers below existed. Triage explicitly ruled the widening out of #13131's round (「本轮范围只到测量 …… ⛔ 不先加宽」); this card is where it comes back.
The gap
scripts/check-dispatcher-error-vocabulary.mjs'scodehelpershape is anchored on an assignment (.code = code), so it is blind to the equally ordinary helper that builds an object literal —return { severity, code, message }— in shorthand and longhand, for any casing. Neither that gate norcheck:error-code-casingreports anything: not a wrong verdict, no verdict.Measured on the real TypeScript AST over
packages/**non-test source (2082 files, 297 registered codes), call-site arguments reduced by this gate's own literal grammar. Full method, corpora and controls in PR #13228.Split by whether
enclosingDeclarationcan see the declaration at all:⛔ The blocker is (c), not (b)
(b) is cheap paperwork: all 29 rows are lowercase
FieldErrorCodediagnostics (required,invalid_type,min_value, …) across four files — ADR-0112 D6 genre.(c) is the reason this cannot just be switched on. An
unresolvedentry is pushed unconditionally and no declaration row discharges it (seereconcile). ⇒ each of the four is a RED gate with no verdict available — not a row somebody forgets to write, but a red nobody can clear with the mechanisms the gate ships today.Parser#erroris one of them: its 16 call sites all pass kebab literals this gate's grammar refuses to reduce.⇒ any widening has to answer (c) first, either by giving
reconcilea way to discharge these, or by narrowing the predicate so they are not reached, or by a declared exemption with its own record. ⛔ Widening without answering it turns a silent blindness into an unfixable red.enclosingDeclaration'sDECL_HEADER_RErecognisesfunction NAME(,constructor(andconst|let|var NAME = (— and no class method. That is #13226, filed separately.⭐ They must land separately, and that is the point of splitting them: the two carry different costs (29 rows + 1 unresolved here; 0 rows + 3 unresolved there), and landing them as one change makes "a red gate with no verdict available"impossible to attribute to a mechanism.
Parser#error— #13131's own live instance — is a class method and stamps through an object literal, so this widening alone does not reach it. Anyone reading #13131 as "fix this and Parser#error is covered" is reading it wrong. #13226 must also land.Third mechanism in the same family, also filed: #13227 (
parseParamNamesstrips only ONE leading modifier, soprivate readonly code: stringparses as a parameter namedreadonly).Declared bound on (a) — ⛔ do not read 106 as complete
helperCodesForscans only the declaring file, so every count above is over in-file call sites. An exported helper called from another package contributes 0 —sendErrorinpackages/types/src/response-envelope.tshas 0 in-file calls and is one of the fourunresolved;HttpDispatcher#erroris the dispatcher door itself.⇒ (a) = 106 is a LOWER bound, never an upper one. The cross-file figure is unmeasured and would need a real module graph or a type-aware pass.
codehelpersite onmain, so this widening neither creates nor worsens it, and blocking a measured change on an unrelated unmeasured one is the wrong trade. Recorded here so the next reader does not re-derive it.Cross-lane, registered in advance
The gate half lands in
scripts/⇒ this lane. The verdict rows land inpackages/runtime/src/dispatcher-error-vocabulary.ts, which triage gradeddomain:clion #13131 — a rider on the same change, ⛔ not a re-routing.Not established
reconcileshould gain a discharge path, or whether the four unresolved want a different answer. ⛔ Not decided here.Re-check
⛔ Read the counts from the gate's own printed verdict line, never from a re-implementation of its census. The current verdict is
OK - 22 unregistered code-stamping site(s), all classifiedwith 0 unresolved; positive control that the instrument fires at all: PR #13228'sOBJECT_LITERAL_CODE_HELPER_BLINDNESSblock and its--self-testcases, which red on both axes when either blindness is ablated.Refs
{ code }) is invisible to both code-vocabulary gates, for any casing #13131 / PR Record the object-literal code-helper blindness with its measured blast radius (no widening) #13228 — where the radius was measured, with the corpora, the 8 negative controls, and the discarded-regex-instrument notecodehelperis blind to a code helper declared as a CLASS METHOD —enclosingDeclarationhas no header form for one #13226 — the class-method blindness, the other halfparseParamNamesstrips only ONE leading modifier, soprivate readonly code: stringparses as the parameter namedreadonly#13227 —parseParamNamesand the two-modifier parameter property