Skip to content

check-error-code-casing reports a clean sweep it cannot support: a lowercase code in an || fallback matches none of its four recognizers, and two live ones ship today #10658

Description

@os-warren

Found while documenting the SSO domain-verification routes for #10534. Not fixed there — different defect class, so it is filed rather than absorbed.

The claim, and why it is not supported

pnpm check:error-code-casing prints, on origin/main at 47aff0938:

✓ check-error-code-casing self-test: 17 cases pass.
✓ no lowercase error codes in 4346 scanned file(s) (ADR-0112).

That second line is unqualified — it reads as "this tree has none". It has two, in a file that is inside the scanned set:

filelinecode
packages/plugins/plugin-auth/src/register-sso-provider.ts411request_domain_verification_failed
packages/plugins/plugin-auth/src/register-sso-provider.ts465verify_domain_failed

Both are emitted as the ObjectStack-authored default of an || chain, e.g. line 465:

return{status: resp.status,body: {success: false,error: {code: parsed?.code||'verify_domain_failed', message }}};

These are not vendor codes passing through — parsed?.code is that path. The literal is ours, and ADR-0112 / Prime Directive #3 make error codes SCREAMING_SNAKE.

Cause: the recognizer requires the quote to sit directly after code:

All four patterns in scripts/check-error-code-casing.mjs (CODE_POSITION_PATTERNS) anchor the string literal immediately after the position token. The emission one:

{ name: 'emission', re: /\bcode\s*:\s*'([a-z][a-z0-9_]*)'/g }

An intervening expression (parsed?.code || ) breaks that adjacency, and the other three do not apply — it is not .code =, not code ===, not a 'a' | 'b' union type. So the shape is invisible to the whole set.

Positive control, so a zero is a real zero rather than a broken probe — the gate's own emission regex, run on both spellings:

direct code: 'oops_bad' -> ["oops_bad"]
fallback code: p?.code || 'oops_bad' -> []

The recognizer demonstrably fires on one and not the other.

Extent, measured. Repo-wide, excluding tests and node_modules, the code: <expr> || '<lower_snake>' shape occurs 2 times — both rows above. So the correction is small; the reporting defect is the point.

Why this is a verdict question, not just two renames

This is the shape PR #10501 just closed for a different scanner: a partial read is a verdict, not a smaller number. A scan that cannot see a spelling produces no finding, silently, and then prints a total that reads as complete. Two prior cards found the same class in the sibling gate — #9223 (non-literal code:) and #9460 (a lowercase thrown code) — so this is the third instance of one pattern across the error-code gates, and the first in this one.

Note the two are not independent: #9460's fix taught check:dispatcher-error-vocabulary about a spelling, and this gate was not revisited.

Suggested direction (not prescriptive)

Two separable pieces, and the second matters more than the first:

  1. Widen the emission recognizer to reach a literal that follows code: through an || / ?? chain, with --self-test cases pinned for both the fallback spelling and the direct one — and pin the population so a widening cannot silently change what "clean" counts.
  2. Consider whether the summary line should be able to say how much it could not read, the way fix(docs-audit): a PARTIAL ledger read is a verdict, not a smaller number #10501 made affected-docs.mjs print both halves of its fraction. A recognizer will always have a boundary; the defect is a boundary that reports as zero.

Then fix the two codes themselves — but note they are also wire-visible, so renaming them is a behaviour change for any client matching on them, and belongs with whoever owns that call. packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts:252 currently pins verify_domain_failed by name.

Refs

#10534 (where this was found) · #10501 (the same "partial read reported as complete" class, other scanner) · #9223 / #9460 (the same class in check:dispatcher-error-vocabulary) · ADR-0112

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions