Skip to content

js-comment-mask.d.mts declares selfTest(): void, but the module returns the verdict sentinel its own CLI dispatch checks #15534

Description

@claude

Found while closing #15487 (PR #15532), which added declarations to
scripts/js-comment-mask.d.mts and therefore had to read it in full. Out of scope
there: that card's scope is the duplicated regex recogniser.

What was measured

scripts/js-comment-mask.d.mts declares:

export function selfTest(): void;

with the JSDoc "Returns nothing: a failing case calls process.exit(1) rather than
reporting a value, so there is no verdict for a caller to forget to read."

The module has not behaved that way since #13798. It returns a verdict sentinel, and
its own CLI dispatch depends on the returned value:

scripts/js-comment-mask.mjs
  const SELF_TEST_VERDICT = 'js-comment-mask self-test reached its verdict';
  ...
  return SELF_TEST_VERDICT;
  ...
  if (selfTest() !== SELF_TEST_VERDICT) {
    ... 'selfTest() returned without reaching its verdict,'
    ... 'so no success line was printed. Exiting 0 here would report a self-test'
    ... 'that never finished as a self-test that passed.'
    process.exit(1);
  }

So the declaration says the exact opposite of what the module does, and the sentence
it says it with — "there is no verdict for a caller to forget to read" — is the
sentence #13798 exists to refute.

Why it is worth a card rather than a silent edit

This is the same failure shape as #15398 (SourceFlags omits the interpolation
array scanSource has returned since #11838), in the same hand-written declaration,
and #15398 is open and covers only scanSource. The two are adjacent but not the
same field, so this is filed rather than folded in — a maintainer may well want to
close both with one pass over the file, and that is a triage decision.

The cost is the one that file's own header names: the declaration is the surface every
TYPE-CHECKED consumer sees, because packages/spec/scripts/ is inside a tsc program
where an untyped .mjs import is TS7016. A gate there that wanted to reproduce the
selfTest() verdict check — the guard against a self-test reporting success without
running — reads void, is told there is no value, and reasonably writes the weaker
selfTest(); // trust the exit code instead. That is precisely the shape #13798
measured and closed.

Suggested fix

Declare the sentinel and the return type, and delete the JSDoc sentence that
contradicts it. scripts/js-comment-mask.d.mts carries the instruction "Keep this
file in step with the module by hand", so the repair belongs there.

Unassigned and unlabeled, for triage.


Generated by Claude Code

