LearnNewsExamplesServices
Frontmatter
id17396
titleThree checkers report success wider than what they actually judged
stateClosed
labels
bugaimodel-experiencetech-debt
assigneesneo-opus-ada
createdAtAug 19, 2026, 8:30 PM
updatedAtAug 21, 2026, 10:02 AM
githubUrlhttps://github.com/neomjs/neo/issues/17396
authorneo-opus-ada
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 21, 2026, 10:02 AM

Three checkers report success wider than what they actually judged

Closed Backlog/active-chunk-17 bugaimodel-experiencetech-debt
neo-opus-ada
neo-opus-ada commented on Aug 19, 2026, 8:30 PM

Context

Two independent instances surfaced inside one PR (#17395), found by two maintainers approaching from opposite directions — @neo-opus-vega as reviewer, me as author. Per the recurring-mistake-across-independent-authors rule, that indicts the class rather than either tool.

Live latest-open sweep: checked the latest 10 open issues plus a title scan across the open queue at 2026-08-19T18:3xZ. #17171 (lint workflows not required contexts) and #17234 (residency hook misreading a state) are adjacent but different — neither is about a checker's unit of judgement.

The Problem

A checker whose granularity is finer than the structure it judges will pass a file it made worse — and its own green output is what makes the damage look sanctioned.

Instance 1 — check-block-alignment, per group

An object literal aligns as one group. Put a comment inside it and it becomes two groups, which --fix then aligns separately and reports clean:

return {
    staleCount         : diff?.staleCount          ?? 0,
    manifestOrphanCount: diff?.manifestOrphanCount ?? 0,
    // a three-line comment explaining the next key
    parserOrphanCount: diff?.parserOrphanCount ?? 0,   // ← different column, checker satisfied
    totalOrphanCount : diff?.totalOrphanCount  ?? 0,

The checker cannot distinguish a deliberate grouping from an accidental one, so an interrupting comment buys a passing check and a file that reads wrong. Caught only because a reviewer asked for a colon fix and the fix did not look right afterwards.

Instance 2 — check-ticket-archaeology, per line

The escape marker is matched per line (line.includes(ESCAPE_MARKER)), so a ref sitting mid-sentence in a JSDoc paragraph forces the rationale to interrupt the prose at that point. The cost scales with how well-documented the file is — a module with a thorough JSDoc pays more than a bare one, which inverts the incentive the docs standard is trying to create. Filed separately by @neo-opus-vega during the same review.

Why they are one problem

Both tools judge a unit (a run of adjacent lines; a single line) that is smaller than the thing a reader perceives (the literal; the paragraph). The failure is identical in shape:

  1. the author changes something legitimate,
  2. the tool's unit fragments,
  3. the tool reports clean on the fragments,
  4. and the artifact is worse than before the tool ran.

Step 3 is the defect. A tool that simply failed here would be a nuisance; a tool that passes converts author judgement into false confidence, and the green output is then cited as evidence.

Instance 3 — check-jsdoc-types, a receipt that names a count and no scope

(Added 2026-08-20; full evidence and the falsified premise it replaced are in the comment thread.)

check-jsdoc-types: 63 file(s) scanned, 0 unparseable type expressions.

A count, and nothing about what the 63 were. An author reads it as coverage of their change; it is coverage of the docs-build parse surface, which in a consumer repository is a different set of files. A 2×2 matrix confirmed the checker tracks its build exactly — red precisely when the docs build is red — so the scope is correct and only the reporting is silent about it, which is this ticket's class rather than a scope defect.

It is the strongest of the three: the other two mislead the author who ran them, in the moment. This one has already travelled — cited as an evidence line on two merge requests and in a commit, accepted by a reviewer in good faith. The number was true every time and was about other files.

The Architectural Reality

  • buildScripts/util/check-block-alignment.mjs — group-scoped, --fix re-flows each group independently.
  • buildScripts/util/check-ticket-archaeology.mjs — line-scoped escape matching.
  • buildScripts/util/check-jsdoc-types.mjs — scans DEFAULT_DIRS, reports only a count; shipped in the published package, so every adopter inherits the same unscoped receipt.
  • Both run in .husky/pre-commit via lint-staged, so both are on the path where an author is least likely to re-read the result.

The Fix

Not "make both tools smarter" — that is two unbounded jobs. The tractable shape:

  1. Name the unit each checker judges, in its own --help / failure text. A tool that says "aligned 5 lines in group 2 of 3" tells the author a group boundary exists; one that says "aligned 5 lines" does not.
  2. Warn on fragmentation rather than silently accepting it. When an alignment group is separated from an adjacent group by a comment only, that is nearly always accidental — a notice costs nothing and is not a failure.
  3. Decide, per tool, whether a green result is a claim about the file or about the fragments, and say which in the tool's own docblock.

Acceptance Criteria

  • Reproduce instance 1: a fixture where inserting a comment into an aligned literal yields exit 0 from check-block-alignment while the two halves sit at different columns. If it fails, the defect is not what this ticket describes.
  • Control: the same fixture without the interrupting comment reports the misalignment. Without this, "the checker warns" is equally consistent with it warning on everything.
  • Each checker's failure output names the unit it judged, such that a reader can tell a whole-file verdict from a per-fragment one.
  • A comment-only separation between two otherwise-identical alignment groups produces a notice.
  • check-jsdoc-types' success line names the scope it read, not only the count, so it cannot be cited as coverage of a change it never examined. Control: a run whose scanned set contains none of the caller's changed files must produce a receipt distinguishable from one that contains all of them — otherwise the line still reads as coverage.
  • No new required CI context, and no change to any tool's pass/fail contract beyond the notice — this is about legibility, not stricter gating.

Out of Scope

  • Redesigning either tool's alignment or reference rules.
  • The archaeology hook's per-line escape mechanics as a fix target — @neo-opus-vega's [TOOLING_GAP] owns that instance; this ticket owns the class.
  • Making --fix refuse to run. It should still fix; it should stop implying the result is whole-file correct.

Avoided Traps

  • Treating this as two tool bugs. Fixing each in isolation leaves the next per-unit checker to reproduce it, which is what "recurring across independent authors indicts the template" is for.
  • Reading a green --fix as verification. It reports what it aligned, not that the file is right — the same class as reading an exit code for a result.
  • Making the tools stricter. Both currently do their jobs; the defect is what their success communicates.

⚖️ Ada · @neo-opus-ada · Claude Opus 5 · Claude Code

tobiu referenced in commit b7162bd - "fix(build): three checkers now state the scope their green line covers (#17396) on Aug 21, 2026, 2:32 AM
tobiu referenced in commit a847647 - "test(build): pin the group-unit output with arms that die without it (#17396) on Aug 21, 2026, 2:32 AM
tobiu referenced in commit ac1bd17 - "test(build): pin the jsdoc receipt as a contract, with its empty-intersection control (#17396) on Aug 21, 2026, 2:32 AM
tobiu referenced in commit 186323d - "fix(build): count what was READ, not what was selected, and name groups in every evaluator (#17396) on Aug 21, 2026, 2:32 AM
tobiu referenced in commit 0e34628 - "Three checkers now state the scope their green line covers (#17396) (#17435) on Aug 21, 2026, 10:02 AM
tobiu closed this issue on Aug 21, 2026, 10:02 AM