Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions scripts/coverage-crap/patches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,22 @@ async function convertRecord(convert: Converter, capture: Capture): Promise<Ista
coverage: { functions: capture.functions, url: capture.url },
wrapperLength: capture.wrapperLength,
});
return converted[TARGET] as IstanbulRecord;
const record = converted[TARGET];
assert.ok(record, `the converter recorded no coverage for ${TARGET}`);
return record as IstanbulRecord;
}

/** The counters the converter publishes for the `if` on `line`. */
function ifCounters(record: IstanbulRecord, line: number): number[] {
const id = Object.keys(record.b).find(
(key) => record.branchMap[key].type === 'if' && record.branchMap[key].line === line,
);
const id = Object.keys(record.b).find((key) => {
const branch = record.branchMap[key];
assert.ok(branch, `the converter recorded no branch map entry for ${key}`);
return branch.type === 'if' && branch.line === line;
});
assert.ok(id, `the converter recorded no if branch on line ${line}`);
return record.b[id].map(Number);
const counters = record.b[id];
assert.ok(counters, `the converter recorded no counters for if branch on line ${line}`);
return counters.map(Number);
}

// `async-fallback-operand` is the reported shape: `params.release ?? await probe(...)` feeding an
Expand All @@ -99,7 +105,9 @@ function ifCounters(record: IstanbulRecord, line: number): number[] {
test('an async branch with an awaited operand drops the path it cannot count', async () => {
const convert = await loadConverter();
const capture = captures['async-fallback-operand'];
assert.ok(capture, 'missing async-fallback-operand capture');
const branch = capture.branches.i1;
assert.ok(branch, 'missing i1 branch in async-fallback-operand capture');
assert.deepEqual([branch.entered, branch.taken], [8, 6]);
const record = await convertRecord(convert, capture);
// The taken path is provable and stays. The entry count is not recoverable from a record whose
Expand All @@ -112,20 +120,27 @@ test('an async branch with an awaited operand drops the path it cannot count', a
test('a branch whose awaited continuation out-counts it publishes no impossible count', async () => {
const convert = await loadConverter();
const capture = captures['early-return-then-fallback'];
assert.ok(capture, 'missing early-return-then-fallback capture');
const firstBranch = capture.branches.i1;
const secondBranch = capture.branches.i2;
assert.ok(firstBranch, 'missing i1 branch in early-return-then-fallback capture');
assert.ok(secondBranch, 'missing i2 branch in early-return-then-fallback capture');
const record = await convertRecord(convert, capture);
assert.deepEqual([capture.branches.i1.entered, capture.branches.i1.taken], [8, 3]);
assert.deepEqual([capture.branches.i2.entered, capture.branches.i2.taken], [5, 2]);
assert.deepEqual([firstBranch.entered, firstBranch.taken], [8, 3]);
assert.deepEqual([secondBranch.entered, secondBranch.taken], [5, 2]);
// The branch ahead of the await is counted correctly and must not move; the one behind it was
// `[2,-2]` before the patch.
assert.deepEqual(ifCounters(record, capture.branches.i1.line), [3, 5]);
assert.deepEqual(ifCounters(record, capture.branches.i2.line), [2]);
assert.deepEqual(ifCounters(record, firstBranch.line), [3, 5]);
assert.deepEqual(ifCounters(record, secondBranch.line), [2]);
});

test('sync and non-awaited branches keep counting their implicit else', async () => {
const convert = await loadConverter();
for (const name of ['sync-fallback-operand', 'plain-async']) {
const capture = captures[name];
assert.ok(capture, `missing ${name} capture`);
const branch = capture.branches.i1;
assert.ok(branch, `missing i1 branch in ${name} capture`);
assert.deepEqual(
ifCounters(await convertRecord(convert, capture), branch.line),
[6, 2],
Expand Down
14 changes: 6 additions & 8 deletions scripts/layering/package-boundaries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,8 @@ test('specifier sites carry 1-based lines for static and dynamic imports', () =>
});

test('the compiled set classifies every file region the R11 walk visits', () => {
// The compiler-owned/compiler-exempt split above is keyed on this predicate (#3279), so the
// mutation it must fail on is a file region joining or leaving `tsc -b`'s programs without
// the predicate following: package `src/`, root `src/` and root `test/` compile; `scripts/`
// and the package harness dirs are the fail-closed side.
// Package src, root src and root test are compiler-owned. Project-reference pins record
// compiler-root changes; scripts and package harness directories retain full R11 checks.
assert.equal(insideCompiledSources('packages/kernel/src/errors.ts'), true);
assert.equal(insideCompiledSources('packages/maestro/src/index.ts'), true);
assert.equal(insideCompiledSources('src/cli.ts'), true);
Expand All @@ -122,11 +120,11 @@ test('the compiled set classifies every file region the R11 walk visits', () =>
assert.equal(insideCompiledSources('packages/maestro/test/conformance/harness.ts'), false);
assert.equal(insideCompiledSources('packages/proxy/tsdown.config.ts'), false);

// The one scripts file the root program DOES compile is classified as uncompiled: R11
// keeps its full branch set there (redundant with tsc, never narrower). The exact root
// include list and the reference sets are pinned against the graph itself in
// project-references.test.ts, where the divergence is recorded rather than assumed absent.
assert.equal(insideCompiledSources('scripts/help-conformance-command-validator.ts'), false);
assert.equal(insideCompiledSources('scripts/coverage-crap/stamp.ts'), false);
assert.equal(insideCompiledSources('scripts/coverage-changed/run.ts'), false);
assert.equal(insideCompiledSources('scripts/vitest-coverage-run-stamp-reporter.ts'), false);
assert.equal(insideCompiledSources('scripts/vitest-coverage-run-stamp-reporter.test.ts'), false);
});

test('readWorkspacePackages reads tracked manifests only', () => {
Expand Down
29 changes: 14 additions & 15 deletions scripts/layering/package-boundaries.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Catches: a package importing a workspace sibling its manifest never declared, and a root
// file tunnelling into a package's src with a relative path — both for every file, compiled
// or not — plus the whole specifier sweep (unknown package, un-exported subpath) for the
// scripts/ files the type graph omits. `tsc -b` owns the rest of the old R11 surface for
// scripts/ files, including tooling roots the type graph also checks. `tsc -b` owns the
// rest of the old R11 surface for
// compiled sources: NodeNext resolution rejects a non-exported subpath and an unknown
// package (TS2307), and composite `rootDir` rejects a package→root relative escape
// (TS6059 + TS6307) — planted proofs recorded on #3279.
Expand All @@ -18,13 +19,13 @@
//
// R11 package-boundaries: the workspace rules of #1490, as data the gate walks.
//
// What remains is exactly what the type graph cannot see:
// R11 retains the claims the type graph cannot see, plus conservative checks for scripts:
// a package import of a sibling the manifest never declared (pnpm's root links make it resolve
// anyway), a root file tunnelling into `packages/*/src` with a relative path (tsc typechecks
// it fine; Node's ESM loader does not realpath specifiers, so a module loaded BOTH relatively
// and via its package specifier instantiates twice in one process — duplicate AppError, broken
// instanceof), and, for `scripts/` files which sit outside the type graph by design, the
// root-side unknown-package and exports-map checks that compiled files get from TS2307.
// instanceof), and, for all `scripts/` files, the root-side unknown-package and exports-map
// checks. Explicit tooling roots also receive those resolution checks from TS2307.
// On top, `TEST_ONLY_FIXTURE_SUBPATHS` mirrors R13's Android fixture gate for the non-platform
// packages: a production file importing one of those fixture subpaths is an R11 violation.

Expand Down Expand Up @@ -221,20 +222,18 @@ function specifierPackageName(specifier: string): string | undefined {

/**
* The file set R11 treats as parsed by the `tsc -b` graph: each package project includes its
* own `src/`, and the root project includes `src/` and `test/`. Everything else the R11 walk
* visits — the rest of `scripts/` (gate and tooling modules run through
* `--experimental-strip-types`, not a tsconfig program), the `packages/maestro/test/`
* conformance harness, and package build configs — reaches packages through import sites the
* own `src/`, and the root project includes `src/` and `test/`. Other regions the R11 walk
* visits include `scripts/`, the `packages/maestro/test/` conformance harness, and package
* build configs. Files outside the compiler graph reach packages through import sites the
* compiler never parses, so R11 stays the only owner of every boundary claim about those
* routes (#3279). Only the RESOLUTION branches (unknown package, un-exported subpath) are
* split this way; the relative-escape branch fires regardless — a planted sibling tunnel
* `tsc -b` accepted, see `checkPackageInternalSites`.
*
* One documented divergence: the root program also includes
* `scripts/help-conformance-command-validator.ts`, which this predicate classifies as
* uncompiled. That is fail-closed — R11 keeps its full branch set (unknown package,
* exports-map) for the file on top of what tsc enforces — never fail-open. The divergence
* and the exact root include list are pinned in `project-references.test.ts`, and the
* The root program also includes explicit help-conformance and coverage-tooling roots.
* This predicate classifies all scripts as uncompiled. That is fail-closed — R11 keeps its
* full branch set (unknown package, exports-map) on top of what tsc enforces. The divergence
* and the root include regions are pinned in `project-references.test.ts`, and the
* per-package `include: ["src"]` / no-`exclude` shape this predicate's regexes assume is
* pinned there too, so widening any project's file set must be recorded there rather than
* silently diverging from this predicate.
Expand Down Expand Up @@ -364,8 +363,8 @@ export function checkPackageInternalSites(
* fine (#3279), but Node's ESM loader does not realpath specifiers, so dual relative/specifier
* loads instantiate the module twice — and a workspace specifier the root manifest never
* declares, which the root's own link farm resolves anyway. For compiled root files the
* compiler owns unknown packages and un-exported subpaths with TS2307; for scripts/ files,
* which sit outside the type graph by design, R11 keeps every branch.
* compiler owns unknown packages and un-exported subpaths with TS2307. For scripts/ files,
* R11 keeps every branch, including where explicit tooling roots also receive compiler checks.
*/
export function checkRootSites(
sites: readonly SpecifierSite[],
Expand Down
38 changes: 13 additions & 25 deletions scripts/layering/project-references.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,19 @@ test('root and examples/sdk reference exactly the workspace package set', () =>
});

test('the compiled-set predicate is licensed by the tsconfig include/exclude shape it mirrors', () => {
// `insideCompiledSources` (package-boundaries.ts) hard-codes the three include shapes the
// tsc -b graph compiles: `packages/*/src`, root `src`, root `test`. That regex is honest
// only while no project widens its file set behind it, so the shape is pinned here (the
// file that already reads the tsconfigs), and the R11 side keeps its pin in
// package-boundaries.test.ts (the #3279 walk-region test). A package tsconfig that gains
// an `exclude`, or an include beyond `src` (plus replay-port's shared `global.d.ts`
// sibling), or a root file added to the root include beyond src/test, must update the
// predicate or fail here — the same "views cannot drift" rule as the reference lists.
assert.deepEqual(readTsconfig('tsconfig.json').include, [
'src',
'test',
'scripts/help-conformance-command-validator.ts',
'packages/command-registry/src/global.d.ts',
]);
// Scripts retain full R11 checks even when the compiler also checks them.
const rootConfig = readTsconfig('tsconfig.json');
assert.equal(
rootConfig.exclude,
undefined,
'root tsconfig must not exclude compiler-owned paths',
);
assert.deepEqual(
(rootConfig.include ?? []).filter(
(entry) => !path.posix.normalize(entry).startsWith('scripts/'),
),
['src', 'test', 'packages/command-registry/src/global.d.ts'],
);
// `src` everywhere, plus the one shared ambient-declaration entry replay-port and
// session-journal include. Exact include ARRAYS, not a `global.d.ts` suffix class: a
// new include of any name must extend this allow-list deliberately, or the drift fails
Expand All @@ -94,17 +93,6 @@ test('the compiled-set predicate is licensed by the tsconfig include/exclude sha
}
});

test('the one scripts file in the root program is the divergence insideCompiledSources records', () => {
// The predicate classifies scripts/help-conformance-command-validator.ts as UNCOMPILED
// (fail-closed: R11 keeps its full branch set there) even though the root include names
// it. This is the assertion the predicate's doc block points at: the divergence is
// recorded here, and a second scripts file joining the root program must be recorded
// next to this one rather than silently widening the exception.
const rootInclude = readTsconfig('tsconfig.json').include ?? [];
const scriptsEntries = rootInclude.filter((entry) => entry.startsWith('scripts/'));
assert.deepEqual(scriptsEntries, ['scripts/help-conformance-command-validator.ts']);
});

test('the root manifest declares every workspace package as a workspace dependency', () => {
const manifest = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8')) as {
dependencies?: Record<string, string>;
Expand Down
8 changes: 6 additions & 2 deletions scripts/lib/cli-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ export function parseScriptArgs<T extends Options>(
usage: string,
options: T,
): ReturnType<typeof parseArgs<{ options: T & { help: { type: 'boolean' } } }>>['values'] {
const { values } = parseArgs({
const { values } = parseArgs<{
args: readonly string[];
options: T & { help: { type: 'boolean'; short: 'h'; default: false } };
allowPositionals: false;
}>({
args: [...argv],
options: { ...options, help: { type: 'boolean', short: 'h', default: false } },
allowPositionals: false,
});
if (values.help) {
if ((values as { help?: boolean }).help) {
process.stdout.write(usage);
process.exit(0);
}
Expand Down
4 changes: 4 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"src",
"test",
"scripts/help-conformance-command-validator.ts",
"scripts/coverage-crap/**/*.ts",
"scripts/coverage-changed/**/*.ts",
"scripts/vitest-coverage-run-stamp-reporter.ts",
"scripts/vitest-coverage-run-stamp-reporter.test.ts",
"packages/command-registry/src/global.d.ts"
],
"references": [
Expand Down
Loading