Skip to content

[finding] check-doc-authoring inStrictOptions() reads only the type ANNOTATION — a satisfies StrictObjectOptions const hides its nested guidance prose #13105

Description

@os-elon

Observation-class, found while widening Rule 3's hoisted-const pass in PR #13104. Latent, not live — measured zero instances in tree today, so nothing is currently escaping.

The shape

scripts/check-doc-authoring.mjs has two places that ask "what type does this const declare", and PR #13104 only fixed one of them.

collectTextSinkConsts() now routes its type anchors through a declaredTypeText() helper that reads the annotation or a trailing satisfies when there is no annotation. Its sibling, inStrictOptions(), still reads the annotation alone:

if(ts.isVariableDeclaration(p)){constnm=p.name.getText();return/_STRICT_OPTIONS$/.test(nm)||/\bStrictObjectOptions\b/.test(p.type ? p.type.getText() : '');}

inStrictOptions() is the position test: it climbs from a guidance: / guidanceSets: / history: / aliases: / retiredForms: / surface: property up to whichever encloses it first, and decides whether that property is really a StrictObjectOptions key whose value is printed at a refusing author.

With satisfies, the climb passes through a SatisfiesExpression — which is neither a CallExpression nor a VariableDeclaration, so the loop continues — and lands on the VariableDeclaration, where p.type is undefined. Result: false. A prescription written like this would not be recognised as customer-facing prose:

constSOMETHING_OPTIONS={surface: 'this shape',guidance: {legacyKey: 'removed in protocol 17 (#1234). Delete it.'},}satisfiesStrictObjectOptions;

The failure mode is the one this whole file is a monument to: no flag, silently.

Why it is latent

Measured on main @ e9c1055ea2:

  • satisfies StrictObjectOptionszero occurrences under packages/. Every options table in the tree uses either the annotation or the *_STRICT_OPTIONS name, both of which inStrictOptions() already recognises.
  • The satisfies spelling is live for the sibling type: WIDGET_GUIDANCE_SETS (packages/spec/src/ui/dashboard.zod.ts) is written as const satisfies readonly KeySetGuidance[]. So the spelling is idiomatic here and the next options table written that way is plausible, not hypothetical.

Why it was left out of PR #13104

Scope. That card's defect class is the cross-module blind spot in collectTextSinkConsts(); this is a spelling gap in a different function with a different job, and folding it in would have blurred what that PR's ablation proves. Zero live instances also means there is no red to demonstrate today, so the fix would ship with no reverse verification available beyond a planted fixture.

Suggested direction

Reuse the helper PR #13104 already added — declaredTypeText(decl, ts) — at the VariableDeclaration branch of inStrictOptions(), and add the --self-test case that the gate's own extend-the-detector rule requires. That makes the two type anchors read the same way, which is the property that made this gap possible in the first place.

Not filed as a bug against Rule 3's verdicts: no current string is misclassified.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions