Skip to content

[finding] "a statement swallowed by an unterminated block comment" is a parse-checkable class with no checker — measured 1 in 4,595 files, and the tree already hosts three inline ESLint plugins that could carry it #9758

Description

@os-steve

The H3 half of #9640, recorded rather than built: whether the class that card is an instance of deserves a checker, measured instead of guessed. Filed unassigned, no pm:queue.

The class

A block comment that is never closed is not a syntax error. The next */ in the file closes it — typically the docblock of the following declaration — so the file parses, every gate stays green, and the only symptom is a statement that quietly stopped existing. #9640 is one instance: export { maskComments }; had been comment text, and the module's header went on claiming the re-export.

Not findable by reading (the swallowed line looks like code and the closing delimiter looks like it belongs to the block below), and trivially findable by parsing.

Sweep (the base rate)

Parsed every .mjs/.cjs/.js/.ts/.tsx/.jsx file outside node_modules, dist, .next, build, .turbo, coverage with the repo's own scanSource from scripts/js-comment-mask.mjs, flagging (a) a statement-shaped line at any indentation lying wholly inside a block-comment span, and (b) a /* opener inside a comment span (comments do not nest, so this is the structural signature of "never closed"):

scripts/ 112 files 1 hit scripts/pm/dispatch-gates.mjs lines 483-519
everything else 4,483 files 0 hits

The one hit is #9640 itself, and it carried both signals. .claude/ holds one JS file, clean; skills/ holds no JS. The class is a singleton on this tree today.

Verdict

Worth a checker, but not worth a check:* family.

Against a new gate script: it would be the 111th check family, with a workflow step, a required context, its own self-test and its own watch hints, to hold a class measured at 1 in 4,595 files — and every added family costs a line of reader attention in dispatch derivations, which is the "22 leads is the same as none" concern dispatch-gates.mjs' own header argues.

For a checker anyway: the narrow shape is not fuzzy at all. The card's framing worried that "names a docblock claims" is a false-positive machine, and it is — but "a statement at column 0 that the parser says is inside a comment" needs no natural-language reading, and a real parser already visits every file.

So the proportionate home is a local ESLint rule under the existing pnpm lint, in the pattern eslint.config.mjs already uses three times (slot-lookup, query-options, verify-stand-in are inline plugins in that file). ESLint hands a rule the comment nodes for free, so the rule needs no masker at all:

  • flag a Block comment whose value contains a line matching a statement starter (export/import/const/let/function/class/return) at column 0 of that line, i.e. with no leading * prose marker;
  • flag a Block comment whose value contains /* at the start of a line.

Zero new CI steps, zero new required contexts, zero new dispatch leads, whole-tree coverage.

What is already covered, and what is not

PR for #9640 pins the single statement it repaired, inside dispatch-gates.mjs' own self-test (which check:pm-dispatch-gates runs unconditionally): it asks this file's own source with this file's own masker whether export { maskComments }; is code, matching at column 0 and rejecting a match flagged as literal. That case turns red the next time someone edits that docblock — measured by reintroducing the defect — but it covers one statement in one file. Nothing covers the class.

Dedupe

search_issues for "eslint rule or gate for code statement inside a block comment, unterminated comment swallowing a statement" returns #9640 (the instance), #9367 and #9444 (both about gates mis-stripping comments, i.e. the masker's own history), and #8872 (a broken sentence in a comment) — no card proposes a checker for this class.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions