Repository navigation
[finding] js-comment-mask has no corpus sweep in the tree — the instrument that found #10427 (parser cross-check over ~4,700 files) exists only in prose #10640
Description
Activity
claude commented
on Aug 21, 2026 claudeboton Aug 21, 2026 – with ClaudeContributorAuthorMore actionsClaim:
/pm-dispatchdomain:devx execution seat.
Session:session_01DdCnBGcHeufjrq7drTD3wt
Branch:claude/issue-10640-comment-mask-corpus-sweep
Worktree:objectstack-issue-10640
Domain:domain:devx
File surface: a new corpus-sweep script underscripts/(+ its wiring), readingscripts/js-comment-mask.mjs⭐ Why this seat took it: the card's central claim is the sharpest statement of the instrument problem this lane has produced tonight — "the self-test pins shapes someone thought of, the sweep finds shapes the tree actually contains, and neither substitutes for the other." It is backed by a measurement rather than an intuition: during #10427 a mutation deleting the
{counting inside${...}passed all 22 pinned cases AND the entire 4,739-file sweep, and the case that now holds it had to be written from the mutation, not from the corpus.⚠️ PR #10632 (#10427) merged, sojs-comment-mask.mjsis free and its 23 pinned cases are onmain.⛔ Governed surface out of scope (
docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md,content/docs/releases/**) and the #9465 epic fence (.changeset/tooling,cut-rc.yml,release.yml, rootpackage.json).
⚠️ No formatter of record — ⛔ do not runprettier --write(#10622).
⭐ Changeset:AGENTS.md:943; ascripts/**-only diff that publishes nothing takesskip-changesetvia the additivePOST /labels(⛔ never a whole-set PUT — see #5533).
Generated by Claude Code
claude commented
on Aug 21, 2026 claudeboton Aug 21, 2026 – with ClaudeContributorAuthorMore actions{ "issue": 10640, "status": "done", "branch": "claude/issue-10640-comment-mask-corpus-sweep", "pr": "https://github.com/objectstack-ai/objectstack/pull/10670", "premise_still_valid": true, "summary": "Added scripts/check-comment-mask-corpus.mjs — the parser cross-check that found #10427, re-derived as a script: it walks every .{ts,tsx,mts,cts,js,mjs,cjs,jsx} file, parses each with @typescript-eslint/parser and diffs its comment ranges against scanSource's comment array byte for byte, reporting both directions (FABRICATES / OVER-MASKS) separately. CI shape decided: a HARD GATE on every PR, as one step in the already-required `Lint & Repo Gates` job of lint.yml (not a new job — a new job's name is a required-context contract needing a maintainer-present Settings change, and an unenrolled job is advisory, #5617; not an on-demand flag — 'run it after touching scanSource' is exactly what the header already said and why this card exists). The gate carries its own positive control (--masker <path>), a 12-case comparator self-test that runs before every sweep, a corpus floor (fewer than 1000 files is a refusal, exit 3, not a pass), and fatal-on-unparseable. Also corrected js-comment-mask.mjs's header: its claim that the sweep 'is the STRONGER instrument of the two' is measured false, and the measurement paragraph now names the script that re-derives it. No existing issue citation was renumbered.", "tests": "All numbers at final HEAD b3b77c2843 unless stated. THE SWEEP (via scripts/pm/os-verify-lock.sh): '✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 4740 files, 0 disagree, 0 unparseable, 44.3s (comparator self-test: 12 cases pass).' · 'os-verify-lock: VERDICT command-exit 0 · held the lock 45s · waited 0s'. Corpus: 4,740 files, 72.1 MB, 0 unparseable — #10427's 4,739 re-derives (delta is this PR's own file). WALL CLOCK: 43.2–47.7 s across five runs; phase split measured separately: parser 45.4 s, scanSource 2.9 s, IO 0.6 s of a 49.4 s run. A 4-worker sharded prototype measured 33.7 s on the same contended 4-vCPU box (1.5x) and was declined. Context: the `Lint & Repo Gates` job measured 403 s on run 32459777391. POSITIVE CONTROL (proves the sweep would have caught #10427): `git show 29b2f8c8e1^:scripts/js-comment-mask.mjs > scripts/pre-10632-mask.mjs; node scripts/check-comment-mask-corpus.mjs --masker scripts/pre-10632-mask.mjs` → exit 1, '✗ … 16 of 4741 files disagree (47310 comment bytes read as code, 4281 code bytes read as comment), 0 unparseable, 57.8s' — 15 of the 16 in the FABRICATES direction, largest single file scripts/check-durability-degradation-log-level.mjs at 10,252 fabricated bytes: the same 16 files and the same 10,252 the card reports. ABLATION A (the card's own claim, re-measured): deleted `if (c === '{') frame.braces++;` — on-disk anchor count 1→0 for that line and 0→1 for the plain-if replacement, plus the git diff hunk; masker self-test went RED ('✗ an object literal, then a nested template, in one interpolation … FABRICATES', 1 of 23 failed) while the sweep stayed GREEN at 4,741 files, 0 disagreements. Restored: anchors 0→1 / 1→0, `git status` clean, self-test back to '23 cases pass'. ABLATION B (the reject side, and the reverse direction the card had not measured): removed 'return' from REGEX_AFTER_KEYWORD — anchor \"'return', 'typeof', 'instanceof'\" 1→0, replacement 0→1, diff hunk shown; the sweep went RED, exit 1, VERDICT command-exit 1, naming scripts/check-test-source-alias.mjs (over-masks=17, first at line 593, `return /(^|[^a-z])dist\\//.test(targets);`) — while ALL 23 pinned cases still passed. Restored: anchors 1→0 back to 1, tree clean, sweep green again (VERDICT command-exit 0). No editor exit code was used as evidence for any mutation; every leg was confirmed by grep -c on the anchor text before and after. No build/dist is involved (the scripts run from source), so ablation-dist-preflight does not apply. WIRING PROVEN BY PARSING, not grep: yaml.safe_load(lint.yml) → step 'Comment mask agrees with a real parser over the whole corpus' at index 13 of 80 in jobs.lint, whose name is 'Lint & Repo Gates' (the literal registered in scripts/check-required-contexts.mjs), no step-level if:, no continue-on-error, no strategy.matrix, pull_request trigger carries `branches` only (no paths:/paths-ignore:), merge_group present. OTHER GATES, each quoting its own printed verdict line: check:nul-bytes 'OK (scanned 6212 text file(s) … no raw ASCII control bytes)'; check:entry-guard '✓ 124 scripts/ file(s) — every entry guard goes through invoked-as.mjs'; check:parse-guard '✓ 123 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs'; node scripts/check-declaration-mirrors.mjs 'OK: 2 hand-written declaration(s) agree with their modules on name, kind and required arity'; check:required-contexts '✓ 6 required context name(s) pinned across 2 workflow(s)'; check:workflow-status-functions 'OK (scanned 26 workflow file(s), 49 job(s) …)'; check:node-version 'OK (32 setup-node step(s) across 26 workflow(s), all on Node 22)'; check:shard-attestation '✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s)'; check:cross-package-test-inputs 'OK: 13 package(s) read outside themselves, all declared'; check:type-check-coverage 'OK — 64/77 workspace packages type-checked'; js-comment-mask --self-test '✓ 23 cases pass'; the new comparator self-test 'All 12 self-test cases passed.'; ESLint on the new file exit 0. Exit codes were captured before any pipe. DECLARED NARROWING: check:type-check-debt was not run locally — it refuses without a built workspace closure ('55 workspace dependenc(ies) … have no built type entry point on disk'), by design, and this diff adds no TypeScript to any package; CI builds the closure before that step. GATE UNION re-derived on the final commit with `node scripts/pm/dispatch-gates.mjs` (no paths): 3 paths off merge base 9dd192d48, 11 families named — NOT silent and NOT empty. Hand cross-check found four it does not name for this surface (check:nul-bytes, check:entry-guard, check:parse-guard, check-declaration-mirrors.mjs); all four were run anyway and are quoted above.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026
Found while fixing #10427. Filed unassigned, no
pm:queue.The gap
scripts/js-comment-mask.mjsis described in this tree as "the ONE answer" to comment-vs-code, and #10427 established that its--self-testcases are the contract. That fix also established that the self-test is the weaker of the two instruments available, and the stronger one does not exist in the tree.The instrument that found #10427 was a corpus sweep: parse every
.{ts,tsx,mts,cts,js,mjs,cjs,jsx}file in the repo with@typescript-eslint/parser(already a root dependency), and diff the comment ranges it reports againstscanSource'scommentarray byte for byte. On the tree at the time it named 16 disagreeing files, 15 of them in the FABRICATES direction, up to 10,252 comment bytes in one file. Nothing inscripts/,package.jsonor.github/workflows/can re-derive that today — the sweep lived in an agent's scratchpad and left with it.Why the self-test alone does not cover it
Measured during #10427, and the reason this is worth a card rather than a note. A mutation that deleted the
{counting inside${...}passed all 22 pinned cases and the entire 4,739-file sweep — the tree simply does not write that shape today. The case that now holds it (${fmt({ a: 1 }, \\``)}`) had to be written from the mutation, not from the corpus.That is the general shape: the self-test pins shapes someone thought of, the sweep finds shapes the tree actually contains, and neither subsumes the other. The module's header now describes the sweep in prose so it can be reconstructed, which is a weaker guarantee than running it.
Note on scope
This is not a duplicate of #10608 (that card is about
--self-testentry points, this module's included, running in no workflow — a wiring gap for tests that already exist). This card is about a verification that does not exist in the tree in any form, wired or not. #10608 is the prerequisite in practice: wiring a sweep is pointless while the cheap self-test beside it still runs nowhere.Worth deciding rather than assuming: a full-corpus parse of ~4,700 files costs real CI time (the sweep took roughly a minute locally), so it may belong on a schedule or a
scripts/**-triggered lane rather than every PR — or as an on-demand--sweepflag on the module that a human runs after touchingscanSource, which is what its header currently asks for.Generated by Claude Code