Skip to content

Commit 24f2191

Browse files
committed
chore(pm): the root-program change kind names typecheck:root
With the root graduated, check:type-check-debt no longer measures the root program, so a card editing a root TypeScript file was sent to a gate that cannot red on it. The family parser now reads pnpm typecheck:root as a family, and the root-program change kind names it with a rewritten why. Claude-Session: https://claude.ai/code/session_01ENjVvxdwZxosWSS4Fqazu1 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 817e38e commit 24f2191

3 files changed

Lines changed: 32 additions & 17 deletions

File tree

‎scripts/pm/dispatch-gates.data.mjs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,9 @@ export const ESCAPABLE_LITERAL_LEDGER_ROWS = Object.freeze([
759759
*
760760
* ⚠ Its known limit, stated here rather than discovered later: `exclude` drops
761761
* files only from tsc's INITIAL WALK, so sources under an excluded directory
762-
* that a root script IMPORTS are pulled into the program anyway — the ledger
763-
* note for this entry records 4 of its 80 errors arriving exactly that way,
764-
* from the showcase example. A path-shaped trigger cannot see an import graph,
762+
* that a root script IMPORTS are pulled into the program anyway — the root's
763+
* DEBT note recorded 4 of its 80 errors arriving exactly that way, from the
764+
* showcase example, before the root graduated. A path-shaped trigger cannot see an import graph,
765765
* so a card editing only such a file is still not sent here. That is
766766
* deliberately NOT closed on this card: the direction is the safe one (this
767767
* entry under-covers rather than over-covers), and closing it means resolving
@@ -978,8 +978,8 @@ export const CHANGE_KIND_ROWS = [
978978
matches: 'root-ts-program',
979979
gates: [
980980
{
981-
name: 'check:type-check-debt',
982-
why: 'the ROOT ledger entry (@objectstack/spec-monorepo) IS this program, so a file here moves its raw tsc count even though your diff touches no package — measured, one added bench file put it 19 over and cost a CI round. It is a shrink-only ratchet: the repair is to make the file typecheck, and raising the entry is maintainer-only — ⛔ MAINTAINER-ONLY under the #8435 convention — never the co-equal option. Most of this class is one missing setting rather than real breakage — the root config carries lib ES2020 and no types, so process and console are absent unless the file declares them ambiently. Needs the workspace closure BUILT — on an unbuilt worktree it refuses outright, and that throw means NOT MEASURED, never `not applicable to me`. Build first, exactly as lint.yml does: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* (quote the filter values for your shell)',
981+
name: 'typecheck:root',
982+
why: 'nothing else type-checks this program: it belongs to no package, so the turbo typecheck never reads a file here, and the root has no ledger entry left to absorb an error. lint.yml runs this script in the Type Check source-gates lane, and one new error reds that lane. The repair is to make the file typecheck, never to add an exclude or bring back a DEBT entry. It needs no build: the program reads no package dist. The program carries lib ES2022 and types node, so `process`, `console` and the `node:*` builtins resolve without a reference line',
983983
},
984984
],
985985
},

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,7 +2223,10 @@ export function extractCheckInvocations(workflowText, workflowFile, { via = null
22232223
// this card is about: a value that reaches the program only through
22242224
// `process.env`, which no reader of the command line can see.
22252225
const carriedEnv = envNamesNotSpelledInCommand(cmd, stepEnv);
2226-
for (const m of cmd.matchAll(/pnpm\s+(?:--filter\s+(\S+)\s+)?(?:run\s+)?(check:[\w:-]+)/g)) {
2226+
// `typecheck:root` is the one non-`check:` script read here: it is the ROOT
2227+
// program's only type check, and the root-program change kind names it, so it
2228+
// has to resolve to a family or that row renders STALE.
2229+
for (const m of cmd.matchAll(/pnpm\s+(?:--filter\s+(\S+)\s+)?(?:run\s+)?(check:[\w:-]+|typecheck:root(?![\w:-]))/g)) {
22272230
out.push(withVia({ check: m[2], filter: m[1] ?? null, workflow: workflowFile, envVariables: carriedEnv }));
22282231
}
22292232
for (const m of cmd.matchAll(DIRECT_CHECK_INVOCATION)) {
@@ -10779,8 +10782,9 @@ export function guardsADurabilityCriticalCall(path, readSource = readTrackedSour
1077910782
}
1078010783

1078110784
/**
10782-
* Is this path inside the ROOT package's tsc program — the population behind
10783-
* the `@objectstack/spec-monorepo` entry of `check:type-check-debt`?
10785+
* Is this path inside the ROOT package's tsc program — the population the
10786+
* root's `typecheck:root` script checks (until the root graduated, the
10787+
* `@objectstack/spec-monorepo` entry of `check:type-check-debt`)?
1078410788
*
1078510789
* This is the one gate population in the tree that no path literal can ever
1078610790
* describe, in principle rather than by omission. The root program is declared

‎scripts/pm/dispatch-gates.self-test.mjs‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,14 @@ export function selfTest({ tier = 'full' } = {}) {
464464
+ 'right names a file this ROOT does not hold, and every audit downstream then passes for the wrong reason',
465465
!pkgInvs.some((i) => String(i.script ?? i.check).startsWith('scripts/')),
466466
);
467+
t(
468+
'the root program\'s `typecheck:root` is read as a family, so the root change kind can name it',
469+
extractCheckInvocations(' - run: pnpm typecheck:root\n', 'lint.yml').some((i) => i.check === 'typecheck:root' && !i.direct),
470+
);
471+
t(
472+
'control — that exception is the one script name, not every `typecheck` spelling',
473+
extractCheckInvocations(' - run: pnpm typecheck\n - run: pnpm typecheck:rooted\n - run: pnpm --filter ./examples/x run typecheck\n', 'x.yml').length === 0,
474+
);
467475
t(
468476
'control — a CLIMBING spelling is refused rather than resolved: `..` is not a prefix segment, because '
469477
+ 'a path this ROOT cannot resolve is exactly how a phantom identity key gets minted',
@@ -4423,10 +4431,12 @@ export function selfTest({ tier = 'full' } = {}) {
44234431
// rename, which is the one rot class the STALE branch exists to report.
44244432
const rootKind = changeKindLines(['scripts/bench/runtime-publish-gate.bench.mts'], resolved);
44254433
t('a root-program path emits the convention section', rootKind.length === 2 && rootKind[0].includes('ROOT tsc program'));
4426-
t('and it names the RATCHET half — the invocation that re-measures', rootKind.some((l) => l.includes('- pnpm check:type-check-debt —')));
4427-
const rootLine = rootKind.find((l) => l.includes('- pnpm check:type-check-debt —')) ?? '';
4428-
t('the root-program line refuses the baseline raise and states the real repair', /shrink-only/.test(rootLine) && /maintainer-only/.test(rootLine));
4429-
t('and carries the built-closure prerequisite, like the other ratchet line', /closure BUILT/.test(rootLine) && rootLine.includes('turbo run build'));
4434+
// The root graduated from the DEBT ledger (#26236), so the gate that reads
4435+
// this program is its own `typecheck:root` script, which lint.yml runs.
4436+
t('and it names the root program\'s own type check', rootKind.some((l) => l.includes('- pnpm typecheck:root —')));
4437+
const rootLine = rootKind.find((l) => l.includes('- pnpm typecheck:root —')) ?? '';
4438+
t('the root-program line refuses an exclude or a returning ledger entry and states the real repair', /never to add an exclude/.test(rootLine) && /make the file typecheck/.test(rootLine));
4439+
t('and says it needs no build, unlike the ratchet lines', /needs no build/.test(rootLine) && !/closure BUILT/.test(rootLine));
44304440
// Re-pointed rather than deleted (#12074). This case pinned one property —
44314441
// a `.mjs` checker script is NOT in the ROOT tsc program, unlike the `.mts`
44324442
// bench file above — and the gate-script kind below now makes the same path
@@ -4436,7 +4446,7 @@ export function selfTest({ tier = 'full' } = {}) {
44364446
// hiding inside a `length` this case no longer checks.
44374447
const checkerKind = changeKindLines(['scripts/check-type-check-coverage.mjs'], resolved);
44384448
t('a checker script is still outside the ROOT tsc program', !checkerKind.some((l) => l.includes('ROOT tsc program')));
4439-
t('…and the root ratchet is not named for it', !checkerKind.some((l) => l.includes('- pnpm check:type-check-debt —')));
4449+
t('…and the root type check is not named for it', !checkerKind.some((l) => l.includes('- pnpm typecheck:root —')));
44404450
t('…and the ONE section it does emit is the gate-script kind', checkerKind.length === 3 && checkerKind[0].includes('GATE SCRIPT'));
44414451

44424452
// -- The GATE SCRIPT entry (#12074) --------------------------------------
@@ -5813,11 +5823,12 @@ export function selfTest({ tier = 'full' } = {}) {
58135823
// Seven, not six, since #10542 added check:cross-package-test-inputs to the
58145824
// test-file kind. `a.test.ts` is a root-level TypeScript file, so it is BOTH
58155825
// a test file and inside the root tsc program and legitimately hits two
5816-
// kinds. The ratchet therefore renders twice, under a different `why` each
5817-
// time — pinned just below, because a bare count cannot tell that apart from
5818-
// one kind rotting away.
5826+
// kinds. Each kind renders its own gate as STALE (the ratchet under the test
5827+
// kind, `typecheck:root` under the root kind since the root graduated) —
5828+
// pinned just below, because a bare count cannot tell that apart from one
5829+
// kind rotting away.
58195830
t('an undiscoverable gate renders as STALE', stale.filter((l) => l.includes('STALE')).length === 7);
5820-
t('a root-level test file hits both kinds, so the ratchet renders STALE under each', stale.filter((l) => l.includes('\u26a0 check:type-check-debt: STALE')).length === 2);
5831+
t('a root-level test file hits both kinds: the ratchet renders STALE under the test kind, the root type check under the root kind', stale.filter((l) => l.includes('\u26a0 check:type-check-debt: STALE')).length === 1 && stale.filter((l) => l.includes('\u26a0 typecheck:root: STALE')).length === 1);
58215832
// Per NAME, anchored on both sides of the rendered name (`⚠ x: STALE`), so the
58225833
// pair that shares one script is reported apart: a count alone stays green if
58235834
// one of the two is dropped from the table and something else is added, and a

0 commit comments

Comments
 (0)