Skip to content

Commit f112a74

Browse files
test(cli): mask comments in the nightly-tier readers of this package's own source, and derive the pins that bound a spelling (#18924)
Fixes #18520 Clause-②: no The nightly-tier test files under `packages/cli/test/` that read this package's own source TEXT now read it through the shared mask (`scripts/js-comment-mask.mjs`), and the pins that bound a byte-exact SPELLING now bind a property derived from the source instead. Every file was judged on its own; the table below gives the rung and the reason per file. ## The population, re-derived — no number inherited **Predicate, stated:** a file under `packages/cli/test/` whose NAME puts it in a nightly tier (`scripts/nightly-tiers.mjs` — `*.e2e.test.*` / `*.live.test.*`, and nothing else selects a tier), which READS this package's own source text at test time, directly or through a helper under `test/helpers/` it imports. Evaluated over each file's local import closure, on MASKED text, so a read one module away still counts and a path named only in prose does not. | predicate | count | measured at | |:--|--:|:--| | card's: `test/*.e2e.test.ts` containing `readFileSync` | **26** | reproduces exactly | | card's: of those, importing the shared mask | **3** | reproduces exactly — `serve-port-readback`, `published-entry-stderr-nonblocking`, `run-dev-stderr-nonblocking` | | claim's: `grep -rl readFileSync test/*.e2e.test.ts \| xargs grep -l 'src/'` | **20** | reproduces exactly | | **mine** (above) — nightly-tier readers of this package's own source | **15** | 12 raw, 3 already masked | ⭐ **20 and 15 are not a contradiction — they are two predicates, and the delta is readable both ways.** Eight files are in the claim's 20 and not in mine, three are in mine and not in its 20: - **prose only** — `lint-eval-generator-load-envelope` matches `src/` in a docblock sentence and reads `/definitely/not/here.json`. This is the false positive the dispatch predicted. - **a temp fixture's `src/`, not ours** — `build-docs-step-count`, `build-multi-package-artifact`, `generate-skill`, `serve-publishes-bound-port` create `src/docs` or `src/skills` inside a scratch project and read `dist/objectstack.json` back. - **another package's source** — `scaffold-emission-policy` reads `packages/create-objectstack`'s template `package.json`. JSON carries no comments, so no rung applies. - **own source, but `bin/` not `src/`** — `published-entry-stderr-nonblocking` (already masked) and `run-dev-unbuilt-workspace` (raw). Both read a hand-written published entry; the second is in this PR, the first was already correct. - **missed by the claim's grep, found by mine** — `config-miss-stdout-purity` reads `src/commands` through `test/helpers/config-miss-family.ts` and never spells `src/` itself; `invocation-loudness` and `serve-host-fallback-base` DO spell `../src/...` but never read it as text — they `symlink`/`execFile` it, so they are readers of a module, not of prose. ## Rung per file | file | reads | rung | what changed, and why that rung | |:--|:--|:--|:--| | `build-json-failure-conversions.e2e.test.ts` | `src/commands/compile.ts` | **3 only** | Masked. Its `indexOf` ORDER block (`declAt` / `tryAt` / `loadAt` / `normalizeAt`) is the #17633 shape verbatim — raw positions over a file whose docblocks name `await loadConfig(`. ⛔ The six frozen integers are card #18894's and are untouched here. | | `build-json-failure-warnings.e2e.test.ts` | `src/commands/compile.ts` | **3 only** | Masked, same fence — integers untouched. | | `cloud-login-json-ndjson.e2e.test.ts` | `src/commands/cloud/login.ts` | **3 + 2** | Masked; and the pin that subtracted ONE byte-exact line from the `emitJson(` hits now partitions the call sites against `emitRecord`'s own brace-matched body: outside must be empty, inside must not be. | | `login-json-ndjson.e2e.test.ts` | `src/commands/login.ts` | **3 + 2** | Same conversion, same reason. | | `login-json-noninteractive.e2e.test.ts` | `src/commands/login.ts` | **3** | Masked only. Its `rl.question(` filter already binds a SHAPE (does the line carry the abort signal), not a spelling — rung 2 has nothing to convert. | | `json-stdout-purity.e2e.test.ts` | `src/commands/**` | **3 + 2** | Masked; and `toHaveLength(10)` demoted to a floor. The line above it already binds the SET against the map a sibling nightly file drives, so the integer was a second frozen copy of one fact. It stays as a floor because it is the only guard on the vacuum both sides share. | | `config-miss-stdout-purity.e2e.test.ts` | `src/commands/**` via helper | **3** | ⭐ No edit in this file — its reader lives in `test/helpers/config-miss-family.ts`, which this PR masks once for both consumers. | | `test/helpers/config-miss-family.ts` | `src/commands/**` | **3** | Masked. Both discovery halves are regexes over command source and both are satisfiable by prose: a docblock naming `json: Flags.boolean(` beside a `utils/config.js` import invents a direct member; a commented-out `export default class X extends Y` invents an alias. | | `diff-usage-error-stream.e2e.test.ts` | `src/commands/**` | **3** | Masked. This scan decides by LINE POSITION — writers above the first `flags.json` read — which is the print-ORDER failure that opened this card. Its `toBeGreaterThan(10)` population control was already a floor. | | `run-dev-unbuilt-workspace.e2e.test.ts` | `bin/run-dev.js` | **3** | Masked. `exec` takes the FIRST match, so a docblock recording the old `STDERR_DRAIN_STALL_MS` would be read as the shim's bound. Its sibling over the other published entry already masks; this makes the pair consistent. | | `serve-app-anchored-optional-import.e2e.test.ts` | `src/commands/serve.ts` | **1 + 2 + 3** | See below — five byte-exact statement pins, rewritten. | | `validate-json-failure-conversions.e2e.test.ts` | `src/commands/validate.ts` | **3** | Masked, and the paragraph that recorded the opposite decision is corrected rather than left false. | | `validate-json-failure-warnings.e2e.test.ts` | `src/commands/validate.ts` | **3** | Same. | | `serve-port-readback` · `published-entry-stderr-nonblocking` · `run-dev-stderr-nonblocking` | own source | — | Already masked. Untouched. | ### Rung 1 — where it applied, and where it did NOT Rung 1 permits DELETING a nightly assertion when a per-PR sibling already binds the same thing. I searched for a sibling for every byte-exact subject in this population and **opened** the one I found: - ✅ **Applied once.** `serve-app-anchored-optional-import` bound `function importFromHost(specifier: string, hostRoot: string = servedAppRootOrCwd())` — an argument LIST, the #17725 shape exactly. `src/commands/serve-cluster-host-resolution.test.ts` is queue-tier (its name carries no `.e2e`, so `nightly-tiers.mjs` leaves it in the per-PR run) and binds that function's EXISTENCE, its module scope and its uniqueness. So this PR does not re-pin any of that here; what it keeps, because the sibling does not bind it, is the DEFAULT — read off the paren-matched parameter list. - ⛔ **Did not apply anywhere else, and this is a measurement.** `anchorServedApp` and `servedAppRootOrCwd` appear in no other test in the package. The login emitter contract (`emitRecord`) appears in no queue-tier test at all. For `warningsSoFar`, the queue-tier `test/truncation-remainder-notices.test.ts` binds `'warnings: warningsSoFar(),'` in `compile.ts` — the CALL SITE, not the declaration and not the spread ORDER these files pin, and for `validate.ts` it binds different payload keys entirely. Not the same thing, so nothing was deleted on its account. ### `serve-app-anchored-optional-import` in detail Five `toContain`/`toMatch` pins over whole statements of `serve.ts`, in a file only the nightly tier collects. Each now binds what it was written for: | was | is | |:--|:--| | `toContain('const { configPath: absolutePath, configExists } = anchorServedApp(args.config!);')` | exactly ONE `anchorServedApp(` call site (the declaration excluded by the same `function` lookbehind the per-PR sibling uses), and its ARGUMENT is `args.config!`. The destructured local names are deliberately no longer bound. | | `not.toMatch(/const absolutePath = path\.resolve\(process\.cwd\(\), args\.config!\)/)` | the same negative as a SHAPE: `path.resolve(process.cwd(), args.config` with whitespace tolerated. A negative pin on one exact spelling passes for every respelling of the defect. | | `toContain('function importFromHost(specifier: string, hostRoot: string = servedAppRootOrCwd())')` | the `hostRoot` parameter's DEFAULT, read off the paren-matched parameter list. A third parameter or a renamed `specifier` no longer reddens it. | | `toContain('const hostRoot = servedAppRootOrCwd();')` and `toContain('const root = hostRoot ?? servedAppRootOrCwd();')` | a partition over every `const`/`let` host-root binding: at least two exist, and none may be bound without resolving through `servedAppRootOrCwd()`. | | `toMatch(/^function servedAppRootOrCwd\(\): string \{$/m)` | exactly one MODULE-SCOPE `function servedAppRootOrCwd(`, never indented, never a `const`/`let`/`var`. The return-type annotation and the brace are not the defect. | ## What the mask changed TODAY: nothing — and that is the measurement Every converted reader was evaluated raw and masked over the same tree, and every verdict is identical. Masking is therefore behaviour-preserving now and protective later — it did not launder a stale pin green: ``` SAME json-stdout-purity/discoverFamily (the same command ids) SAME config-miss-family/discover (the same 10 ids) SAME diff-usage/offenders = [] SAME diff-usage/withJson count = 28 SAME emitJson call sites — login.ts = 1 cloud/login.ts = 1 SAME rl.question without signal — login.ts = [] SAME payloadLiterals — validate.ts = 7 compile.ts = 11 SAME order indexes — validate.ts / compile.ts (declAt/tryAt/loadAt/normalizeAt) SAME serve.ts anchorServedApp / servedAppRootOrCwd sites SAME run-dev STDERR_DRAIN_STALL_MS = "15_000" ``` ⛔ `scripts/check-comment-mask-adoption.mjs` is green before and after and its ledger is unchanged at 14 rows — because none of these twelve files ever carried a private stripper. They carried NO masking at all, which is the blind spot the card names and the gate documents. This PR adds no private stripper; every file imports the shared module. ## Ablations Each is a script with `trap` restore on `EXIT INT TERM`, absolute paths, an on-disk occurrence count proving the mutation landed, exit codes captured BEFORE any pipe, and a restore proven by `git hash-object` against the HEAD blob plus a clean `git status` for both paths. ⚠️ The first attempt of all three read exit 1 everywhere in 17 seconds. That was not a result: without `OS_TEST_TIERS=nightly` the package collects none of these files, and it says so loudly. Recorded here because a run that measured nothing is the failure mode this card is about. The numbers below are from the re-run with the switch set. **ABL-1 — the #17633 shape, reproduced and then shown fixed.** A COMMENT is injected into `src/commands/info.ts` between `async run(` and its first `flags.json` read, naming `printHeader(`. Behaviour is untouched; only prose moved. | leg | pin | exit | reading | |:--|:--|--:|:--| | masked (this PR) | `diff-usage-error-stream` | **0** | prose is invisible | | raw (pre-conversion, restored from the merge base) | same file | **1** | `AssertionError: expected [ 'info.ts' ] to deeply equal []` | ⭐ That failure line IS the card's first row: a docblock reporting an order change that never happened. **ABL-2 — `serve.ts`, both directions.** | leg | mutation | pin | exit | reading | |:--|:--|:--|--:|:--| | 1 | `const hostRoot = servedAppRootOrCwd();` becomes `process.cwd()` | converted | **1** | `a host root is bound without resolving through servedAppRootOrCwd(): expected [ Array(1) ] to deeply equal []` — the new partition is not vacuous | | 2 | a THIRD parameter added to `importFromHost`, same arguments, same behaviour | converted | **0** | the argument LIST is no longer the contract | | 2 | same mutation | pre-conversion | **1** | `expected '…' to contain 'function importFromHost(specifier: st…'` — #17725 verbatim, on a pin no pull request can see | **ABL-3 — the login emitter partition.** | leg | mutation | pin | exit | reading | |:--|:--|:--|--:|:--| | 1 | a real second `await emitJson(` outside `emitRecord` | converted | **1** | `every --json write in login.ts must go through emitRecord(): expected [ Array(1) ] to deeply equal []` | | 2 | a COMMENT quoting `emitJson(payload, 0)` | converted | **0** | prose is invisible | | 2 | same comment | pre-conversion | **1** | same assertion — a comment breaking a pin whose code never moved, the card's second row | All three restored cleanly: `git hash-object` matches the HEAD blob for every mutated path and `git status --porcelain` is empty for both paths in each script. ## Verification - ⚠️ **Tier.** `OS_TEST_TIERS` unset collects none of these files. Every red/green below is under `OS_TEST_TIERS=nightly`. Independently confirmed by `pnpm check:tier-file-adoption`: 68 nightly-tier files on disk, owned by one package. - **`packages/cli` built** (`pnpm --filter '@objectstack/cli...' build`) so the pins that refuse at load on an absent `dist/index.js` actually run. - **Both whole-package runs, no `--project` filter — what CI runs:** ``` pnpm --filter @objectstack/cli test Test Files 267 passed (267) Tests 3485 passed (3485) exit 0 OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli test Test Files 68 passed (68) Tests 695 passed (695) exit 0 ``` The 12 converted files were also run on their own under `OS_TEST_TIERS=nightly` before the whole runs: 12 files / 336 tests / exit 0. - **Gate families** derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` and reconciled with `--ran`: 47 derived, 46 run green, **1 NOT MEASURED** — `pnpm check:dual-build-cjs-loads` exits 3 (PREREQUISITE NOT MET: it reads built output for packages outside this closure). ⛔ Recorded as NOT MEASURED, not as a pass and not as a red. - **`pnpm lint`** over the whole repo, unnarrowed. `node --stack-size=4000 eslint . --no-inline-config` over the whole repository, **exit 0**. This is the unnarrowed run, so no narrowing has to be justified. ## `skip-changeset`, by measurement with controls both ways This PR's own diff (`git diff --name-only origin/main...HEAD`) is 12 files, all under `packages/cli/test/`. `packages/cli`'s `files[]` is `["dist","README.md","CHANGELOG.md"]` and `tsconfig.build.json` has `include: ["src"]`, so `test/` reaches neither. Measured against the built tree rather than argued: - **negative** — `bodySpan`, `splitParams`, `paramsOf` (the three symbols this PR introduces): 0 files in `dist`, 0 in `README.md`. - **positive control** — `anchorServedApp` (2 files), `servedAppRootOrCwd` (2), `emitRecord` (3) in `dist`, so the grep over the published tree finds things and the zero above is a reading, not a dead search. - ⛔ No `scripts/**` path is touched, so the published surface did not have to be re-derived. ## Fences - ⛔ The six frozen integers in `build-json-failure-{warnings,conversions}` are untouched; card #18894 owns that axis. Only rung 3 was applied in those two files. - PR #18878 (card #18779) was re-measured at the start: zero intersection with this population, and it has since merged as `031e5fbfa3`. Its `compile.ts` / `validate.ts` edits moved no count these pins read (compile.ts still 11 payloads, validate.ts still 7), re-measured after the merge. This PR touches none of its six test files. - ⛔ No test is skipped, quarantined or weakened; no assertion was loosened to make a run pass. - ⛔ `content/docs/releases/` and every `packages/*/CHANGELOG.md` untouched. - ⛔ No command was refused by the permission classifier. ## Acceptance notes - `src/commands/serve-cluster-host-resolution.test.ts` carries a private `stripComments` at line 151. It is a DECLARED row in `check-comment-mask-adoption.mjs`'s shrink-only ledger, measured there as agreeing with the shared mask byte for byte over 212 files, so it is recorded debt rather than a finding, and converting it is that ledger's per-row work. Noted, not filed. - `validate-json-failure-{warnings,conversions}` still pin a multi-line spread ORDER in `validate.ts` byte-exactly (`...ruleAdvisories, ...docWarnings, ...`), and the two `build-json-failure-*` files pin the same shape over `compile.ts`. That is a rung-1/rung-2 question on an axis this PR did not open, and for the `build-*` pair it sits in the file the fence closes. Noted, not filed — the carrier is card #18894, which is already open over those two files. - `invocation-loudness.e2e.test.ts` and `serve-host-fallback-base.e2e.test.ts` reach `../src/...` and are NOT source-text readers (they symlink and execute it). No rung applies; recorded so the next re-derivation does not re-open them. Noted, not filed. --- _Generated by [Claude Code](https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 54145cc commit f112a74

