refactor(devx): js-comment-mask publishes maskCommentsAndLiterals, and its two callers import it (#15594) - #15774
Conversation
…ction `js-comment-mask.mjs` published `stripComments` and `maskComments` but no comments+literals projection -- the mask a gate needs when its signal is a bare CODE position and a spelling inside prose *or* inside a string must not satisfy it. Two gates on main re-derived it under two names. Export `maskCommentsAndLiterals(source)`, documented beside `maskComments` in the module's own terms (the `stripComments` docblock already argues the module owns the projections rather than the callers), declared in the `.d.mts`, and driven by nine new self-test rows under a literal roster and floor of their own -- the shape the recogniser section established, so a deleted or renamed row names itself in the refusal. The rows live outside the `cases` table on purpose: that table asserts the OPPOSITE property for quoted text (`REAL` code inside a string MUST survive `maskComments`), and one fixture cannot state both directions. Self-test: 44 -> 53 cases (23 mask/strip corpus, 12 interpolation view, 9 shared recogniser, 9 comments+literals projection). Part of #15594 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…f re-deriving it
`check-adr-0087-registration.mjs` spelled it `maskCommentsAndLiterals`
(`comment.map((c, i) => c || literal[i])`); `check-registry-log-declared.mjs`
spelled it `maskCode` (`comment[i] | literal[i]` over a Uint8Array). Same
projection, two names, no shared pin. Both are now the module's export, and
each file's now-unused `blank`/`scanSource` imports go with the local copy.
`maskCode`'s docblock said it stays "local only because js-comment-mask.mjs
publishes no comments+literals projection yet, and hoisting one waits on a
follow-up card". That card is this one, so the docblock moves with the
conversion rather than becoming a stale assertion: what survives is the fact
that belongs to THIS gate -- that it reads two projections of one source and
relies on them agreeing offset-for-offset.
Behaviour byte-identical, proven per gate by diffing plain and `--self-test`
output before and after (four diffs, exit 0 each):
check-adr-0087-registration ✓ ... no declared-breaking changeset
✓ ... 304 assertions over real temp git repos
check-registry-log-declared OK: 72 vitest-running package(s) walked, 8 ...
self-test OK: 16 cases + level-vocabulary read ...
Part of #15594
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…sk-comments-and-literals
|
ACCEPT — PR #15774 (head As ruled: Measured by this seat: module The unfiled finding (eight MORE scripts re-derive the projection under eight names) is filed by this seat — see the card note. Flip + arm when both required jobs report success on Generated by Claude Code |
|
Flipped ready + auto-merge enabled (07:34Z, Generated by Claude Code |
Fixes #15594
scripts/js-comment-mask.mjspublishedstripCommentsandmaskCommentsbut nocomments+literals projection — the mask a gate needs when its signal is a bare CODE
position (
new SchemaRegistry(, a property key) and a spelling inside prose orinside a string/template must not satisfy it. Two gates on
mainre-derived it, undertwo names, with no shared pin. This exports it once, declares it, pins it, and converts
both.
What landed
scripts/js-comment-mask.mjs—export function maskCommentsAndLiterals(source),documented beside
maskCommentsin the module's own terms (thestripCommentsdocblock already argues the module owns the projections rather than the callers, and
names the two that existed; this is the third).
scripts/js-comment-mask.d.mts— declared besidemaskComments.(
SELF_TEST_PROJECTION_BATTERIES, floor 9) — the shape the shared-recogniser sectionestablished, so a deleted or renamed row names itself in the refusal instead of
quietly lowering a count it also supplies.
scripts/check-adr-0087-registration.mjs— localmaskCommentsAndLiterals(
comment.map((c, i) => c || literal[i])) replaced by the import.scripts/check-registry-log-declared.mjs— localmaskCode(
comment[i] | literal[i]over aUint8Array) replaced by the import, both callsites renamed, and its docblock updated: it said the copy "stays local only because
js-comment-mask.mjspublishes no comments+literals projection yet, and hoisting onewaits on a follow-up card". That card is this one, so the sentence moves with the
conversion rather than becoming a stale assertion. What survives is the fact that
belongs to this gate — that it reads two projections of one source and relies on
them agreeing offset-for-offset.
Each conversion also drops the
blank/scanSourceimports that the local copy wasthe only remaining user of.
Why the new rows are not in the
casestableThat table asserts the OPPOSITE property for quoted text:
REALcode inside a stringMUST survive
maskCommentsandstripComments, and a row that removed one would bereporting a bug. Under
maskCommentsAndLiteralsthat same spelling must NOT survive.One fixture cannot state both directions, so the rows are driven in their own section.
Behaviour byte-identical — the four diffs
Captured as
> file 2>&1; echo EXIT=$?, exit code read before any pipe.check-adr-0087-registrationDIFF_EXIT=0check-adr-0087-registration--self-testDIFF_EXIT=0check-registry-log-declaredDIFF_EXIT=0check-registry-log-declared--self-testDIFF_EXIT=0Verdict lines, identical on both sides:
Module self-test, before and after (both
EXIT=0):The new rows can fail — two ablation legs
Both legs ran from a COMMITTED implementation, mutated with an absolute-path
trap … EXIT INT TERMrestore, and each mutation was confirmed on disk by counting theremoved text and the injected text separately before any reading was taken.
Leg A — rename the export. Injected-spelling count 0 → 1, original 1 → 0.
Leg B — drop the
literalterm (flags[k] = comment[k] | literal[k]→flags[k] = comment[k]), which is the mutation a vacuous case would sit through.EXIT=1, four of the nine new rows red, and the corpus-wide row names the firstdisagreeing fixture:
The first attempt at leg B was a no-op — a
perl -0pianchor whose\Q…\Equotingdid not match. The on-disk count caught it (
removed-text count 1 -> 1), the run wasdeclared void and re-done with a correct anchor. Recording it because a silently re-run
ablation is the same defect one layer up.
Restore proven on both legs, not assumed:
The 23 vitest importers cannot be affected, and that is measured
js-comment-mask.mjsis imported by 23 test files across the workspace, none of whichthis worktree can run (no installed dependencies). Rather than declare them to CI on the
strength of "the change looks additive", every export that existed on
origin/mainwasrun over the whole tracked corpus under both module versions and compared byte for byte:
The importers name only pre-existing exports, and no pre-existing export moved. That is
the narrowing this PR relies on; CI still runs them.
Hot-file fence —
scripts/measure-self-test-floor.mjsLEFT UNTOUCHEDGET /repos/objectstack-ai/objectstack/pulls/15758at the moment that step was reached:The fence condition (
merged: true) is not met, so that file keeps its ownmaskCommentsAndLiterals(:258) and this PR does not touch it. The PM re-points it ina follow-up.
The other consumer named in the ledger
Seat post #6023 §3 lists
scripts/check-adr-0087-registration.mjsunder PR #15724 aswell as under this card. #15724 (open, draft, governed) does edit that file — but only
to ADD two call sites of
maskCommentsAndLiterals(:2484region) and to widenparseSymbolRef. It does not touch the function's definition, which is the only regionthis PR changes, and it calls the function under the same name this PR keeps. The two
diffs are textually disjoint and semantically compatible; nothing here needs #15724 to
move first, and #15724 needs no rewrite to sit on top of this.
Gate family
Derived — not guessed — from the merged tree, with the repo asserted:
The first derivation warned STALE TREE (
.github/workflows/lint.ymlhad moved), soorigin/mainwas fetched and merged and the family re-derived; the two command lists areidentical (
diffexit 0) and the second run carries no staleness warning.Green (31): all 41 derived commands were attempted. Of the 22 bare-
nodeones, 17are green, including
check-comment-mask-adoption(+--self-test),check-declaration-mirrors(+--self-test),check-self-test-wired(+--self-test),check-self-test-workflow-commands(+--self-test), both converted gates plain and--self-test, andjs-comment-mask --self-test. Of the 19pnpmones, 14 are green,including
check:nul-bytes,check:parse-guard,check:entry-guard,check:changeset-gate-self-tests,check:pm-dispatch-gatesandcheck:ratchet-remedy-authority.NOT MEASURED, by name — every one is a missing dependency in this worktree, never a
finding, and each is declared to CI:
scripts/check-comment-mask-corpus.mjsERR_MODULE_NOT_FOUND@typescript-eslint/parserpackages/lint/scripts/check-reference-carrier-shape.mjs(+--self-test)PREREQUISITE NOT MET: typescriptscripts/check-ci-filter-parity.mjsPREREQUISITE NOT MET: yamlscripts/check-closing-keyword-parity.mjs(+--self-test)PREREQUISITE NOT MET: yamlpnpm check:driver-memory-censusPREREQUISITE NOT MET: typescriptpnpm check:type-check-debt--re-measurerefuses without a built closure; its own--self-testleg printed greenpnpm --filter @objectstack/lint run check:doc-formula-expressionsPREREQUISITE NOT MET: typescriptpnpm --filter @objectstack/spec run check:browser-reachable-entriestsxnot installedpnpm --filter @objectstack/spec run check:error-code-provenancetsxnot installedpnpm lint(repo-wide eslint)dispatch-gatesalso printed three families whose argv takes a value only a CI runsupplies (
check-cross-package-test-inputs --union-into "$RUNNER_TEMP/…",check-shard-attestation --shard ${{ matrix.shard }},check-test-completeness "$RUNNER_TEMP/test-core.log") — NOT MEASURED by construction.Governance and changeset
scripts/check-adr-0087-registration.mjsis underscripts/, notdocs/adr/, and theregister agrees.
No changeset: all four paths are root
scripts/tooling in the private workspace root(
@objectstack/spec-monorepo,private: true), so per AGENTS.md theskip-changesetlabel is the right disposition — "that label is for a diff that publishes nothing from
any released package."
Out of scope, filed separately
The card says two callers re-derive this projection on
main. Re-derived on thisbranch's base, that is an undercount: eight more files spell the same
comment | literal→blankprojection under eight more names (codeOnlytwice,maskedProjections,maskLiterals,maskProse,projections,project, plus thefenced
measure-self-test-floor.mjs). None is named by the ruling, two sit under otherPRs' hot-file rows (#15758, #15762), so none is touched here. Filed as a follow-up.
🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code