Resolving the scratch graph downloads a registry packument for every name in
it. Measured with a cold npm cache: 149MB for a 413-package closure, and
853MB for a full one. Both the real install and `--package-lock-only` pay this
— it is the dominant cost of the step once the tarball bodies are gone, so it
is what is worth caching.
Keyed on the pnpm lockfile rather than the run id, so an entry is written once
per dependency change. This cache shares a 10GB repo-wide budget with the pnpm
store caches, and a per-run key would evict them.
No `--prefer-offline`: a restored entry is revalidated, so a third-party
release published since the entry was written still changes the resolution.
Conditional requests cost a fraction of the bodies.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scratch step installed ~2500 packages to read their package.json files.
`npm install --package-lock-only` reports the tree npm would install without
fetching a tarball, and the lockfile's `packages` keys are the paths npm
materializes — one key is one physical copy, the same ground truth the on-disk
walk reads.
`copiesFromLockfile` maps that to the shape `analyze()` already takes. Two
details the on-disk walk got for free:
- An alias (`"zod-v3": "npm:zod@^3"`) is a real second copy of zod. npm records
`name` on the entry when it differs from the directory, so the copy is keyed
on the manifest name.
- A lockfile lists every platform's build of an optional native dependency,
where an install unpacks only the matching one. Entries whose `os`/`cpu` rule
out this machine are dropped, or the report invents copies that never
coexist.
An unreadable or v1 lockfile throws, matching `collectCopies`: callers must
read that as "the check did not run", never as "no duplicates found".
Verified equivalent through the CLI on a 17-package closure (413 resolved
packages): the duplicate report is byte-identical to the real install's — 15
groups, same copy counts, same versions.
`collectCopies` stays; `verify-closure` still walks a built closure on disk.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The last master run took 4m26s: checkout 34s, toolchain 21s, `pnpm install`
35s, turbo build 2s, packing 51 packages 17s, the scratch `npm install` 2m20s,
analyze under 1s.
- Pack in one recursive `pnpm pack --json` run instead of one spawn per
package: 32.9s to 17.3s locally for the same 51 packages. `--json` also names
each tarball, so the destination directory is no longer diffed to guess it —
that mis-attributes a tarball whose name is already present and then reports
"pack produced nothing". Scripts are skipped; the one `prepack` among
publishable packages only rebuilds `dist`, which this check does not read.
- Fetch the checkout without history blobs, which are 866MiB of the 1.0GiB
pack, and check out `base-branch` through `ref` so that case fetches one
commit rather than all history.
- Diff with `--no-renames`, so a moved file counts against both the old and the
new package and rename detection reads no blobs.
- Quiet the scratch install, whose third-party deprecation notices ran to about
2000 lines and buried the report.
The workspace install has to stay: `pnpm pack` resolves `workspace:` specs from
installed `node_modules`, so narrowing it to the verifier's own closure — 5.7s
instead of 35s — fails every pack outside that closure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`release-create-pr.yml` passes the release line it prepares as `base-branch`,
and the v1 patch track resolves that to `1.x`, where
`packages/testing/code-health` does not exist. The job checked that ref out and
then failed on the missing directory, which reads as a dependency finding
rather than as "this ref predates the check". The full-scope job landed on
2026-08-17, so that path has not run yet.
A new step reports whether the package is present, and the steps below are
gated on it.
The skip applies only when the caller passed `base-branch`. Without one the ref
is the caller's own revision, where the package must exist, so a move or rename
still fails the job — the rest of this file is built around the rule that the
job must not go green after verifying nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>