Problem
A strict-mode builder works in an isolated git worktree at .builders/<id>/, which is its git root. The Write and Edit tools require absolute paths. The builder synthesized an absolute path anchored at the repo's canonical root (e.g. <repo>/codev/plans/...), which drops the .builders/<id>/ segment and points at the main checkout's working tree instead of the worktree.
Because the worktree is nested inside the main checkout, that wrong path is a real, writable directory, so the mis-write succeeds silently: the file lands in the main checkout. The error only surfaces later when git add from the worktree fails with a pathspec error (the worktree never received the file). The byte-identical trees at branch base also mean wrong-rooted reads succeed silently, masking the mistake until the first write.
Net effects: wasted builder turns, and (worse) silent pollution of the main checkout's working tree by a builder that is supposed to be isolated.
Regression framing (read this first)
This is not a codev code regression, and a git bisect for a culprit commit will not find it. Two pieces of evidence:
- It has occurred across multiple days, including before the most recent local install of the latest codev build, so it does not correlate with any recent codev change.
- The most recent change to the spawn-prompt machinery inlines worktree-relative paths (
codev/plans/<id>-<slug>.md, "open in the worktree"), so the prompt content pushes toward worktree-relative thinking, not main-root paths. It does not explain the bug.
The most likely root cause is intrinsic path-synthesis behavior of the current builder model / CLI runtime. Both Read and Write require absolute paths, so the builder must synthesize one. It synthesizes the canonical repo root it infers rather than its actual worktree cwd. Wrong-rooted reads then return identical content (byte-identical trees), giving the model no corrective signal, so it keeps reusing the bad prefix until a write to the worktree's git index exposes it.
Implication for the fix: this behavior will drift across model and CLI upgrades in both directions. Instructions, per-agent memory, and bisects do not hold against a moving runtime. Only a deterministic guard holds across versions. That is what this issue should deliver.
Proposed fix (deterministic guard + backstop)
- PreToolUse hook on Write/Edit (the real fix). Reject absolute paths that resolve outside the worktree root. Source the worktree root from
git rev-parse --show-toplevel (project-agnostic; returns the worktree, not the main checkout). Allowlist declared scratch dirs (/tmp, /private/tmp) so legitimate temp writes are not blocked. Deterministic and model-proof: it does not care whether the bad path came from a model quirk, a CLI change, or a future regression.
- Role-doc invariant (backstop). In
roles/builder.md, name the failure mode, not just the cwd: a byte-identical sibling main checkout exists, so wrong-rooted reads succeed silently and mask the error until a write fails; absolute paths for Write/Edit must be rooted at the worktree. Add: "Bash cwd is the worktree, prefer relative paths there" (a relative path cannot be anchored to the wrong root, which closes the Bash surface the Write/Edit hook does not cover).
Scope notes
- The Write/Edit hook covers the surface where this actually occurs (those tools require absolute paths). Bash-based writes (
> redirects, cp, tee, sed -i) are not statically guardable; relative-path discipline (cwd = worktree) is the complementary control there.
- Out of scope: full per-builder filesystem / namespace isolation (making the main checkout unreachable). It is the textbook "eliminate the hazard" answer, but it fights codev's current local, shared-filesystem model: git worktrees share the main repo's object DB and gitdir, and codev deliberately relies on cross-checkout reads (architect reads builder threads, sibling-thread reads) and symlinked root config. Isolation is the right direction for a future cloud / ephemeral-sandbox execution model, not for local builders today.
- No personal-memory fix. A per-agent memory patch would mask the systemic signal on one machine and leave every other user / machine in the trap. The fix belongs in the version-controlled shared surfaces.
Suggested protocol
PIR. The hook is shared builder infrastructure (high blast radius: every builder) and the approach benefits from a quick design review before coding (cross-platform path resolution, the temp-dir allowlist, the Bash-surface decision). The dev-approval gate also has real teeth here: spawn a throwaway builder, confirm a Write to the main checkout is blocked, and confirm a legitimate /tmp write still passes. The coding itself is small.
Problem
A strict-mode builder works in an isolated git worktree at
.builders/<id>/, which is its git root. TheWriteandEdittools require absolute paths. The builder synthesized an absolute path anchored at the repo's canonical root (e.g.<repo>/codev/plans/...), which drops the.builders/<id>/segment and points at the main checkout's working tree instead of the worktree.Because the worktree is nested inside the main checkout, that wrong path is a real, writable directory, so the mis-write succeeds silently: the file lands in the main checkout. The error only surfaces later when
git addfrom the worktree fails with a pathspec error (the worktree never received the file). The byte-identical trees at branch base also mean wrong-rooted reads succeed silently, masking the mistake until the first write.Net effects: wasted builder turns, and (worse) silent pollution of the main checkout's working tree by a builder that is supposed to be isolated.
Regression framing (read this first)
This is not a codev code regression, and a
git bisectfor a culprit commit will not find it. Two pieces of evidence:codev/plans/<id>-<slug>.md, "open in the worktree"), so the prompt content pushes toward worktree-relative thinking, not main-root paths. It does not explain the bug.The most likely root cause is intrinsic path-synthesis behavior of the current builder model / CLI runtime. Both
ReadandWriterequire absolute paths, so the builder must synthesize one. It synthesizes the canonical repo root it infers rather than its actual worktree cwd. Wrong-rooted reads then return identical content (byte-identical trees), giving the model no corrective signal, so it keeps reusing the bad prefix until a write to the worktree's git index exposes it.Implication for the fix: this behavior will drift across model and CLI upgrades in both directions. Instructions, per-agent memory, and bisects do not hold against a moving runtime. Only a deterministic guard holds across versions. That is what this issue should deliver.
Proposed fix (deterministic guard + backstop)
git rev-parse --show-toplevel(project-agnostic; returns the worktree, not the main checkout). Allowlist declared scratch dirs (/tmp,/private/tmp) so legitimate temp writes are not blocked. Deterministic and model-proof: it does not care whether the bad path came from a model quirk, a CLI change, or a future regression.roles/builder.md, name the failure mode, not just the cwd: a byte-identical sibling main checkout exists, so wrong-rooted reads succeed silently and mask the error until a write fails; absolute paths for Write/Edit must be rooted at the worktree. Add: "Bash cwd is the worktree, prefer relative paths there" (a relative path cannot be anchored to the wrong root, which closes the Bash surface the Write/Edit hook does not cover).Scope notes
>redirects,cp,tee,sed -i) are not statically guardable; relative-path discipline (cwd = worktree) is the complementary control there.Suggested protocol
PIR. The hook is shared builder infrastructure (high blast radius: every builder) and the approach benefits from a quick design review before coding (cross-platform path resolution, the temp-dir allowlist, the Bash-surface decision). The
dev-approvalgate also has real teeth here: spawn a throwaway builder, confirm a Write to the main checkout is blocked, and confirm a legitimate/tmpwrite still passes. The coding itself is small.