chore(scripts): build test-servers unconditionally from one shared helper

Five `scripts/*.mjs` consumers each carried their own `ensureTestServer()`
that returned early when `test-servers/build` was already on disk. Presence
is not freshness: once that directory exists — which it does on any machine
that has run the smokes once — an edit to `test-servers/src` was never
picked up again, and the smoke went on driving the previously-built fixture.

The failure is silent and misattributed. It does not read as "your fixture
is stale"; it reads as a product bug in whatever the smoke is checking.

`scripts/lib/ensure-test-servers.mjs` is now the one place that build is
produced for a script consumer, and it builds unconditionally — memoized
once per process per repo root, so pack-and-verify's two asks stay one tsc
pass while a fresh run always rebuilds. Callers name the entries they
depend on and get one actionable failure message, replacing five that had
drifted. pack-and-verify also stops shelling out to `npx tsc`, which is
what made it the one script unrunnable on Windows (#1939).

`test-servers/tsconfig.json` gains `incremental` so the up-to-date case is
cheap: 3.0s cold, 1.6s incremental here.

Unconditional emit is still not a clean — `--noCheck` leaves the stale .js
of a deleted src file behind — which is unchanged from before and recorded
in the helper's header and AGENTS.md.

Closes #2111

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-25 00:57:48 -04:00
commit e59452ea8c

View file

@ -256,6 +256,19 @@ v2/main/
│ # caller's teardown reaches it on every throw path —
│ # the readiness timeout included, which is what used
│ # to orphan a live server holding its port — #2000;
│ # ensure-test-servers.mjs is the ONE place
│ # `test-servers/build` is produced for a script
│ # consumer, and it builds UNCONDITIONALLY (once per
│ # process per repo root): the five hand-rolled copies
│ # it replaced each returned early when the output was
│ # already on disk, so after the first run an edit to
│ # `test-servers/src` was never picked up and the smoke
│ # silently drove the previous fixture — reported not as
│ # staleness but as a product failure in whatever the
│ # smoke was checking. Unconditional emit is still not a
│ # clean: a DELETED src file leaves its stale `.js`
│ # behind, so `rm -rf test-servers/build` after one —
│ # #2111;
│ # resolve-node-bin.mjs resolves a
│ # package's bin through its own package.json, so the
│ # verify/smoke scripts spawn it with `process.execPath`
@ -264,7 +277,9 @@ v2/main/
│ # the exception — its children are `npm` itself and the
│ # installed `.bin` shim, neither resolvable that way, so
│ # it keeps a Windows shell and quotes its generated
│ # paths via win-shell-args.mjs — #1939). Prettier-gated via
│ # paths via win-shell-args.mjs — #1939, though its
│ # test-server build now goes through the shared helper
│ # like everyone else's). Prettier-gated via
│ # `format:check:scripts`; its own pure parsers are
│ # unit-tested by `npm run test:scripts` (node --test).
├── specification/ # Build specification