chore: address Copilot review round 1 (#2226)

- Include `optionalDependencies` in the declared tier's candidate union. A
  peer range constrains the consumer's host; an optional range is a direct
  declaration npm attempts to install, so omitting it left an optional-only
  package free to skew unseen. `peerDependencies` stays excluded, with the
  reason stated.
- Make the end-to-end fixture's client copy UNDECLARED — only the root
  manifest names `solo`, while `clients/web` merely holds a top-level copy.
  That is #2226's defining shape, and it is what makes the candidate set a
  union across installs rather than a per-install intersection; the old
  fixture would have passed a per-install implementation. Adds the mirror
  case (declared only by a client, held by the root) so the union is pinned
  in both directions.
- Update the two docs that still described the single-tier guard:
  `.claude/skills/local-dev/SKILL.md` said "nothing gates either of those,
  and `verify:dep-lockstep` is not it" and cited the live `@types/node`
  drift in the present tense; `docs/quality-gate.md` described only the
  program tier. Both now state what each tier covers and the two limits
  that remain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J86a6vMcA9csPtdsg7ZcCZ
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-05 16:09:44 -04:00
commit 8e2c4d49fa
3 changed files with 110 additions and 50 deletions

View file

@ -528,16 +528,20 @@ test("main: exits 1 when the root validate no longer runs the sibling guard", ()
// ---------------------------------------------------------------------------
test("main: a DECLARED package skewed across installs fails even when no program holds both copies (#2226)", () => {
// The exact shape the issue reported. `solo` is installed under the root and
// under `clients/web`, and both manifests name it — but only the shared
// `core/` imports it, so the client's program resolves the root's copy alone
// and the program tier is silent, correctly. The declared tier is what sees
// it, which is the whole reason it exists: cli's `@types/node` was hoisted in
// via `@types/express` and no program ever met the root's copy.
// The exact shape the issue reported, and the client copy is deliberately
// UNDECLARED (Copilot): only the ROOT manifest names `solo`, while
// `clients/web` merely holds a top-level copy — the way cli's `@types/node`
// arrives, hoisted via `@types/express` with no range of ours governing it.
// That is what makes the candidate set a UNION across installs rather than a
// per-install intersection, and declaring it in both manifests would let a
// per-install implementation pass this test.
//
// Only the shared `core/` imports `solo`, so the client's program resolves the
// root's copy alone and the program tier is silent — correctly. The declared
// tier is the one that sees it, which is the whole reason it exists.
withFixture(
{
rootManifestDeps: { solo: "^7.0.0" },
webManifestDeps: { solo: "^7.0.0" },
webSolo: "7.0.0",
},
(dir) => {
@ -553,10 +557,11 @@ test("main: a DECLARED package skewed across installs fails even when no program
});
test("main: a declared package held at the same version in both installs passes and is counted", () => {
// Same union shape as above — root-declared, client copy undeclared — so the
// pass and the fail differ only in the version, not in how the name is found.
withFixture(
{
rootManifestDeps: { solo: "^7.0.0" },
webManifestDeps: { solo: "^7.0.0" },
webSolo: "7.8.9",
},
(dir) => {
@ -567,6 +572,26 @@ test("main: a declared package held at the same version in both installs passes
);
});
test("main: the union runs the other way too — declared only by a CLIENT, held by the root", () => {
// The mirror of the case above, and the reason `declaredPackages` unions every
// install's manifest rather than reading the root's. `@types/react` is the
// live shape: web and tui declare it, the root does not, and the two clients
// disagreed. A root-manifest-only candidate set would report success here.
withFixture(
{
webManifestDeps: { solo: "^7.0.0" },
webSolo: "7.0.0",
},
(dir) => {
const { status, out } = runGuard(dir);
assert.equal(status, 1, out);
assert.match(out, /1 declared dependency resolves/);
assert.match(out, /7\.8\.9\s+\(\.\/node_modules\/solo\)/);
assert.match(out, /7\.0\.0\s+\(clients\/web\/node_modules\/solo\)/);
},
);
});
test("main: an installed package NO manifest declares is not a declared-tier candidate", () => {
// `outer` and `inner` sit in both installs in every fixture, but no manifest
// names them. The declared tier's boundary is what the repo declares, so its

View file

@ -278,18 +278,33 @@ export function toleratesSkew(name, holders, tolerated) {
* Every package name any install declares, across `dependencies` and
* `devDependencies`.
*
* `manifests` is `[{ dir, manifest }]`. Both fields count because the boundary
* this tier polices is "does the repo name it", not "does it ship": a
* devDependency skew is exactly the `@types/*` and toolchain case #2226 is
* about. `peerDependencies` and `optionalDependencies` are NOT declarations of
* what this repo installs — a peer range is a constraint on the consumer, and
* the copy npm auto-installs to satisfy one is caught anyway, because it lands
* top-level in an install whose sibling declares the same name.
* `manifests` is `[{ dir, manifest }]`. All three fields count because the
* boundary this tier polices is "does the repo name a package npm will install
* for us", not "does it ship":
*
* • `devDependencies` — the `@types/*` and toolchain case #2226 is about; every
* one of the four skews this tier first surfaced was a devDependency
* somewhere.
* • `optionalDependencies` — a direct declaration npm attempts to install like
* any other. Omitting it would leave an optional-only package free to hold
* conflicting top-level copies in two installs while this tier reported
* success (Copilot, #2226).
*
* `peerDependencies` is the one exclusion, and it is not an oversight: a peer
* range is a constraint the consumer places on its *host*, not a statement that
* this repo installs the package. The copy npm auto-installs to satisfy an unmet
* peer is caught anyway whenever some manifest declares that name — which is the
* `eslint`/`typescript`/`vitest` shadow case, since all three are root
* devDependencies.
*/
export function declaredPackages(manifests) {
const names = new Set();
for (const { manifest } of manifests)
for (const field of ["dependencies", "devDependencies"])
for (const field of [
"dependencies",
"devDependencies",
"optionalDependencies",
])
for (const name of Object.keys(manifest?.[field] ?? {})) names.add(name);
return names;
}

View file

@ -413,10 +413,12 @@ test("majorOf: prerelease and build metadata are irrelevant", () => {
// The declared tier (#2226). One case per rule, same convention as above.
// ---------------------------------------------------------------------------
test("declaredPackages: dependencies and devDependencies from every install", () => {
// devDependencies count because the boundary is "does the repo name it", not
// "does it ship" — `@types/node` and the toolchain are exactly the case #2226
// is about, and all of them are devDependencies.
test("declaredPackages: dependencies, devDependencies and optionalDependencies from every install", () => {
// devDependencies count because the boundary is "does the repo name a package
// npm installs for us", not "does it ship" — `@types/node` and the toolchain
// are exactly the case #2226 is about, and all of them are devDependencies.
// optionalDependencies count for the same reason: npm attempts to install one
// like any other direct declaration (Copilot, #2226).
assert.deepEqual(
[
...declaredPackages([
@ -426,28 +428,24 @@ test("declaredPackages: dependencies and devDependencies from every install", ()
manifest: {
dependencies: { "react-dom": "^19.2.4" },
devDependencies: { "@types/react": "^19.2.14" },
optionalDependencies: { fsevents: "^2.3.3" },
},
},
]),
].sort(),
["@types/react", "react", "react-dom"],
["@types/react", "fsevents", "react", "react-dom"],
);
});
test("declaredPackages: peer and optional ranges are not declarations", () => {
// A peer range constrains the consumer rather than naming what we install;
// the copy npm auto-installs to satisfy one is still caught, because it lands
// top-level in an install whose sibling declares the same name.
test("declaredPackages: a peer range is not a declaration", () => {
// A peer range constrains the consumer's HOST rather than naming what this
// repo installs; the copy npm auto-installs to satisfy an unmet one is still
// caught whenever some manifest declares that name, which is the
// eslint/typescript/vitest shadow case (all three are root devDependencies).
assert.deepEqual(
[
...declaredPackages([
{
dir: ".",
manifest: {
peerDependencies: { react: "^19.0.0" },
optionalDependencies: { fsevents: "^2.3.3" },
},
},
{ dir: ".", manifest: { peerDependencies: { react: "^19.0.0" } } },
]),
],
[],