Found by measurement while producing the blast-radius census for #13131. Recorded rather than fixed there: that card is fenced to the object-literal stamp POSITION, and this is a different mechanism — the DECLARATION FORM — so not a rider.
The shape
codehelper is structural: it fires only when the assigned identifier is a parameter of the enclosing declaration, resolved by enclosingDeclaration in scripts/check-dispatcher-error-vocabulary.mjs. That function's DECL_HEADER_RE recognises exactly three header forms:
function NAME( constructor( const|let|var NAME = (
There is no form for a class method. So a code-carrying helper that happens to be a method is unreachable even in the assignment position codehelper already implements — the identifier is never recognised as a parameter, helperCodesFor returns null, and the site falls through to resolveConstant, which reduces nothing. No site, no unresolved, nothing reported.
Measured, with a same-genre positive control
Same body, same unregistered probe code, one declaration form apart, through the real deriveSites:
| # | declaration form | body | vocabulary gate | casing gate |
|---|
| K (control) | function fail(code, message) | e.code = code | 1 site, shape codehelper | 0 |
| I | private fail(code, message) in a class | e.code = code | 0, and 0 unresolved | 0 |
| J | private fail(code, message) in a class | { severity, code, message } | 0, and 0 unresolved | 0 |
K is the control that proves the instrument fires. I differs from K only in being a method.
Why it matters
It is the #9223 / #9460 / #10918 / #13131 genre once more: a shape the gate's prose claims to cover, implemented for one syntactic form and silently absent in a sibling form. The declared bound — a value the gate cannot reduce is REPORTED, never dropped — cannot hold here, because nothing is recognised in the first place.
It also blocks #13131 from delivering its stated value. That card's headline live instance, Parser#error(code, message, start?, tag?) in packages/sdui-parser/src/parse.ts, is a class method AND stamps through an object literal. Widening the position alone would still not reach it. Measured directly: enclosingDeclaration at that stamp offset answers with the Parser CONSTRUCTOR, whose parsed parameter names are ["readonly","readonly"] — see the sibling finding on parseParamNames.
Blast radius, measured on packages/** non-test source
Of the 13 helpers matching #13131's object-literal predicate, 4 are invisible to enclosingDeclaration:
4 helpers - 25 in-file call sites - 0 new verdict rows - 3 undischargeable `unresolved` findings
The 3 unresolved are the expensive half (an unresolved entry is pushed unconditionally and no declaration row discharges it):
packages/sdui-parser/src/parse.tsParser#error (16 call sites, all kebab literals the gate's grammar refuses to reduce)packages/runtime/src/http-dispatcher.tsHttpDispatcher#error (7 call sites)packages/plugins/plugin-auth/src/auth-manager.tsAuthManager#deliverPhoneOtp (2 call sites)
0 SCREAMING_SNAKE unregistered codes are hiding behind it today, so the value is preventing a future defect.
What a fix would have to decide
Not decided here:
- whether
DECL_HEADER_RE should gain a class-method form, which is harder than the other three because a method header is just NAME( — the discriminator is being directly inside a class body, and a textual scan has to earn that; - whether the anonymous-arrow form (
(code) => ({ code })) is in or out: it has no name for a call scan to anchor on, so it can produce neither a site nor an unresolved, and 1 of the 13 helpers is one (packages/spec/src/system/i18n-resolver.ts).
The blindness itself is now recorded and pinned in OBJECT_LITERAL_CODE_HELPER_BLINDNESS in scripts/check-dispatcher-error-vocabulary.mjs, with the class-method half pinned on SITES rather than on matches (the recognizer does match here; the structural resolver is what drops it).
Found by measurement while producing the blast-radius census for #13131. Recorded rather than fixed there: that card is fenced to the object-literal stamp POSITION, and this is a different mechanism — the DECLARATION FORM — so not a rider.
The shape
codehelperis structural: it fires only when the assigned identifier is a parameter of the enclosing declaration, resolved byenclosingDeclarationinscripts/check-dispatcher-error-vocabulary.mjs. That function'sDECL_HEADER_RErecognises exactly three header forms:There is no form for a class method. So a code-carrying helper that happens to be a method is unreachable even in the assignment position
codehelperalready implements — the identifier is never recognised as a parameter,helperCodesForreturnsnull, and the site falls through toresolveConstant, which reduces nothing. No site, no unresolved, nothing reported.Measured, with a same-genre positive control
Same body, same unregistered probe code, one declaration form apart, through the real
deriveSites:function fail(code, message)e.code = codecodehelperprivate fail(code, message)in a classe.code = codeprivate fail(code, message)in a class{ severity, code, message }K is the control that proves the instrument fires. I differs from K only in being a method.
Why it matters
It is the #9223 / #9460 / #10918 / #13131 genre once more: a shape the gate's prose claims to cover, implemented for one syntactic form and silently absent in a sibling form. The declared bound — a value the gate cannot reduce is REPORTED, never dropped — cannot hold here, because nothing is recognised in the first place.
It also blocks #13131 from delivering its stated value. That card's headline live instance,
Parser#error(code, message, start?, tag?)inpackages/sdui-parser/src/parse.ts, is a class method AND stamps through an object literal. Widening the position alone would still not reach it. Measured directly:enclosingDeclarationat that stamp offset answers with the Parser CONSTRUCTOR, whose parsed parameter names are["readonly","readonly"]— see the sibling finding onparseParamNames.Blast radius, measured on
packages/**non-test sourceOf the 13 helpers matching #13131's object-literal predicate, 4 are invisible to
enclosingDeclaration:The 3 unresolved are the expensive half (an
unresolvedentry is pushed unconditionally and no declaration row discharges it):packages/sdui-parser/src/parse.tsParser#error(16 call sites, all kebab literals the gate's grammar refuses to reduce)packages/runtime/src/http-dispatcher.tsHttpDispatcher#error(7 call sites)packages/plugins/plugin-auth/src/auth-manager.tsAuthManager#deliverPhoneOtp(2 call sites)0 SCREAMING_SNAKE unregistered codes are hiding behind it today, so the value is preventing a future defect.
What a fix would have to decide
Not decided here:
DECL_HEADER_REshould gain a class-method form, which is harder than the other three because a method header is justNAME(— the discriminator is being directly inside a class body, and a textual scan has to earn that;(code) => ({ code })) is in or out: it has no name for a call scan to anchor on, so it can produce neither a site nor an unresolved, and 1 of the 13 helpers is one (packages/spec/src/system/i18n-resolver.ts).The blindness itself is now recorded and pinned in
OBJECT_LITERAL_CODE_HELPER_BLINDNESSinscripts/check-dispatcher-error-vocabulary.mjs, with the class-method half pinned on SITES rather than on matches (the recognizer does match here; the structural resolver is what drops it).