Allow directory entries in worktree.symlinks via trailing-slash opt-in#947
Merged
Conversation
amrmelsayed
added a commit
that referenced
this pull request
May 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PIR Review: Allow directory entries in
worktree.symlinksvia trailing-slash opt-inFixes #805
Summary
worktree.symlinkspreviously accepted file entries only — directory entries weresilently dropped because the spawn-time symlink loop globbed with
nodir: true. Thischange adds a trailing-slash opt-in: an entry like
".local-user-data/"is treatedas a literal path and symlinked into each new worktree whole, so builders can share a
gitignored runtime-state directory with the parent checkout instead of re-bootstrapping
it. Entries without a trailing slash keep their exact prior behaviour, preserving the
nodir: trueguard that stops a glob from masking the worktree's own source.Preview
Files Changed
(vs merge-base
b4904bf9, code + docs)packages/codev/src/agent-farm/commands/spawn-worktree.ts(+58 / -14) — branch thesymlinks loop on trailing slash; add
pathOccupied()helper;statSync/lstatSyncimportspackages/codev/src/agent-farm/types.ts(+16 / -) —symlinksfield JSDoc documents the opt-inpackages/codev/src/agent-farm/__tests__/spawn-worktree.test.ts(+120 / -) — 6 new cases +node:fsmock additionsCLAUDE.md(+1 / -1),AGENTS.md(+1 / -1) — one-sentence note on the trailing-slash opt-incodev/plans/805-…md,codev/state/pir-805_thread.md— plan + cohort thread (ship with the PR)Commits
43db7bee[PIR Allow directory entries in worktree.symlinks via trailing-slash opt-in #805] Symlink directory entries via trailing-slash opt-inbe62b59b[PIR Allow directory entries in worktree.symlinks via trailing-slash opt-in #805] Test directory-symlink opt-in (dir type, dangling, idempotency, footgun)23662e4f[PIR Allow directory entries in worktree.symlinks via trailing-slash opt-in #805] Document trailing-slash directory opt-in in worktree configd91edb11draft,6489bac1revised — dangling-symlink-safe idempotency)Test Results
pnpm build: ✓ passpnpm test(porchtestscheck, full suite): ✓ pass (20.5s)spawn-worktree.test.ts: ✓ 77 passed (6 new)dev-approvalgate.New test cases:
'dir'typeEEXIST)Architecture Updates
No
arch.mdchanges needed. This change extends the behaviour of one existing helper(
symlinkConfigFiles) within the already-documented runnable-worktree setup flow; itintroduces no new module, boundary, or pattern. The runnable-worktree config surface
(
worktree.symlinks/postSpawn/devCommand) is already described in CLAUDE.md /AGENTS.md, which this PR keeps in sync.
Lessons Learned Updates
No
lessons-learned.mdentry added — the one reusable gotcha here is narrow enough tolive in this review rather than the curated lessons file (which MAINTAIN keeps lean):
Things to Look At During PR Review
acceptance criterion requires a dangling link when the source is absent, and a glob
only ever matches existing paths. Trade-off: glob wildcards in a directory entry are
not expanded —
packages/*/data/would be a literal*. Mitigated by a warn-and-skipwhen a trailing-slash entry contains glob metacharacters (
/[*?[\]{}!()]/).pathOccupied()/lstatSyncguard — the dangling-link idempotency fix (seeLessons). Test case 4 pins it.
symlinkSync(src, dest, isDir ? 'dir' : undefined).POSIX ignores the type; on Windows it's set only when the source exists at spawn.
Dangling Windows dir-symlinks are best-effort, matching the existing POSIX-leaning code.
filtered by
nodir: true(test case 5).How to Test Locally
pir-805→ View Diffafx dev pir-805(or VSCode → Run Dev Server)".local-user-data/"to a test repo's.codev/config.jsonworktree.symlinks,spawn a builder, confirm
<worktree>/.local-user-data → <workspaceRoot>/.local-user-data