Builder worktree write-guard: block writes anchored at the main checkout (#1018)#1098
Merged
Conversation
… Codex REQUEST_CHANGES)
amrmelsayed
added a commit
that referenced
this pull request
Jun 24, 2026
Substantive ## section in UNRELEASED.md covering the guard mechanism, the 4-branch spawn coverage, the dev-approval verification matrix, the allowlist + realpath canonicalisation, and the explicit scope decision keeping #1092 (consult read surface) out of this PR. Vscode CHANGELOG.md skipped: this is agent-farm / spawn-side infrastructure, not a VS Code extension feature.
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: Builder worktree write-guard
Fixes #1018
Summary
Strict-mode builders run in a git worktree nested inside the main checkout; the runtime sometimes synthesizes an absolute
Write/Editpath anchored at the inferred repo root, dropping the.builders/<id>/segment so the write silently lands in the main checkout. This PR installs a deterministic Claude PreToolUse hook into every builder worktree at spawn time that deniesWrite/Editto paths resolving outside the worktree root (allowlisting temp dirs and~/.claude), turning a silent main-checkout pollution into a loud, correctable deny. A role-doc backstop documents the failure mode and the relative-path discipline that covers the Bash write surface.Files Changed
packages/codev/src/agent-farm/utils/worktree-write-guard.ts(+233 / -0) — new; single source of truth (guard script string +buildWorktreeGuardFiles())packages/codev/src/__tests__/worktree-write-guard.test.ts(+214 / -0) — new; 14 cases exercising the emitted script + the file builderpackages/codev/src/agent-farm/utils/harness.ts(+12 / -2) —CLAUDE_HARNESS.getWorktreeFilesemits the guard; signature gainsworktreePathpackages/codev/src/agent-farm/commands/spawn-worktree.ts(+9 / -4) —writeWorktreeFilesmkdir -ps the target dir; both call sites passworktreePathpackages/codev/src/agent-farm/__tests__/harness.test.ts(+11 / -4) — contract update (CLAUDE_HARNESS now hasgetWorktreeFiles)codev/roles/builder.md(+21 / -0) — worktree path-discipline backstopcodev-skeleton/roles/builder.md(+21 / -0) — identical mirrorcodev/resources/arch.md(+9 / -0) — COLD: write-guard mechanism under Worktree Managementcodev/resources/lessons-learned.md(+3 / -0) — COLD: deterministic-guard / scope-the-hazard / mkdir-before-write lessonsCommits
93f455de[PIR Builder worktree write-guard: prevent writes anchored at the main checkout root #1018] Add worktree write-guard hook for Claude buildersd5ec7156[PIR Builder worktree write-guard: prevent writes anchored at the main checkout root #1018] Test the write-guard script and CLAUDE_HARNESS wiring84b2f7b1[PIR Builder worktree write-guard: prevent writes anchored at the main checkout root #1018] Document worktree path-discipline backstop in builder role10aa6095[PIR Builder worktree write-guard: prevent writes anchored at the main checkout root #1018] Update builder thread for implement phaseTest Results
npm run build: ✓ pass (porch check, 6.8s)npm test: ✓ pass (porch check, 20.2s; 3358 passed, 0 failed, 48 skipped) — 14 new tests inworktree-write-guard.test.tsdev-approvalgate): the running worktree was reviewed and approved.Note on running the suite: a full
npm run buildmust precedenpm test. The terminalsession-managerintegration tests spawn and connect to the built shellper binary; without a prior full build they fail to connect. This is environmental and unrelated to this change — confirmed by the suite passing cleanly afternpm run build.Architecture Updates
COLD — added a "Worktree Write-Guard (Issue #1018)" subsection to
codev/resources/arch.mdunder Worktree Management: the failure mode, the hook mechanism, where it's wired (CLAUDE_HARNESS.getWorktreeFiles→writeWorktreeFiles), the allowlist, fail-open behavior, baked-inCODEV_WORKTREE_ROOTwithgit rev-parsefallback, and scope (write surface / Claude only / architect unaffected / #1092 separate).HOT — no change. The hot
arch-critical.mdis capped and full; this fact is reference detail that belongs in the cold archive, and the existing "Worktrees in.builders/are Agent-Farm-managed" hot fact already covers the always-on invariant.Lessons Learned Updates
COLD — added three entries to
codev/resources/lessons-learned.md(Architecture):main; reads stay unguarded to preserve cross-checkout reads).writeFileSyncdoesn't create parent dirs and git only materializes dirs with tracked files —mkdir -pbefore writing a generated file into a worktree subdir.HOT — no change.
lessons-critical.mdis capped and full; the closest existing hot lesson ("When guessing fails, build a minimal repro") is adjacent but distinct, and these are spec-narrow recipes better kept cold.Things to Look At During PR Review
worktree-write-guard.ts): temp dirs +$HOME/.claude. The~/.claudeentry is deliberate so builder memory writes are not blocked. A consequence worth a conscious nod: symlinked shared root config (.codev/config.json,.env) resolves outside the worktree and is denied for writes — judged correct (a builder should not mutate shared root config), but it is a behavior boundary./tmp→/private/tmpis normalized. Covered by tests, but the trickiest part to get right.CODEV_WORKTREE_ROOTis baked at spawn (absolute);git rev-parse --show-toplevelis the runtime fallback. Both are canonicalized.How to Test Locally
pir-1018→ Review Diffafx dev pir-1018Writeto a main-checkout path (e.g.<repo>/codev/plans/zzz.md) is blocked with a re-root message./tmp(or scratchpad) write passes.~/.claude/...memory write passes.