Repository navigation
Description
Found by a defect-hotspot pass over the 90 days to 2026-10-26. This overlaps the freshness-decider task.
Scope change on 2026-10-29. The children half, property (a) as first written, moved to #228: the lockfile records a directory package's specifiers, so the checkers compare them with the manifest instead of deriving children, peers and spec policy. This task keeps the manifest reader, now without children and peers, and the copy contents.
Property
- (a) Manifest. The resolver, the frozen-lockfile check, the repeat-install check, deploy and every copy writer read a directory package's manifest from one function. That manifest can be a publish directory, an in-memory
dependencyManifest, or one rewritten by areadPackagehook. Which children and peers it yields is the resolver's business alone (Record a directory package's specifiers in the lockfile and make the frozen check an equality #228). - (b) Contents. Every writer of an injected copy produces the files and links a clean install does. The writers are the install, the sync after scripts, the edit watcher and the dedicated-lockfile sync.
Fixes in the loop
Seed commits: pnpm/pnpm#15356, pnpm/pnpm#15702, pnpm/pnpm#15881.
- Manifest: fix(package-manager): reuse the lockfile when injected projects come from memory pnpm#16815, fix(package-manager): check an injected publish directory against its project's manifest pnpm#17042, fix: run the workspace root's pnpmfile for projects with their own lockfiles pnpm#17047, fix(install): unify directory manifest lookup and local spec agreement pnpm#17133, fix: read a file: dependency's manifest with the resolver's hooks in lockfile checks pnpm#17431. The children fixes are listed in Record a directory package's specifiers in the lockfile and make the frozen check an equality #228.
- Contents: fix(install): sync only the package files into injected copies with dedicated lockfiles pnpm#16684, fix(injected-deps-syncer): diff injected copies without parsing their manifests pnpm#16749, fix: sync only the package files into injected copies after scripts pnpm#17033, fix: sync injected copies after listed scripts without a shared lockfile or with a publish directory pnpm#17083, fix: keep a package's links to its own files in synced injected copies pnpm#17088.
Issues: pnpm/pnpm#17094, pnpm/pnpm#17057, pnpm/pnpm#17284, pnpm/pnpm#17280, pnpm/pnpm#16683, pnpm/pnpm#17447, pnpm/pnpm#17473.
Design flaw
- One decision, several deciders. Which manifest describes a directory, and with which hooks, is decided in
lockfile_freshness/directory_deps/injected_manifest.rsandlockfile_freshness/directory_manifests.rs, inoptimistic_repeat_install/manifest_agreement/transforms.rs, inresolving-local-resolverandresolving-npm-resolver, and inreadDirectoryManifestin v11. fix: read a file: dependency's manifest with the resolver's hooks in lockfile checks pnpm#17431 made the hook chain one function; the lookup is still copied. - The
publishConfig.directoryrule is copied into about 9 places across both stacks. Examples:package-manifest/src/project.rs,injected-deps-syncer/src/lib.rs, v11injected-deps-syncer,installEachProject.ts,runLifecycleHooksConcurrently.ts,extendProjectsWithTargetDirs.ts,getWorkspacePackagesByDirectory.ts. - The copy's file set is decided per caller. Each caller passes
include_only_package_filesitself, and fix(install): sync only the package files into injected copies with dedicated lockfiles pnpm#16684 and fix: sync only the package files into injected copies after scripts pnpm#17033 were callers getting it wrong. The importer andDirPatchereach apply their own symlink rule. - State with no owner. The
.modules.yamlinjectedDepsrecords sit at the root or in each project, depending on the lockfile layout (fix: sync injected copies after listed scripts without a shared lockfile or with a publish directory pnpm#17083). - Order-dependent setup. With dedicated lockfiles, the checker re-applies the pnpmfiles to see hooked manifests (fix: run the workspace root's pnpmfile for projects with their own lockfiles pnpm#17047).
Proposed change (about 1 week per CLI)
- A resolver-owned
local_directory_manifest(dir, ctx)returning the hooked manifest. It takes the hooks, publish directory anddependencyManifestinto account. The resolver and every checker call it. In v11,lockfile.verificationimports it fromdeps-resolver. Children, peers and spec policy are not part of it: Record a directory package's specifiers in the lockfile and make the frozen check an equality #228 records them. - One function per CLI that decides an injected copy's file set: source directory, packlist mode and symlink rule. The fetcher, restorer, syncer and watcher call it, and callers stop passing the flag.
- One helper that locates the
injectedDepsrecords for every lockfile layout.
Tests that would have caught the loop
injected_freshness_matrix.rs and injectedFreshnessMatrix.ts are oracle-based, but their edit list contains only cases that had already broken. The matrix axes for hook source and directory location are part of #228.
directory_manifest_reader_agrees_across_callers: for each manifest source (disk, publish directory, in-memorydependencyManifest,readPackagehook,packageExtensions), the resolver, the frozen check and the repeat-install check read the same manifest.injected_copy_writers_agree: vary lockfile layout × publish directory ×deployAllFiles× a symlinked tree. Every writer must match a clean install.dedicated_lockfile_check_sees_root_hooks.
Fixes the change makes redundant
- Manifest: fix(package-manager): check an injected publish directory against its project's manifest pnpm#17042, the checker half of fix(package-manager): reuse the lockfile when injected projects come from memory pnpm#16815, the re-apply half of fix: run the workspace root's pnpmfile for projects with their own lockfiles pnpm#17047, the manifest part of fix(install): unify directory manifest lookup and local spec agreement pnpm#17133, and the hooking inside the check from fix: read a file: dependency's manifest with the resolver's hooks in lockfile checks pnpm#17431.
- Copy contents: fix(install): sync only the package files into injected copies with dedicated lockfiles pnpm#16684, the per-call-site flag in fix: sync only the package files into injected copies after scripts pnpm#17033, fix: sync injected copies after listed scripts without a shared lockfile or with a publish directory pnpm#17083, and the sync half of fix: keep a package's links to its own files in synced injected copies pnpm#17088.
Written by an agent (Claude Code, claude-opus-5-5). Scope narrowed on 2026-10-29 by an agent (Claude Code, claude-fable-5-1).
Activity
zkochan commentedon Oct 27, 2026
pnpm/pnpm#17408 covers item 2 of the proposed change for pnpm v12.
pnpm_directory_fetcher::CopyFilesnow decides an injected copy's file set (packlist or every file, how symlinks are listed, no path escape). The install, the custom fetcher's directory callback, the sync after scripts, the edit watcher, the dedicated-lockfile sync andpnpm deployall read their source through it. Behavior does not change.This is partial. Still open:
DirPatcher;includeOnlyPackageFileson published packages, so it is held back. The PR explains why.Written by an agent (Claude Code, claude-opus-5-5).
refactor: decide an injected copy's file set in one place (#17408)
zkochan commentedon Oct 27, 2026
pnpm/pnpm#17431 takes the manifest part of item 1, for pnpm v12. The lockfile checks now read every directory package that no workspace project describes through the resolver's hook chain (
ManifestTransformHooks::apply). That coversfile:dependencies outside the workspace and anything nested below them. It fixes two v12 bugs that 11.28.9 does not have:readPackagehook that rewrote such a package's dependencies made--frozen-lockfilefail;ignoredOptionalDependencieson such a package made every repeat install resolve again.Status of the rest:
ctx.dir(no repro yet); the optimistic repeat-install check has no hooks; and the peer, catalog-condition and spec decisions are unchanged.Written by an agent (Claude Code, claude-opus-5-5).
fix: read a file: dependency's manifest with the resolver's hooks in …
[-]Compute an injected package's children and copy contents in one place[/-][+]Compute an injected package's copy contents in one place, and read its manifest through one function[/+]zkochan commentedon Nov 8, 2026
Status check against main (2026-11-08).
Item 2 is done for v12 (pnpm/pnpm#17408), item 3 is done (pnpm/pnpm#17083), and the shared symlink rule landed in pnpm/pnpm#17415.
Left: item 1, a shared
local_directory_manifestreader, and the v11 half.Written by an agent (Claude Code, claude-opus-5-5).