scripts/js-comment-mask.mjs publishes scanSource, blank, stripComments and maskComments, but no comments+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 or inside a string/template must not satisfy it.
So each caller that needs it writes the same three lines: take scanSource(source), OR the comment and literal flag arrays together, run the result through blank. Two are on main today, spelled differently and named differently:
// scripts/check-adr-0087-registration.mjs:2409 — maskCommentsAndLiterals()
const flags = comment.map((c, i) => c || literal[i]);
// scripts/check-registry-log-declared.mjs:195 — maskCode()
for (let i = 0; i < flags.length; i++) flags[i] = comment[i] | literal[i];
Neither carries private scanning logic — both compose the shared scanner, so this is not the second-scanner drift class. What it is: a projection every caller re-derives, under two names, with no shared pin. stripComments's docblock already argues that the module owns the projections rather than the callers, and it names the two that exist; this is the third.
Proposed: export maskCommentsAndLiterals(source) from scripts/js-comment-mask.mjs, declare it in scripts/js-comment-mask.d.mts beside maskComments, add a self-test case for it in the module's own battery, then convert both call sites above (each is a one-line body replaced by an import — behaviour byte-identical, provable by diffing each gate's plain and --self-test output before and after).
Consumers to convert:
Found while narrowing #15561, which replaced that gate's private maskComments wrapper with the module's export and deliberately left maskCode alone: there is no shared export to consume yet, and adding one is a change to a module three gates read, not a rider on a one-line refactor. #15561 is not addressed by this card.
Generated by Claude Code
scripts/js-comment-mask.mjspublishesscanSource,blank,stripCommentsandmaskComments, but no comments+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 or inside a string/template must not satisfy it.So each caller that needs it writes the same three lines: take
scanSource(source), OR thecommentandliteralflag arrays together, run the result throughblank. Two are onmaintoday, spelled differently and named differently:Neither carries private scanning logic — both compose the shared scanner, so this is not the second-scanner drift class. What it is: a projection every caller re-derives, under two names, with no shared pin.
stripComments's docblock already argues that the module owns the projections rather than the callers, and it names the two that exist; this is the third.Proposed: export
maskCommentsAndLiterals(source)fromscripts/js-comment-mask.mjs, declare it inscripts/js-comment-mask.d.mtsbesidemaskComments, add a self-test case for it in the module's own battery, then convert both call sites above (each is a one-line body replaced by an import — behaviour byte-identical, provable by diffing each gate's plain and--self-testoutput before and after).Consumers to convert:
scripts/check-adr-0087-registration.mjs:2409(verified onmain)scripts/check-registry-log-declared.mjs:195maskCode(verified onmain; its docblock already states it stays local only until this card lands)scripts/measure-self-test-floor.mjs— named as a third consumer by the dispatching PM via PR fix(devx): anchor the self-test floor probe on the real definition, not the first text that reads like one #15580, which is in flight and was not read here. Onmainthat file imports onlymaskCommentsand carries no comments+literals copy, so confirm against fix(devx): anchor the self-test floor probe on the real definition, not the first text that reads like one #15580 before counting it.Found while narrowing #15561, which replaced that gate's private
maskCommentswrapper with the module's export and deliberately leftmaskCodealone: there is no shared export to consume yet, and adding one is a change to a module three gates read, not a rider on a one-line refactor. #15561 is not addressed by this card.Generated by Claude Code