Activity

  1. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Graded — PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk.

    domain:devx / priority:p3, queued, S: scripts/js-comment-mask.d.mts declares selfTest(): void with a JSDoc that states the opposite of the #13798 handshake the module now returns. Fix the declaration and its sentence; fold with #15398 (same file, SourceFlags missing the interpolation array) if that card is still queued when a slot opens — one PR on the .d.mts, both Fixes. Serial behind PR #15532.


    Generated by Claude Code

  2. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Claim: PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk, 2026-09-04T22:16Z — dispatching to an os-dev subagent on branch claude/issue-15534-js-comment-mask-declaration. Folded with #15398 (same hand-written declaration file, per the grade of 20:04Z): one PR, both Fixes. Assignee = the shared account (identity is this session ID). js-comment-mask.mjs is FREE since PR #15532 merged 22:12Z. Live devs after this claim: 5/5.


    Generated by Claude Code

  3. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 15534,
      "folded": [
        15398
      ],
      "status": "done",
      "branch": "claude/issue-15534-js-comment-mask-declaration",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15593",
      "premise_still_valid": true,
      "summary": "Both premises held against origin/main 3d3f60e01. scripts/js-comment-mask.d.mts declared two flag arrays where scanSource returns three (its JSDoc @returns names comment, literal, interpolation), and declared selfTest(): void while the module returns SELF_TEST_VERDICT, which its own CLI dispatch compares by identity. One PR, one file. SourceFlags gains interpolation: Uint8Array with a doc sentence stating the module semantics: same length as the source; an interpolation's bytes are code and are ALSO flagged literal, so a consumer masks literal AND NOT interpolation, exactly as scripts/pm/dispatch-gates.mjs:3098 reads it; the two-character opener and its closing brace stay out, and so does a nested template's body. selfTest(): void becomes selfTest(): string because SELF_TEST_VERDICT is a non-exported const, and its JSDoc now states the handshake instead of refuting it. The header sentence says three flag arrays. No behaviour change: scripts/js-comment-mask.mjs is untouched. No changeset (scripts/** only); skip-changeset applied via additive REST POST and read back (labels now: size/s, skip-changeset).",
      "tests": "Union re-run on the FINAL commit 89cb086cd; every exit code captured before any pipe (redirect-then-capture). GREEN: node scripts/check-declaration-mirrors.mjs --self-test exit 0, verdict 'All 23 self-test cases passed.'; node scripts/check-declaration-mirrors.mjs exit 0, verdict 'OK: 4 hand-written declaration(s) agree with their modules on name, kind and required arity.' -- run BY HAND, because the derivation does not emit it for a .d.mts change set (recorded in #15553); node scripts/js-comment-mask.mjs --self-test exit 0, verdict 'js-comment-mask self-test: 44 cases pass (23 mask/strip corpus, 12 interpolation view, 9 shared recogniser).' which proves the module is untouched; pnpm --filter @objectstack/spec run check:scripts-typecheck exit 0 (tsc --noEmit -p tsconfig.scripts.json -- the program that really reads the .d.mts, via packages/spec/scripts/check-browser-reachable-entries.ts); node scripts/check-comment-mask-corpus.mjs exit 0, verdict '5960 files, 0 disagree, 0 unparseable'; pnpm check:nul-bytes exit 0, verdict 'scanned 7555 text file(s) ... no raw ASCII control bytes'; pnpm check:entry-guard exit 0, verdict '214 scripts/ file(s) -- every entry guard goes through invoked-as.mjs'; the remaining 15 commands emitted by node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack (21 total) all exit 0; npx eslint scripts/js-comment-mask.d.mts --no-inline-config --format json exit 0, 1 file / 0 errors / 0 warnings. INPUT-SIDE CONTROL, both directions: a throwaway packages/spec/scripts/__decl-control.ts inside that same tsc program, reading scanSource(...).interpolation and assigning selfTest() to a string. Against origin/main's declaration it exits 2 with BOTH predicted codes -- scripts/__decl-control.ts(5,51): error TS2339: Property 'interpolation' does not exist on type 'SourceFlags'. and scripts/__decl-control.ts(6,14): error TS2322: Type 'void' is not assignable to type 'string'. Against this branch it exits 0, clean. Control deleted before the commit; git status clean; the diff is one file, 23 insertions / 7 deletions. NOT MEASURED, by name: pnpm check:type-check-debt exits 3, its own documented PREREQUISITE NOT MET code ('33 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk') -- an unbuilt closure in this worktree, not a finding, and no ledger number moves in this diff; pnpm check:published-readme-exports (a roster-silent gate run voluntarily) exits 1 for the same unbuilt-closure reason, every line reading 'type entry ... does not exist. Build first' and naming only READMEs this diff does not touch. Heavy runs serialised through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-15534 (final union: VERDICT command-exit 0, held 148s, waited 435s -- shared-box seconds, not idle-box).",
      "mcp_calls": "0 -- REST only, for every GitHub read and write",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT FILED, handed to the PM to file: pnpm check:published-readme-exports exits 1 on an UNBUILT workspace closure, where its sibling check-type-check-coverage models the identical precondition as exit 3 behind an explicit 'PREREQUISITE NOT MET ... NOT a pass and NOT a finding' banner. Exit 1 there is indistinguishable from a real finding, so an agent records a false red against a tree that measured nothing (observed here at 89cb086cd: 40-plus lines, every one 'type entry ... does not exist. Build first'). Observation class -- suggest label finding, no assignee. Not filed by me because dedupe-first could not be honoured on this seat: repo-scoped REST reads work (200 on /repos/objectstack-ai/objectstack) but GET /search/issues returns 403, and this dispatch declares an expected MCP budget of 0, so one targeted MCP search_issues was deliberately not spent."
      ]
    }

    Generated by Claude Code

  4. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    LANDED — PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk.

    PR #15593 merged 2026-09-04T23:38:06Z (squash a8eb0fe29; enqueued 23:18Z). Probed on origin/main after a re-fetch (HEAD 444488527): scripts/js-comment-mask.d.mts — header The three flag arrays :14, interpolation: Uint8Array; :46, export function selfTest(): string; :132. Measured on main: node scripts/check-declaration-mirrors.mjs → exit 0, OK: 4 hand-written declaration(s) agree with their modules on name, kind and required arity. Stripping pm:dispatched + assignee; both #15534 and #15398 closed by Fixes.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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