12 files changed

Lines changed: 352 additions & 51 deletions

‎packages/cli/test/build-json-failure-conversions.e2e.test.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import { mkdtempSync, rmSync, writeFileSync, mkdirSync, readFileSync } from 'nod
6464
import { tmpdir } from 'node:os';
6565
import { join, resolve } from 'node:path';
6666
import { fileURLToPath } from 'node:url';
67+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
6768
import { childEnv } from './helpers/serve-process.js';
6869

6970
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -407,7 +408,14 @@ function payloadLiterals(src: string): string[] {
407408
}
408409

409410
describe('#12125 — the contract is exhaustive over `compile.ts`, not just over the exits pinned above', () => {
410-
const SRC = readFileSync(COMPILE_TS, 'utf8');
411+
// Comments are masked before a single thing is read off this file. A raw
412+
// read cannot tell CODE from PROSE, and this package has been bitten in both
413+
// directions: a docblock quoting a shape has satisfied a pin with no code
414+
// behind it, and a docblock quoting one 281 lines above the code has broken a
415+
// pin whose code never moved. `maskComments` BLANKS comment spans in place —
416+
// spaces for text, newlines kept — so every byte offset, every line number and
417+
// every brace-matching walk below reads exactly as it did on raw text (#18520).
418+
const SRC = maskComments(readFileSync(COMPILE_TS, 'utf8'));
411419

412420
it('the extractor produces a POSITIVE before its negative is trusted', () => {
413421
const SYNTHETIC = [

‎packages/cli/test/build-json-failure-warnings.e2e.test.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import { mkdtempSync, rmSync, writeFileSync, mkdirSync, readFileSync } from 'nod
8787
import { tmpdir } from 'node:os';
8888
import { join, resolve } from 'node:path';
8989
import { fileURLToPath } from 'node:url';
90+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
9091
import { childEnv } from './helpers/serve-process.js';
9192

9293
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -482,7 +483,14 @@ function payloadLiterals(src: string): string[] {
482483
}
483484

484485
describe('#11772 — the contract is exhaustive over `compile.ts`, not just over the exits pinned above', () => {
485-
const SRC = readFileSync(COMPILE_TS, 'utf8');
486+
// Comments are masked before a single thing is read off this file. A raw
487+
// read cannot tell CODE from PROSE, and this package has been bitten in both
488+
// directions: a docblock quoting a shape has satisfied a pin with no code
489+
// behind it, and a docblock quoting one 281 lines above the code has broken a
490+
// pin whose code never moved. `maskComments` BLANKS comment spans in place —
491+
// spaces for text, newlines kept — so every byte offset, every line number and
492+
// every brace-matching walk below reads exactly as it did on raw text (#18520).
493+
const SRC = maskComments(readFileSync(COMPILE_TS, 'utf8'));
486494

487495
it('the extractor produces a POSITIVE before its negative is trusted', () => {
488496
// ⭐ A "no payload lacks `warnings`" pass is worthless from an instrument

‎packages/cli/test/cloud-login-json-ndjson.e2e.test.ts‎

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import { mkdtempSync, rmSync, readFileSync, existsSync } from 'node:fs';
7070
import { tmpdir } from 'node:os';
7171
import { join, resolve } from 'node:path';
7272
import { fileURLToPath } from 'node:url';
73+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
7374
import { childEnv } from './helpers/serve-process.js';
7475

7576
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -440,25 +441,81 @@ describe('os cloud login --json — the declared NDJSON stream (#6730)', () => {
440441
});
441442
});
442443

444+
/**
445+
* The `{ … }` body of a declaration, brace-matched from its own `(`, as a span
446+
* in the MASKED source — so a `{` inside a comment cannot close it early and
447+
* the offsets are still the file's own line numbers.
448+
*/
449+
function bodySpan(src: string, declaration: RegExp): { start: number; end: number } | null {
450+
const m = declaration.exec(src);
451+
if (!m) return null;
452+
let i = src.indexOf('(', m.index);
453+
let depth = 0;
454+
for (; i < src.length; i++) {
455+
if (src[i] === '(') depth++;
456+
else if (src[i] === ')') {
457+
depth--;
458+
if (depth === 0) break;
459+
}
460+
}
461+
const open = src.indexOf('{', i);
462+
if (open === -1) return null;
463+
depth = 0;
464+
for (let j = open; j < src.length; j++) {
465+
if (src[j] === '{') depth++;
466+
else if (src[j] === '}') {
467+
depth--;
468+
if (depth === 0) return { start: open, end: j };
469+
}
470+
}
471+
return null;
472+
}
473+
443474
describe('the exception stays declared, not just implemented (#6730 ruling)', () => {
444-
const cloudLoginSrc = () => readFileSync(CLOUD_LOGIN_SRC, 'utf-8');
475+
// Masked before anything is read: every case in this describe decides from
476+
// the TEXT of `cloud/login.ts`, and a docblock that quotes `emitJson(` — the natural
477+
// way to explain why one emitter exists — is indistinguishable from a call to
478+
// it in a raw read (#18520).
479+
const cloudLoginSrc = () => maskComments(readFileSync(CLOUD_LOGIN_SRC, 'utf-8'));
445480

446481
it('routes every --json write through the single compact emitter', () => {
447482
// The contract is "one document per line" for the WHOLE command, so a new
448483
// write that called `emitJson` directly could reintroduce a multi-line
449484
// record on a path the e2e above does not drive. One emitter is what makes
450485
// that structurally impossible; this is the guard on the emitter.
451486
const src = cloudLoginSrc();
452-
const direct = src
453-
.split('\n')
454-
.map((line, i) => ({ line, n: i + 1 }))
455-
.filter(({ line }) => /\bemitJson\s*\(/.test(line))
456-
.filter(({ line }) => !/^\s*await emitJson\(payload, exitCode, \{ compact: true \}\);$/.test(line));
487+
488+
// ⛔ This used to subtract ONE BYTE-EXACT LINE — `await emitJson(payload,
489+
// exitCode, { compact: true });` — from the `emitJson(` line hits, and call
490+
// anything left an offender. That binds the argument LIST, and binding an
491+
// argument list is the defect this file's own tier cannot survive: a pull
492+
// request that adds a parameter, renames `payload`, or simply wraps the call
493+
// over two lines moves the spelling, the per-PR run never collects this file
494+
// to say so, and the red arrives on `main` days later under whatever card
495+
// happens to be open. Bind the PROPERTY the ruling actually made instead —
496+
// ONE emitter — by partitioning the call sites against the emitter's own
497+
// brace-matched body: outside must be empty, inside must not be, so neither
498+
// half can pass by finding nothing (#18520).
499+
const emitter = bodySpan(src, /async function emitRecord\s*\(/);
500+
expect(emitter, '`cloud/login.ts` no longer declares the single `emitRecord` emitter').not.toBeNull();
501+
502+
const sites = [...src.matchAll(/\bemitJson\s*\(/g)];
503+
const lineOf = (at: number): number => src.slice(0, at).split('\n').length;
504+
const outside = sites
505+
.filter((m) => m.index < emitter!.start || m.index > emitter!.end)
506+
.map((m) => {
507+
const eol = src.indexOf('\n', m.index);
508+
return `${lineOf(m.index)}: ${src.slice(m.index, eol === -1 ? undefined : eol).trim()}`;
509+
});
510+
457511
expect(
458-
direct.map(({ n, line }) => `${n}: ${line.trim()}`),
512+
outside,
459513
'every --json write in cloud/login.ts must go through emitRecord()',
460514
).toEqual([]);
461-
expect(/async function emitRecord\(/.test(src)).toBe(true);
515+
expect(
516+
sites.length - outside.length,
517+
'the emitter itself no longer calls `emitJson`, so the partition above is vacuous',
518+
).toBeGreaterThanOrEqual(1);
462519
});
463520

464521
it('declares NDJSON in the --json flag help text', () => {

‎packages/cli/test/diff-usage-error-stream.e2e.test.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { mkdtempSync, rmSync, existsSync, readFileSync, readdirSync, statSync }
6363
import { tmpdir } from 'node:os';
6464
import { join, resolve, relative, sep } from 'node:path';
6565
import { fileURLToPath } from 'node:url';
66+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
6667
import { childEnv } from './helpers/serve-process.js';
6768

6869
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -227,10 +228,17 @@ describe('no new command has grown a stdout write above its --json guard', () =>
227228
const WRITES_TO_STDOUT =
228229
/\b(?:console\.log|printHeader|printStep|printInfo|printSuccess|printWarning|printError)\(/;
229230

231+
// ⛔ Every read below is MASKED. This scan is the exact shape that has already
232+
// reported a change that never happened: it decides by LINE POSITION — `run()`
233+
// above the first `flags.json` read — and a raw read cannot tell a docblock
234+
// quoting `printSuccess(` from a call to it. A comment is enough to invent an
235+
// offender here, and enough to hide one by pushing the guard's line above a
236+
// write's. `maskComments` blanks spans in place, so the indices stay the
237+
// file's own (#18520).
230238
function offenders(): string[] {
231239
const found: string[] = [];
232240
for (const abs of commandFiles(COMMANDS_DIR)) {
233-
const lines = readFileSync(abs, 'utf-8').split('\n');
241+
const lines = maskComments(readFileSync(abs, 'utf-8')).split('\n');
234242
if (!lines.some((l) => /\bjson:\s*Flags\.boolean\(/.test(l))) continue;
235243
const runIdx = lines.findIndex((l) => /async run\s*\(/.test(l));
236244
if (runIdx < 0) continue;
@@ -250,7 +258,7 @@ describe('no new command has grown a stdout write above its --json guard', () =>
250258
// moved or renamed the commands directory would otherwise report "no
251259
// offenders" from an empty sweep.
252260
const withJson = commandFiles(COMMANDS_DIR).filter((abs) =>
253-
/\bjson:\s*Flags\.boolean\(/.test(readFileSync(abs, 'utf-8')),
261+
/\bjson:\s*Flags\.boolean\(/.test(maskComments(readFileSync(abs, 'utf-8'))),
254262
);
255263
expect(withJson.length).toBeGreaterThan(10);
256264
});

‎packages/cli/test/helpers/config-miss-family.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import { readFileSync, readdirSync, statSync } from 'node:fs';
2727
import { join, resolve, relative, sep } from 'node:path';
2828
import { fileURLToPath } from 'node:url';
29+
import { maskComments } from '../../../../scripts/js-comment-mask.mjs';
2930

3031
const HERE = resolve(fileURLToPath(import.meta.url), '..');
3132

@@ -135,7 +136,13 @@ function commandId(abs: string): string {
135136
*/
136137
export function discoverConfigMissFamily(): string[] {
137138
const files = commandFiles(COMMANDS_DIR);
138-
const sources = new Map(files.map((abs) => [abs, readFileSync(abs, 'utf-8')]));
139+
// Masked. Both halves below are regexes over command SOURCE, and both are
140+
// satisfiable by prose: a docblock naming `json: Flags.boolean(` beside an
141+
// import of `utils/config.js` invents a direct member, and a commented-out
142+
// `export default class X extends Y` invents an alias. The discovery feeds a
143+
// `toEqual` in two nightly-tier files, where a phantom member is a red no
144+
// pull request can be shown (#18520).
145+
const sources = new Map(files.map((abs) => [abs, maskComments(readFileSync(abs, 'utf-8'))]));
139146

140147
const direct = new Set<string>();
141148
for (const [abs, src] of sources) {

‎packages/cli/test/json-stdout-purity.e2e.test.ts‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import { mkdtempSync, rmSync, writeFileSync, readFileSync, readdirSync, statSync
6262
import { tmpdir } from 'node:os';
6363
import { join, resolve, relative, sep } from 'node:path';
6464
import { fileURLToPath } from 'node:url';
65+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
6566
import { childEnv } from './helpers/serve-process.js';
6667
import { CONFIG_MISS_FAMILY, discoverConfigMissFamily } from './helpers/config-miss-family.js';
6768

@@ -137,7 +138,12 @@ function commandFiles(dir: string): string[] {
137138
function discoverFamily(): string[] {
138139
const ids: string[] = [];
139140
for (const abs of commandFiles(COMMANDS_DIR)) {
140-
const src = readFileSync(abs, 'utf-8');
141+
// Masked: both halves are text probes, and both are satisfiable by prose. A
142+
// command whose only `bootSchemaStack(` is inside a docblock explaining that
143+
// it does NOT boot one would join this family and be driven against boot
144+
// diagnostics it never writes — a red in a tier no pull request collects,
145+
// wearing this file's title rather than the docblock's (#18520).
146+
const src = maskComments(readFileSync(abs, 'utf-8'));
141147
if (!src.includes('bootSchemaStack(')) continue;
142148
if (!/\bjson:\s*Flags\.boolean\(/.test(src)) continue;
143149
const rel = relative(COMMANDS_DIR, abs).replace(/\.ts$/, '');
@@ -233,7 +239,21 @@ describe('the family this contract has to hold across', () => {
233239
// assertion goes red.
234240
const preBoot = discoverConfigMissFamily();
235241
expect(preBoot).toEqual(Object.keys(CONFIG_MISS_FAMILY).sort());
236-
expect(preBoot).toHaveLength(10);
242+
243+
// ⛔ Not `toHaveLength(10)`. The line above already binds the SET, against a
244+
// map a sibling nightly file drives member by member — that equality IS the
245+
// contract, and a new member reddening it until it is driven is the point.
246+
// The integer bound nothing that equality did not, and it bound it in a
247+
// SECOND place that has to be hand-edited: two frozen numbers over one fact,
248+
// in a tier where the author who moves the fact is never shown the failure.
249+
// What it was load-bearing for is the vacuum the pair shares — a discovery
250+
// that stops matching returns `[]`, and an emptied map would agree with it —
251+
// so it stays as a FLOOR. Ten is the population the pre-boot family was
252+
// measured over, not a count of today (#18520).
253+
expect(
254+
preBoot.length,
255+
'the pre-boot `--json` discovery reports fewer faces than the family was measured over',
256+
).toBeGreaterThanOrEqual(10);
237257

238258
// The two families are NOT disjoint, and measuring that was worth more
239259
// than assuming it: `os migrate meta` is in both, legitimately and by

‎packages/cli/test/login-json-ndjson.e2e.test.ts‎

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { mkdtempSync, rmSync, readFileSync, existsSync } from 'node:fs';
6666
import { tmpdir } from 'node:os';
6767
import { join, resolve } from 'node:path';
6868
import { fileURLToPath } from 'node:url';
69+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
6970
import { childEnv } from './helpers/serve-process.js';
7071

7172
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -388,25 +389,81 @@ describe('os login --json — the declared NDJSON stream (#6531)', () => {
388389
});
389390
});
390391

392+
/**
393+
* The `{ … }` body of a declaration, brace-matched from its own `(`, as a span
394+
* in the MASKED source — so a `{` inside a comment cannot close it early and
395+
* the offsets are still the file's own line numbers.
396+
*/
397+
function bodySpan(src: string, declaration: RegExp): { start: number; end: number } | null {
398+
const m = declaration.exec(src);
399+
if (!m) return null;
400+
let i = src.indexOf('(', m.index);
401+
let depth = 0;
402+
for (; i < src.length; i++) {
403+
if (src[i] === '(') depth++;
404+
else if (src[i] === ')') {
405+
depth--;
406+
if (depth === 0) break;
407+
}
408+
}
409+
const open = src.indexOf('{', i);
410+
if (open === -1) return null;
411+
depth = 0;
412+
for (let j = open; j < src.length; j++) {
413+
if (src[j] === '{') depth++;
414+
else if (src[j] === '}') {
415+
depth--;
416+
if (depth === 0) return { start: open, end: j };
417+
}
418+
}
419+
return null;
420+
}
421+
391422
describe('the exception stays declared, not just implemented (#6531 ruling)', () => {
392-
const loginSrc = () => readFileSync(LOGIN_SRC, 'utf-8');
423+
// Masked before anything is read: every case in this describe decides from
424+
// the TEXT of `login.ts`, and a docblock that quotes `emitJson(` — the natural
425+
// way to explain why one emitter exists — is indistinguishable from a call to
426+
// it in a raw read (#18520).
427+
const loginSrc = () => maskComments(readFileSync(LOGIN_SRC, 'utf-8'));
393428

394429
it('routes every --json write through the single compact emitter', () => {
395430
// The contract is "one document per line" for the WHOLE command, so a new
396431
// write that called `emitJson` directly could reintroduce a multi-line
397432
// record on a path the e2e above does not drive. One emitter is what makes
398433
// that structurally impossible; this is the guard on the emitter.
399434
const src = loginSrc();
400-
const direct = src
401-
.split('\n')
402-
.map((line, i) => ({ line, n: i + 1 }))
403-
.filter(({ line }) => /\bemitJson\s*\(/.test(line))
404-
.filter(({ line }) => !/^\s*await emitJson\(payload, exitCode, \{ compact: true \}\);$/.test(line));
435+
436+
// ⛔ This used to subtract ONE BYTE-EXACT LINE — `await emitJson(payload,
437+
// exitCode, { compact: true });` — from the `emitJson(` line hits, and call
438+
// anything left an offender. That binds the argument LIST, and binding an
439+
// argument list is the defect this file's own tier cannot survive: a pull
440+
// request that adds a parameter, renames `payload`, or simply wraps the call
441+
// over two lines moves the spelling, the per-PR run never collects this file
442+
// to say so, and the red arrives on `main` days later under whatever card
443+
// happens to be open. Bind the PROPERTY the ruling actually made instead —
444+
// ONE emitter — by partitioning the call sites against the emitter's own
445+
// brace-matched body: outside must be empty, inside must not be, so neither
446+
// half can pass by finding nothing (#18520).
447+
const emitter = bodySpan(src, /async function emitRecord\s*\(/);
448+
expect(emitter, '`login.ts` no longer declares the single `emitRecord` emitter').not.toBeNull();
449+
450+
const sites = [...src.matchAll(/\bemitJson\s*\(/g)];
451+
const lineOf = (at: number): number => src.slice(0, at).split('\n').length;
452+
const outside = sites
453+
.filter((m) => m.index < emitter!.start || m.index > emitter!.end)
454+
.map((m) => {
455+
const eol = src.indexOf('\n', m.index);
456+
return `${lineOf(m.index)}: ${src.slice(m.index, eol === -1 ? undefined : eol).trim()}`;
457+
});
458+
405459
expect(
406-
direct.map(({ n, line }) => `${n}: ${line.trim()}`),
460+
outside,
407461
'every --json write in login.ts must go through emitRecord()',
408462
).toEqual([]);
409-
expect(/async function emitRecord\(/.test(src)).toBe(true);
463+
expect(
464+
sites.length - outside.length,
465+
'the emitter itself no longer calls `emitJson`, so the partition above is vacuous',
466+
).toBeGreaterThanOrEqual(1);
410467
});
411468

412469
it('declares NDJSON in the --json flag help text', () => {

‎packages/cli/test/login-json-noninteractive.e2e.test.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { mkdtempSync, rmSync, readFileSync } from 'node:fs';
5656
import { tmpdir } from 'node:os';
5757
import { join, resolve } from 'node:path';
5858
import { fileURLToPath } from 'node:url';
59+
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
5960
import { childEnv } from './helpers/serve-process.js';
6061

6162
const HERE = resolve(fileURLToPath(import.meta.url), '..');
@@ -316,7 +317,12 @@ describe('the paths that must keep working (#6728 did not narrow them)', () => {
316317
});
317318

318319
describe('the refusal stays structural, not one call site (#6728)', () => {
319-
const loginSrc = () => readFileSync(LOGIN_SRC, 'utf-8');
320+
// Masked: both cases below decide from the TEXT of `login.ts`. A docblock
321+
// that quotes `rl.question(` without the abort signal — which is exactly what
322+
// a comment explaining `askOrFailAtEof` would carry — reads as a live
323+
// unsettleable prompt, and the flag-help extractor's non-greedy `})` stops at
324+
// a `})` inside a comment. Neither failure is about the product (#18520).
325+
const loginSrc = () => maskComments(readFileSync(LOGIN_SRC, 'utf-8'));
320326

321327
it('asks no question that can outlive its input', () => {
322328
// `rl.question(...)` without the abort signal is the unsettleable form —

0 commit comments

Comments
 (0)