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:
parent
1e6bf6425a
commit
8e2c4d49fa
1 changed files with 110 additions and 50 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue