Observed twice while verifying the SDK 0.2.150 upgrade, with opposite outcomes on identical setups — so this is reported as nondeterministic and needing confirmation, not as a settled diagnosis.
What happened
A harness ran AgentWrapper.run_work_session(..., parallel=True) against a scratch project, with working_dir (and therefore ClaudeAgentOptions.cwd) set to /tmp/.../hive-verify. The parent Python process's own cwd was the claude-task-master checkout.
The scratch project contained src/alpha.py, src/beta.py, src/gamma.py; the task was to implement run() in each.
- Run 1 — correct. A worker explicitly noted it resolved
src/alpha.py to the absolute scratch path "since that is the actual working directory for this task", and the lead committed in the scratch repo.
- Run 2 — wrong. The workers wrote
src/alpha.py, src/beta.py and src/gamma.py into the claude-task-master checkout, and the lead then ran cd "$(git rev-parse --show-toplevel)" && git add … && git commit, producing a real commit (feat: add run() to alpha/beta/gamma modules) on the checked-out feature branch of the wrong repository. The scratch project was left untouched, still holding the raise NotImplementedError stubs.
Both runs used the same harness, same options, same launch directory. The only difference was a git reset --hard on the scratch repo between them.
Why it matters
CLAUDE.md is explicit that the working directory is passed to the SDK as cwd= and never chdir'd, precisely so concurrent queries and concurrent subagents are safe. If a session's tools can resolve relative paths — and git rev-parse --show-toplevel — against the parent process's cwd instead, then:
- a run can write into, and commit to, a repository that is not its target;
- the blast radius is whatever directory
claudetm-server (or any embedding process) happens to be running in.
In normal CLI use claudetm start runs from inside the project, so process cwd and cwd= coincide and this cannot bite. It matters for server mode, for the REST /repo/* flows, and for anything embedding AgentWrapper with a working_dir other than its own cwd.
Not established
- Whether the divergence is in the lead, the workers, or both (in run 2 the workers wrote to the wrong tree and the lead committed there).
- Whether
forward_subagent_text or the concurrency env var has any bearing (neither plausibly should).
- Whether the SDK propagates
cwd to subagent processes at all, or only to the parent — this is undocumented as far as the subagent docs go.
Suggested next step
A small deterministic probe: query() with cwd=<dir A> while the process sits in <dir B>, prompt = "dispatch two subagents; each runs pwd and git rev-parse --show-toplevel and reports". Repeat enough times to see whether the answer is stable. If subagents do not inherit cwd, the fan-out brief needs to state the absolute working directory explicitly, and the "the lead alone runs git" rule needs an absolute -C <dir>.
Observed twice while verifying the SDK 0.2.150 upgrade, with opposite outcomes on identical setups — so this is reported as nondeterministic and needing confirmation, not as a settled diagnosis.
What happened
A harness ran
AgentWrapper.run_work_session(..., parallel=True)against a scratch project, withworking_dir(and thereforeClaudeAgentOptions.cwd) set to/tmp/.../hive-verify. The parent Python process's own cwd was the claude-task-master checkout.The scratch project contained
src/alpha.py,src/beta.py,src/gamma.py; the task was to implementrun()in each.src/alpha.pyto the absolute scratch path "since that is the actual working directory for this task", and the lead committed in the scratch repo.src/alpha.py,src/beta.pyandsrc/gamma.pyinto the claude-task-master checkout, and the lead then rancd "$(git rev-parse --show-toplevel)" && git add … && git commit, producing a real commit (feat: add run() to alpha/beta/gamma modules) on the checked-out feature branch of the wrong repository. The scratch project was left untouched, still holding theraise NotImplementedErrorstubs.Both runs used the same harness, same options, same launch directory. The only difference was a
git reset --hardon the scratch repo between them.Why it matters
CLAUDE.mdis explicit that the working directory is passed to the SDK ascwd=and neverchdir'd, precisely so concurrent queries and concurrent subagents are safe. If a session's tools can resolve relative paths — andgit rev-parse --show-toplevel— against the parent process's cwd instead, then:claudetm-server(or any embedding process) happens to be running in.In normal CLI use
claudetm startruns from inside the project, so process cwd andcwd=coincide and this cannot bite. It matters for server mode, for the REST/repo/*flows, and for anything embeddingAgentWrapperwith aworking_dirother than its own cwd.Not established
forward_subagent_textor the concurrency env var has any bearing (neither plausibly should).cwdto subagent processes at all, or only to the parent — this is undocumented as far as the subagent docs go.Suggested next step
A small deterministic probe:
query()withcwd=<dir A>while the process sits in<dir B>, prompt = "dispatch two subagents; each runspwdandgit rev-parse --show-topleveland reports". Repeat enough times to see whether the answer is stable. If subagents do not inheritcwd, the fan-out brief needs to state the absolute working directory explicitly, and the "the lead alone runs git" rule needs an absolute-C <dir>.