Skip to content

fix(tooling): route check-stack-collection-maps through the shared comment mask - #13269

Draft
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13143-mask-adoption-scripts-population
Draft

fix(tooling): route check-stack-collection-maps through the shared comment mask#13269
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13143-mask-adoption-scripts-population

Conversation

@os-elon

@os-elon os-elon commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Part of #13143 — deliberately not Fixes. See "Why this is Part of" at the bottom; the sub-issue #13274
carries the half this PR does not do.

Converts scripts/check-stack-collection-maps.mjs' private maskLiterals scanner onto the shared
scripts/js-comment-mask.mjs, with the before/after reading a conversion owes. The card's other route —
widening check-comment-mask-adoption.mjs' population to scripts/** — was priced first and is not taken
here
; the price falsifies the assumption it rests on, and the numbers are below so #13274 can be written
against them rather than re-derive them.

All measurements on e758630543 unless a commit is named.

What the card assumed, and what the tree says

The gap is structural — confirmed. check-comment-mask-adoption.mjs derives SCAN_ROOTS from
ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**'] and pins the collapse (SCAN_ROOTS.join(',') === 'packages,examples')
in its own --self-test; check-parse-guard.mjs walks scripts/ and its self-test forbids declaring any root
it does not walk, with a subject of three TypeScript parser entry points. Neither is a spelling artefact: the
adoption gate physically never reads this directory, and the parse guard physically never looks for a stripper.

