@@ -555,14 +555,42 @@ const maskedModuleBody = memoiseMask((source) => maskSelfTests(maskedComments(so
555555 * unbounded under-mask measured above.
556556 *
557557 * Same projection as `blank` next door — spans become spaces, newlines and byte
558- * offsets survive — so this composes onto `maskedModuleBody`'s output rather
559- * than replacing it. The order is measured too, and it is the one that cannot
560- * widen: masking `#` FIRST stops a `#` comment containing `@objectstack/*` from
561- * opening a phantom JS block comment, which UNCOVERS code below it and adds
562- * hints (2 on this tree — `scripts/downstream-smoke.sh` and
563- * `.claude/hooks/guard-tree-enum.sh`). Masking `#` LAST can only blank more of
564- * an already-masked body, so the shell hint set is a subset of today's by
565- * construction.
558+ * offsets survive — so this composes WITH the JS mask rather than replacing it.
559+ *
560+ * ## The ORDER, and why it is now `#` FIRST (#16744)
561+ *
562+ * Masking `#` LAST can only blank more of an already-masked body, so the shell
563+ * hint set was a subset of the JS-masked one by CONSTRUCTION. That is why the
564+ * card above took it: it could not widen, and widening was out of its scope.
565+ * The price it left unpaid is the OTHER direction of the same "THIRD kind"
566+ * defect. A `#` comment spelling `@objectstack/*`, a glob, or any other
567+ * two-character block-comment opener is not a comment to the JS scanner — it
568+ * opens a BLOCK comment that runs to the next terminator and blanks every line
569+ * of real shell CODE in between, with nothing in the output saying a region was
570+ * skipped. Re-measured per byte on `ce7bae8b`, over the 31 tracked `.sh` files:
571+ * 12 of them hand a caller shell code as prose, 73,859 bytes in
572+ * `scripts/pm/os-verify-lock.sh` alone.
573+ *
574+ * So `#` is masked FIRST now: shell prose never reaches the JS scanner and
575+ * cannot open a phantom comment. This UNCOVERS the code below such a comment
576+ * and therefore ADDS hints — 2 on this tree, `node_modules/@objectstack/spec/dist`
577+ * in `scripts/downstream-smoke.sh` and one site in
578+ * `.claude/hooks/guard-tree-enum.sh`, both of them real code the follow was
579+ * blind to. ⛔ The shell hint set is therefore NOT a subset of the JS-masked one
580+ * any more, and the live sweep in the self-test pins what the additions ARE —
581+ * ⛔ never that there are none, which is the blindness this removed.
582+ *
583+ * The JS mask still RUNS on a shell source, and still runs BEFORE
584+ * `maskSelfTests` as that function's header requires. Keeping it is the whole
585+ * difference between this order and "do not run the JS scanner over a non-JS
586+ * kind at all": the latter also drops the `//` masking pinned below on a `.sh`
587+ * source, which is a reconciliation across both cards rather than this fix.
588+ *
589+ * Composed from the raw maskers rather than through `maskedModuleBody` — the
590+ * intermediate is a DIFFERENT string on this path, so there is no half to
591+ * share, and routing it through the JS memo would only fill that cache with
592+ * shell-derived keys no JS caller can hit. The memo below is keyed on the raw
593+ * source, so the whole chain is still derived once per source.
566594 */
567595const SHELL_WORD_START = /[\s;&|()<>]/;
568596
@@ -621,8 +649,10 @@ export function maskShellComments(source) {
621649 return blank(String(source), shellCommentSpans(String(source)));
622650}
623651
624- /** `maskShellComments(maskedModuleBody(source))`, memoised — see the block above. */
625- const maskedHashCommentBody = memoiseMask((source) => maskShellComments(maskedModuleBody(source)));
652+ /** `maskSelfTests(maskComments(maskShellComments(source)))`, memoised — see the block above. */
653+ const maskedHashCommentBody = memoiseMask((source) =>
654+ maskSelfTests(maskComments(maskShellComments(source))),
655+ );
626656
627657// ── What a gate that IMPORTS this module inherits (#11556) ─────────────────
628658//
@@ -15541,6 +15571,46 @@ function selfTest() {
1554115571 shHints(shJsComment).length === 0,
1554215572 shHints(shJsComment),
1554315573 );
15574+ // ── The PHANTOM BLOCK COMMENT the order used to open (#16744) ─────────────
15575+ //
15576+ // The other direction of the same "THIRD kind" defect, and the card's whole
15577+ // acceptance criterion: two sources that differ in FOUR CHARACTERS OF PROSE,
15578+ // both of which must read the one hint their code spells. The first carries a
15579+ // block-comment opener inside a `#` comment; while `#` was masked LAST that
15580+ // opener reached the JS scanner and blanked the real `DEST=` line under it.
15581+ //
15582+ // ⛔ The control is not decoration. A change that only makes the first row
15583+ // fire — by disabling the mask, or by dropping the JS scanner on this kind —
15584+ // takes the second row down with it or leaves it as the ONLY row that fires.
15585+ // Both rows read 1, or this is not the fix.
15586+ const shPhantom = '# published @objectstack/* packages\nDEST="node_modules/@objectstack/spec/dist"\n';
15587+ const shPhantomControl = '# published packages\nDEST="node_modules/@objectstack/spec/dist"\n';
15588+ t(
15589+ '⭐ a `/*` inside a `#` comment no longer opens a block comment over the shell code below it',
15590+ shHints(shPhantom, 'scripts/x.sh').join() === 'node_modules/@objectstack/spec/dist',
15591+ shHints(shPhantom, 'scripts/x.sh'),
15592+ );
15593+ t(
15594+ '⭐ CONTROL: the same code under a comment with NO opener still reads its one hint — the mask was fixed, not switched off',
15595+ shHints(shPhantomControl, 'scripts/x.sh').join() === 'node_modules/@objectstack/spec/dist',
15596+ shHints(shPhantomControl, 'scripts/x.sh'),
15597+ );
15598+ // The span, not just the line: an unterminated opener ran to the END OF FILE,
15599+ // so the cost was every hint below it rather than the one line beside it.
15600+ const shPhantomSpan =
15601+ '# everything below this line is /* invisible\n'
15602+ + 'bash "scripts/one.sh"\n'
15603+ + 'bash "scripts/two.sh"\n';
15604+ t(
15605+ '…and it ran to END OF FILE, so the recovered span is every hint below the comment, not one line',
15606+ shHints(shPhantomSpan, 'scripts/x.sh').join() === 'scripts/one.sh,scripts/two.sh',
15607+ shHints(shPhantomSpan, 'scripts/x.sh'),
15608+ );
15609+ t(
15610+ '…non-vacuously: the JS-kind control on the same bytes still loses both, which is what this order costs a `.sh` file',
15611+ shHints(shPhantomSpan, 'scripts/x.sh.mjs').length === 0,
15612+ shHints(shPhantomSpan, 'scripts/x.sh.mjs'),
15613+ );
1554415614 // KIND-SCOPED, in both directions. The same bytes on a `.mjs` path must keep
1554515615 // spelling their hint: `#` is not a comment in JavaScript, and a mask that
1554615616 // fired there would be a widening rather than this card's narrowing.
@@ -15628,25 +15698,46 @@ function selfTest() {
1562815698 let shellShrank = 0;
1562915699 let shellBefore = 0;
1563015700 let shellAfter = 0;
15701+ const shellAddedProse = [];
1563115702 for (const f of liveShellFiles) {
1563215703 const src = readFileSync(nodePath.join(ROOT, f), 'utf8');
1563315704 const masked = extractWatchHints(src, f, { tree: hintTree });
1563415705 const unmasked = extractWatchHints(src, `${f}.mjs`, { tree: hintTree });
1563515706 shellBefore += unmasked.length;
1563615707 shellAfter += masked.length;
15637- if (masked.some((h) => !unmasked.includes(h))) shellGrew++;
15708+ const added = masked.filter((h) => !unmasked.includes(h));
15709+ // An ADDED hint is admissible only if it is spelled in CODE: its literal has
15710+ // to survive the `#` mask. One spelled only inside a `#` comment would be
15711+ // the FABRICATING direction arriving through the very reorder that fixed
15712+ // the under-mask, so it is collected by name rather than counted.
15713+ const code = maskShellComments(src);
15714+ for (const h of added) if (!code.includes(h)) shellAddedProse.push([f, h]);
15715+ if (added.length) shellGrew++;
1563815716 else if (masked.length < unmasked.length) shellShrank++;
1563915717 }
1564015718 t(
15641- `⭐ LIVE: over ${liveShellFiles.length} tracked .sh file(s) the mask never ADDS a hint — ${shellBefore} spelled without it , ${shellAfter} with`,
15642- liveShellFiles.length > 0 && shellGrew === 0 && shellAfter < shellBefore,
15643- JSON.stringify({ files: liveShellFiles.length, shellBefore, shellAfter, shellGrew, shellShrank }),
15719+ `⭐ LIVE: over ${liveShellFiles.length} tracked .sh file(s) every hint the \`#\` mask ADDS is spelled in CODE — ${shellBefore} hints without the mask , ${shellAfter} with`,
15720+ liveShellFiles.length > 0 && shellAddedProse.length === 0 && shellAfter < shellBefore,
15721+ JSON.stringify({ files: liveShellFiles.length, shellBefore, shellAfter, shellGrew, shellShrank, shellAddedProse }),
1564415722 );
1564515723 t(
1564615724 '…non-vacuously: at least one live file really loses a hint, so the sweep is not passing over an instrument that changed nothing',
1564715725 shellShrank >= 1,
1564815726 JSON.stringify({ shellShrank }),
1564915727 );
15728+ // ⛔ This case replaced a `shellGrew === 0` pin (#16132), and the replacement
15729+ // is the point of #16744 rather than a relaxation of it. That spelling was
15730+ // true only because `#` was masked LAST, which left shell prose to reach the
15731+ // JS scanner first: a `#` comment containing `/*` opened a phantom block
15732+ // comment over the real code below it, so the code could not spell a hint in
15733+ // EITHER column and the difference read 0. Masking `#` FIRST uncovers that
15734+ // code, so additions are now expected — and the honest invariant is what they
15735+ // ARE, not that there are none.
15736+ t(
15737+ '…and the additions really happen, so the case above is not a subset test wearing a code test\'s name',
15738+ shellGrew >= 1,
15739+ JSON.stringify({ shellGrew }),
15740+ );
1565015741 // The card's sharpest specimen, both ends. DEPARTURE alone would stay green
1565115742 // on a mask that emptied the file, so the ARRIVAL half names a live shell
1565215743 // script whose hint is spelled in CODE and must survive.
@@ -15663,6 +15754,26 @@ function selfTest() {
1566315754 'docs/releases-maintenance.md',
1566415755 ),
1566515756 );
15757+ // ⭐ LIVE RECOVERY (#16744): the site the card named, both ends. The `.mjs`
15758+ // control is what makes it a recovery rather than an ordinary arrival — the
15759+ // JS-only path STILL cannot read this line, because the phantom comment the
15760+ // header's `@objectstack/*` opens is what hid it, and that is precisely what
15761+ // masking `#` first removes.
15762+ const liveDownstream = 'scripts/downstream-smoke.sh';
15763+ const liveDownstreamSrc = readFileSync(nodePath.join(ROOT, liveDownstream), 'utf8');
15764+ t(
15765+ '⭐ LIVE RECOVERY: a path spelled in real shell CODE under a `#` comment carrying a block-comment opener is read again',
15766+ extractWatchHints(liveDownstreamSrc, liveDownstream, { tree: hintTree }).includes(
15767+ 'node_modules/@objectstack/spec/dist',
15768+ ),
15769+ extractWatchHints(liveDownstreamSrc, liveDownstream, { tree: hintTree }),
15770+ );
15771+ t(
15772+ '…non-vacuously: the JS-kind control on the same bytes still cannot see it, so a phantom comment is what was hiding it',
15773+ !extractWatchHints(liveDownstreamSrc, `${liveDownstream}.mjs`, { tree: hintTree }).includes(
15774+ 'node_modules/@objectstack/spec/dist',
15775+ ),
15776+ );
1566615777 const liveShardSelfTest = 'scripts/ci/select-shard-packages.selftest.sh';
1566715778 t(
1566815779 '⭐ LIVE ARRIVAL: a shell script whose hint is spelled in CODE still spells it, so the mask removed prose and not the population',
0 commit comments