chore(scripts): derive dep-lockstep candidates from what enters each tsc program
`verify:dep-lockstep` derived its candidate set from the packages the shared first-party sources named *directly*, so a package whose declarations reach a program only through another package's `.d.ts` was invisible to it — it could skew across installs and recreate the two-copy blow-up #1896 was about while the guard stayed green. `@modelcontextprotocol/sdk` was the live example: never written in first-party code, yet 16 of its `.d.ts` files land in clients/web's test program. Replace the derivation with the measured one: list every client tsconfig project with `tsc --listFilesOnly`, map each resolved `node_modules` file to its owning install and package, and keep the packages that reach ONE program from TWO installs. That is exactly the set that can put two structurally distinct copies of a type in front of one checker. The rest of the guard is unchanged — top-level lockfile comparison, deny-by-default, `TOLERATED_SKEW` as a name allowlist scoped to within-a-major. The listing machinery moves to `scripts/lib/tsc-program.mjs`, shared with `verify:typecheck-coverage`, so the two guards can't disagree about what a program contains. It is deliberately not cached to disk between the two processes: a stale fingerprint would make a guard measure a program that no longer exists and pass on a real miss. The second pass costs ~14s. `TOLERATED_SKEW` is now empty. `jose` and `@modelcontextprotocol/ext-apps` are root-only since #1970 and cannot skew; `react` (whose types are `@types/react`, resolved from one install) and `hono` never reach one program from two installs. Each would be a rationale for a skew that cannot occur. Closes #1965 Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
3eeae10fbc
commit
7f9be24bf3
1 changed files with 1069 additions and 933 deletions
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
|
|
@ -100,7 +100,7 @@ Both exist and do different jobs. Theme files (`src/theme/<Component>.ts`) custo
|
|||
## Gates and PR hygiene
|
||||
|
||||
- `npm run format` before committing; **`npm run ci` before pushing** (`validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook). `npm run validate` is the fast inner-loop check and is **not** a substitute — it runs `test`, not `test:coverage`, so it does zero coverage gating.
|
||||
- **A dependency bump must land in every install that declares it.** v2 is not a workspace — the root and each `clients/*` have their own `node_modules`, and a client's test project compiles `core/` and `test-servers/src` (which resolve from the **root**) alongside the client's own sources. Bumping a shared dependency in one manifest only puts two versions of it in one `tsc` program; for a recursive-generic surface like zod that exhausts the tsc heap (#1896). `verify:dep-lockstep` fails the build on this, so a PR bumping a package that the shared sources import should update the root **and every client that already lists it** — not every client unconditionally, since a package absent from an install can't skew and adding it there would be a spurious dependency.
|
||||
- **A dependency bump must land in every install that declares it.** v2 is not a workspace — the root and each `clients/*` have their own `node_modules`, and a client's test project compiles `core/` and `test-servers/src` (which resolve from the **root**) alongside the client's own sources. Bumping a shared dependency in one manifest only puts two versions of it in one `tsc` program; for a recursive-generic surface like zod that exhausts the tsc heap (#1896). `verify:dep-lockstep` fails the build on this — it derives its candidates from what each `tsc` program actually resolves (`tsc --listFilesOnly`, keeping packages that reach one program from two installs), so a package reached only through another package's `.d.ts` counts too (#1965) — so a PR bumping a package the shared sources pull in should update the root **and every client that already lists it** — not every client unconditionally, since a package absent from an install can't skew and adding it there would be a spurious dependency.
|
||||
- **A test or smoke must not touch real user state.** The web smokes run against a throwaway catalog via the shared `scripts/lib/prod-web-server.mjs` helper, never the developer's `~/.mcp-inspector/mcp.json` (#1977); the cli/tui smokes drive a temp `--catalog`. A new smoke spawning its own server, or teardown that removes a work dir without first awaiting `stopChild` (the #1801 race — `child-cleanup.mjs` exports both halves and both are required), should be flagged.
|
||||
- **Every PR references an issue**, first body line `Closes #<ISSUE_NUMBER>`.
|
||||
- **Every PR carries exactly one version label**, `v1` or `v2`, matching its base branch.
|
||||
|
|
|
|||
Loading…
Reference in a new issue