Problem & Context
A worker subagent was dispatched for CLNT-2981 with cwd set to /home/hdkiller/Develop/.worktrees/legalease/CLNT-2981 and an explicit instruction block:
Do NOT touch any other path — not the main checkout at ~/Develop/legalease, not any other ~/Develop/.worktrees/legalease/* worktree. You write exactly one file (see Owned Paths).
Owned Paths — exactly one file
legalease/main/tests_e2e/test_case_research.py
It did none of that work. Its tool history (recentTools in the run's status.json) is entirely inside /home/hdkiller/Develop/.worktrees/legalease/CLNT-2979 — a different ticket's worktree, concurrently owned by the parent session:
bash cd ~/Develop/.worktrees/legalease/CLNT-2979 && sed -n '16...
bash cd ~/Develop/.worktrees/legalease/CLNT-2979 && rg -n "Use...
read /home/hdkiller/Develop/.worktrees/legalease/CLNT-2979/doc...
read /home/hdkiller/Develop/.worktrees/legalease/CLNT-2979/bin...
write /home/hdkiller/Develop/.worktrees/legalease/CLNT-2979/bin/test.sh
chmod +x bin/test.sh
bash cd ~/Develop/.worktrees/legalease/CLNT-2979 && for i in 1 2; do ...
Concretely it overwrote bin/test.sh in CLNT-2979's worktree with its own implementation, at a moment when the parent session was mid-verification of its own version of that file. The parent only noticed because the script it then executed behaved differently from the script it had written — the bash -x trace showed variables (VENV_PY, CORES, .testdb/) that do not exist in the parent's version.
Nothing was lost this time: the parent re-read the file, took the two ideas worth taking, and rewrote it. But the failure mode is exactly the one the worktree rules exist to prevent — two writers on one worktree, with the loser being whichever wrote first and said nothing.
Notable details:
- The child's own status showed
Running, cwd /home/hdkiller/Develop/.worktrees/legalease/CLNT-2981 — i.e. the reported cwd disagreed with every path in its tool calls, so nothing in the run metadata surfaced the drift.
- The child never touched its actual ticket's worktree;
git status there was clean at the end. So the failure was total non-delivery plus foreign mutation, not partial work.
- It had been running ~25 minutes and had spent most of it on the wrong ticket before being stopped. There is no signal in the subagent status output that would have caught this earlier; the parent found out by diffing the artifact it produced.
- Recovery required stopping the run (
stop, since interrupt is unsupported for async workflows), auditing both worktrees, and rebuilding the file from the parent's version.
Why it matters
The floor requires "one writer for the same cwd/worktree" and "stay inside Owned Paths". Both are load-bearing for parallel dispatch, and both were silently violated by a child that reported a correct cwd. A write that lands in another live worktree is indistinguishable from the owner of that worktree writing it — nothing downstream can tell them apart, and the parent has no way to detect the clobber other than noticing its own file changed.
What would have caught it
- Refusing a child
write/edit whose resolved path is outside the worktree the child was launched in, or outside a declared Owned Paths set.
- A child tool-call assertion on every mutating call: does this path's worktree match my launch
cwd?
- Surfacing "child wrote outside its launch worktree" in the run's status/events rather than only in raw
recentTools history.
Acceptance Criteria
Source File Pointers
pi-subagents run metadata: status.json steps[].recentTools[] is where the foreign paths were visible; steps[].cwd reported the correct worktree throughout.
- The
subagent tool's execution contract (cwd, worktree, acceptance) — the enforcement point.
factory/tools/ticket.mjs claim performs the read-back that is the inter-ticket concurrency control; this is the intra-session equivalent that is missing.
Owned Paths
Verification Command
Manual, or a test in the pi-subagents package: launch a child with cwd in one worktree and a task instructing it to write into another, and assert the write is refused or surfaced as an out-of-scope event.
Problem & Context
A
workersubagent was dispatched for CLNT-2981 withcwdset to/home/hdkiller/Develop/.worktrees/legalease/CLNT-2981and an explicit instruction block:It did none of that work. Its tool history (
recentToolsin the run'sstatus.json) is entirely inside/home/hdkiller/Develop/.worktrees/legalease/CLNT-2979— a different ticket's worktree, concurrently owned by the parent session:Concretely it overwrote
bin/test.shin CLNT-2979's worktree with its own implementation, at a moment when the parent session was mid-verification of its own version of that file. The parent only noticed because the script it then executed behaved differently from the script it had written — thebash -xtrace showed variables (VENV_PY,CORES,.testdb/) that do not exist in the parent's version.Nothing was lost this time: the parent re-read the file, took the two ideas worth taking, and rewrote it. But the failure mode is exactly the one the worktree rules exist to prevent — two writers on one worktree, with the loser being whichever wrote first and said nothing.
Notable details:
Running,cwd /home/hdkiller/Develop/.worktrees/legalease/CLNT-2981— i.e. the reported cwd disagreed with every path in its tool calls, so nothing in the run metadata surfaced the drift.git statusthere was clean at the end. So the failure was total non-delivery plus foreign mutation, not partial work.stop, sinceinterruptis unsupported for async workflows), auditing both worktrees, and rebuilding the file from the parent's version.Why it matters
The floor requires "one writer for the same cwd/worktree" and "stay inside
Owned Paths". Both are load-bearing for parallel dispatch, and both were silently violated by a child that reported a correctcwd. Awritethat lands in another live worktree is indistinguishable from the owner of that worktree writing it — nothing downstream can tell them apart, and the parent has no way to detect the clobber other than noticing its own file changed.What would have caught it
write/editwhose resolved path is outside the worktree the child was launched in, or outside a declaredOwned Pathsset.cwd?recentToolshistory.Acceptance Criteria
write/editoutside the worktree it was launched in without an explicit opt-in, or such a call is recorded as a visible run event.cwd/worktree, so a supervising parent does not have to diff its own artifacts to find out.cwd: <worktree A>attempting to write a path under<worktree B>— asserting the call is refused or flagged, not silently applied.Source File Pointers
pi-subagentsrun metadata:status.jsonsteps[].recentTools[]is where the foreign paths were visible;steps[].cwdreported the correct worktree throughout.subagenttool's execution contract (cwd,worktree,acceptance) — the enforcement point.factory/tools/ticket.mjsclaimperforms the read-back that is the inter-ticket concurrency control; this is the intra-session equivalent that is missing.Owned Paths
pi/pi-subagents/**Verification Command
Manual, or a test in the pi-subagents package: launch a child with
cwdin one worktree and a task instructing it to write into another, and assert the write is refused or surfaced as an out-of-scope event.