The ledger's 14 rows really are all under packages/** — confirmed. 14 of 14, zero under examples/**,
zero under scripts/**. One row (packages/spec/scripts/lazify-schemas.ts) carries a scripts path segment
inside packages/, which is the shape that makes the sentence worth checking rather than assuming.

"Only check-stack-collection-maps.mjs" does not hold — and it never did. Re-swept with probes independent
of the gate's own vocabulary (declared name, character-scanner shape, indexOf of a comment terminator), over
193 sources under scripts/**. Four hand-rolled comment scanners that do not import the shared mask:

file the scanner shape
scripts/check-stack-collection-maps.mjs maskLiterals careful — tracks regex literals (converted by this PR)
scripts/check-ratchet-remedy-authority.mjs authorFacingMessages careful — inline scanner with regexAllowedAfter
scripts/check-whole-set-label-write.mjs blankComments / blankJsComments careful — tracks regex via opensRegex
scripts/measure-partial-retirement-annotation.mjs extractProse regex-BLIND — reads the // inside a regex literal as a line-comment opener

⚠️ All four are present at the card's own commit 19763458ff (git cat-file -e, and each declaration counted
there). So this is not drift since the card was filed — the card's hand-probe was narrow. Recorded that way
because "the tree changed under us" and "the instrument was narrow" call for different follow-ups.

Pricing the widening — the assumption it rests on is false in two ways

Running the adoption gate's own shapesIn() over scripts/** (193 sources):

  • 11 files are newly reached, not "exactly maskLiterals and nothing else":
    check-comment-mask-adoption.mjs (the gate itself), check-declaration-mirrors.mjs, check-live-db-isolation.mjs,
    check-optional-error-sink-contract.mjs, check-section-landing-index.mjs, check-type-check-coverage.mjs,
    check-type-source-resolution.mjs, check-undeclared-dep-imports.mjs, js-comment-mask.d.mts,
    js-comment-mask.mjs, pm/dispatch-gates.mjs.
    At least four of those are self-references or fabrications rather than private strippers: the gate's own SHAPES
    regexes and self-test fixture strings, the shared module itself (which is that gate's declared POSITIVE CONTROL),
    its .d.mts, and a fixture string inside an adopter. A shrink-only ledger gaining eleven rows of which several
    are artefacts is a ledger nobody can re-read.
  • None of the four live scanners is among the 11 — including the one the card is about.
    shapesIn(check-stack-collection-maps.mjs) returns []. The scanner-decl shape recognises
    (strip|mask|remove|without)Comment(s)? only, and maskLiterals is outside that vocabulary
    (/…maskComments\s*\(/i.test('export function maskLiterals(source) {') is false).
    "widen the population and ledger maskLiterals" is not executable as written: a ledger row for a file the
    scan does not find fails as stale, by the gate's own shrink-only invariant, on the very PR that adds it.

So the miss is two independent misses — population and vocabulary — and only the second one reaches this card's
own instance. Pricing the second axis too, for #13274: widening scanner-decl's verbs and nouns (adding
blank|scrub|elide|extract and Literals?|Prose|NonProse|JsComments?) costs 0 new rows under today's
packages/** + examples/** population and newly reaches 5 files under scripts/** — including three of the
four scanners above. authorFacingMessages stays out of reach of any spelling rule.

What this PR does instead

Option 2 from the card, for this one file, with the measurement Zone 1 requires.

maskLiterals keeps its name, its contract and all four call sites, and its body becomes the shared scanner's
comment | literal projection. Measured against the private scanner over the population this gate actually
reads
(its seven SITE files plus stack.zod.ts — 8 files, 1,028,984 chars): 3 files disagree, 49 spans,
247 characters
, in two classes:

  • 18 spans — projection only. The private copy blanked a regex literal's / delimiters; the shared scanner
    keeps them as code. No bracket is a slash, so no caller here could see it.
  • 31 spans — a real defect. The private scanner closed an outer template at the first backtick inside a
    ${...}, flipping the parity of every backtick after it and handing the bracket counter 20 bracket characters
    out of string and template interiors
    , in packages/objectql/src/engine.ts and packages/metadata/src/plugin.ts
    — both live SITE files. Same family as the .describe('Screen Flows (ADR-0019)') incident this function's
    docblock records, through a different door.

⚠️ Stated precisely: the card was right that no measured disagreement existed when it was filed, and right that
maskLiterals sits in the careful family. This is that measurement, taken now, with controls.

The instrument was shown able to fail first. Positive control: the naive two-regex pair diffed against the
shared scanner over the same eight files disagrees on 8 of 8 (3,884 spans on engine.ts alone). Negative
control: the shared scanner diffed against itself returns 0 spans. Every file asserts non-empty bytes.

Verdict-preserving on this tree. --list is byte-for-byte identical before and after
(sha256 7a4c9bd44ff1355439a452231e53c2938a7d39a7807e0a52f4267deb4f940d30 both ways), and the gate stays green.
That is a fact about where this tree's nested templates sit, not a reason the private copy was safe — so both
directions are pinned in --self-test, which grows from 13 to 15 assertions.

Ablations — prediction named before each run

Both mutate the committed file, prove the mutation on disk by decomposed grep -c of the injected and the
removed text plus git hash-object, and restore under a trap on absolute paths, proving the restore by an empty
git diff HEAD and a hash equal to the HEAD blob. No build step exists on this path: node executes the .mjs
source directly, so there is no dist/ for a stale artefact to hide in.

Ablation 1 — put the old private scanner back, keep the new pins.
Predicted: --self-test exits 1 with exactly the two new pins failing, actuals ["a"] and ["a","${v}","b"];
the 13 pre-existing assertions stay green; the main gate stays exit 0. Observed: exactly that, and --list kept
the same sha256 — an independent confirmation of verdict-preservation.

Ablation 2 — drop the literal half of the projection (both[i] = comment[i]).
Predicted: --self-test reds; the literal flag is load-bearing rather than a wrapper. Observed: exit 1, two
failures — one new pin and the pre-existing objectEntries() reads top-level keys only. ⚠️ The main gate stayed
exit 0 under this mutation, which is the "green over text it never read" direction in one line, and the reason
the pins are synthetic.

Gates

Family derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack on the committed diff
(1 path, three-dot semantics), re-derived after the edit rather than from the dispatch brief. Exit codes captured
before any pipe; verdicts quoted from each gate's own line.

Green (exit 0): check:stack-collection-maps (self-test 15 assertions + scan), check:agent-test-spelling,
check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard,
check:parse-guard, check:pnpm-filter-targets, check:watch-hint-literal, check-ci-filter-parity,
check-shard-attestation, plus the convention pair for editing a gate script —
scripts/pm/bare-root-worklist.mjs --self-test and check:pm-dispatch-gates. The adjacent mask family was
re-run too: check-comment-mask-adoption (self-test + scan), js-comment-mask --self-test (35 cases), and
check-comment-mask-corpus (5,454 files, 0 disagree) — all exit 0.

pnpm lint — the whole repo, eslint . --no-inline-config, not a narrowed run — under the shared verify
lock: VERDICT command-exit 0 · held the lock 49s.

NOT MEASURED, not red: node scripts/check-test-completeness.mjs with no argument exits 3 and prints
PREREQUISITE NOT MET; it grades a saved turbo run test log CI tees. check-ci-filter-parity and
check-shard-attestation first exited 1 with PREREQUISITE NOT MET — the dependency yaml is not installed;
both are green above after pnpm install.

No changeset: this edits a CI-internal script and releases nothing — the skip-changeset case the lint workflow
names in those words. The label is applied, and the read-back confirms it did not displace anything (the PR
carried no labels before the write).

Why this is Part of and not Fixes

The dispatch word asked for Fixes #13143; the measurement argues against it, so this deviates and says so.
#13143's finding is that nothing watches scripts/** for a private comment-stripper. This PR removes the one
instance the card names, and leaves the watcher exactly as absent as it found it — plus three more live scanners
there that the same sweep turned up, none of which any gate can see. Merging with Fixes would close a card whose
finding is measurably still true. The remaining half is #13274, a sub-issue of #13143, carrying the eleven-row
population price and the zero-row vocabulary price as its starting measurement.

Generated by Claude Code

…mment mask

Its private `maskLiterals` scanner was the one comment-scanning body under
scripts/ that neither check:comment-mask-adoption (population packages/ +
examples/) nor check:parse-guard (subject: the three TS parser entry points)
could see.

Measured against scanSource()'s comment|literal projection over the population
this gate actually reads (8 files / 1,028,984 chars): 3 files disagree, 49
spans, 247 chars -- 18 a harmless regex-delimiter projection difference, 31 the
private scanner mis-reading a nested template and handing the bracket counter
20 bracket characters out of literal interiors. Verdict-preserving on this tree
(--list byte for byte identical); both directions pinned in --self-test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants