Skip to content

Commit acef174

Browse files
committed
test: pin the bundle-stamp acquittal and the two cases that keep it non-vacuous
Three cases in `check:browser-reachable-entries --self-test`, beside the mtime cases that already drive `inspectBundleFreshness`: the acquittal itself, the conviction that returns the moment a source byte changes (so the rule is keyed on the digest and not on the stamp's presence), and a stamp that is not a digest at all leaving the refusal standing. A fourth asserts the sandbox digest can be computed, so a fixture that silently stopped producing one cannot pass the rest by accident. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
1 parent 6f9447c commit acef174

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

packages/spec/scripts/check-browser-reachable-entries.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ import { tmpdir } from 'node:os';
152152
import { dirname, join, relative, resolve } from 'node:path';
153153
import { fileURLToPath } from 'node:url';
154154

155+
import { buildStamp } from '../../../scripts/check-regen-pending.mjs';
155156
import { scanSource } from '../../../scripts/js-comment-mask.mjs';
156157
import { inspectBundleFreshness } from './lib/dist-freshness';
157158

@@ -927,6 +928,71 @@ function selfTest(): never {
927928
!inspectBundleFreshness(fresh, 'check', RERUN).fresh,
928929
mtimes(),
929930
);
931+
932+
// ── The acquittal: an mtime accusation that CAN be answered (#16175) ────
933+
// Every case above is the mtime rule convicting, and until #16175 there was
934+
// nothing it would accept as an answer. So it also refused the tree a
935+
// `git merge`, `git checkout` or `git worktree add` leaves behind: those
936+
// re-check-out an UNCHANGED source file — bytes identical, mtime bumped —
937+
// and the build that follows is a turbo cache hit that rewrites nothing, so
938+
// every dist/ mtime stays where the previous build left it. Measured on the
939+
// real tree: `touch packages/spec/src/data/query.zod.ts` with `git status`
940+
// empty made this gate exit 1 and prescribe a multi-minute rebuild of
941+
// bundles that were already exactly current.
942+
//
943+
// The evidence is `dist/.build-input-hash`, which every build of the package
944+
// writes AFTER its unconditional `tsup` pass. It may only ever ACQUIT, and
945+
// the two cases after this one are what keep that non-vacuous.
946+
const stampFile = join(fresh, 'dist', '.build-input-hash');
947+
// Two steps, for the reason `dist-freshness.test.ts` gives: `buildStamp`
948+
// computes `actual` only when a syntactically valid digest is recorded, so
949+
// seed a placeholder, read what the sources really hash to, then write that.
950+
// Asked of the rule's own reader rather than hardcoded — the input set
951+
// includes turbo.json's globalDependencies and every input's repo-relative
952+
// path, so a literal here would rot into a `mismatch` that reads exactly
953+
// like the refusal these cases exist to tell apart. The stamp is written
954+
// last, so it records the sandbox as it now stands, tsup.config.ts included
955+
// (that file is in the digest's input set as well as in the mtime rule's).
956+
writeFileSync(stampFile, `${'0'.repeat(64)}\n`);
957+
const digest = buildStamp(fresh).actual;
958+
check(
959+
'the sandbox build-input digest can be computed at all (the fixture is not vacuous)',
960+
typeof digest === 'string' && /^[0-9a-f]{64}$/.test(digest),
961+
JSON.stringify(digest),
962+
);
963+
writeFileSync(stampFile, `${digest}\n`);
964+
check(
965+
'ACQUITS an mtime-stale tree whose build stamp matches the sources (#16175)',
966+
inspectBundleFreshness(fresh, 'check', RERUN).fresh,
967+
mtimes(),
968+
);
969+
970+
// The half that makes the case above non-vacuous. If the acquittal were
971+
// keyed on the stamp's mere PRESENCE rather than on the digest, this would
972+
// stay green — and that is #7122's false green restored, one axis over. The
973+
// stamp is written first and the source edited after, so the recorded digest
974+
// is genuinely stale rather than never-valid.
975+
writeFileSync(srcFile, 'export const a = 2;\n');
976+
stamp(srcFile, 0);
977+
check(
978+
'and CONVICTS the same tree the moment a source byte actually changes',
979+
!inspectBundleFreshness(fresh, 'check', RERUN).fresh,
980+
mtimes(),
981+
);
982+
983+
// Absence of the freshness input is not licence to acquit (#4690), and
984+
// neither is a truncated or half-flushed write. Anything that is not 64 hex
985+
// characters is `unstamped`, which leaves the mtime verdict standing. The
986+
// source is restored to the bytes the digest above was taken over first, so
987+
// the ONLY reason this refuses is the stamp itself.
988+
writeFileSync(srcFile, 'export const a = 1;\n');
989+
stamp(srcFile, 0);
990+
writeFileSync(stampFile, 'not-a-digest\n');
991+
check(
992+
'ignores a build stamp that is not a digest at all',
993+
!inspectBundleFreshness(fresh, 'check', RERUN).fresh,
994+
mtimes(),
995+
);
930996
} finally {
931997
rmSync(fresh, { recursive: true, force: true });
932998
}

0 commit comments

Comments
 (0)