Summary
bin/fm-spawn.sh wires a claude crewmate's per-task busy-state hooks by writing
<worktree>/.claude/settings.local.json inside the project worktree, then adding that path to the
worktree's .git/info/exclude. Two consequences follow:
- Firstmate writes into a project checkout, which
AGENTS.md hard rule 1 otherwise forbids.
- Every spawn leaves a residue line in the clone's
.git/info/exclude. Nothing retires those lines, and
cleaning them would itself require firstmate to write into the clone.
On a project that tracks .claude/settings.local.json the write is worse than untidy: the worktree ends
up dirty, so teardown needs --force on work that is otherwise perfectly landed, which collides with the
"never tear down unlanded work" directive because the operator learns to reach for --force routinely.
At origin/main (dc0172c) the behaviour is still present - bin/fm-spawn.sh around lines 2284, 2358 and
2361 writes the file and calls exclude_path '.claude/settings.local.json'.
Suggested direction
The harness already accepts a settings file by path, so the hooks never need to live in the project tree:
pass --settings <firstmate-home>/state/<id>.claude-settings.json at launch and have teardown remove that
state file. The worktree then stays clean, no exclude residue accumulates, and teardown needs no --force.
Care is needed in three places we hit while doing this locally:
- The same wiring path covers other harnesses (codex writes project hooks too), so a gate that only
considers claude leaves the sibling case behind.
- Relaunch must settle whether out-of-tree wiring is feasible before retiring the prior wiring,
otherwise a failed relaunch leaves a task with neither.
- A spawn abort should release its worktree-pool lease, and must not force-discard a dirty worktree while
doing so.
Patch available
We carry a working fix locally (13 files, +921/-35, including a live end-to-end hooks test and teardown
coverage). We have no push access to this repository, so we cannot open a pull request. Happy to attach the
patch or the individual commits here if that is useful to you - say the word and we will paste them.
We are also carrying it as our only local deviation from upstream, and would rather drop it the moment an
upstream fix lands.
Summary
bin/fm-spawn.shwires a claude crewmate's per-task busy-state hooks by writing<worktree>/.claude/settings.local.jsoninside the project worktree, then adding that path to theworktree's
.git/info/exclude. Two consequences follow:AGENTS.mdhard rule 1 otherwise forbids..git/info/exclude. Nothing retires those lines, andcleaning them would itself require firstmate to write into the clone.
On a project that tracks
.claude/settings.local.jsonthe write is worse than untidy: the worktree endsup dirty, so teardown needs
--forceon work that is otherwise perfectly landed, which collides with the"never tear down unlanded work" directive because the operator learns to reach for
--forceroutinely.At
origin/main(dc0172c) the behaviour is still present -bin/fm-spawn.sharound lines 2284, 2358 and2361 writes the file and calls
exclude_path '.claude/settings.local.json'.Suggested direction
The harness already accepts a settings file by path, so the hooks never need to live in the project tree:
pass
--settings <firstmate-home>/state/<id>.claude-settings.jsonat launch and have teardown remove thatstate file. The worktree then stays clean, no exclude residue accumulates, and teardown needs no
--force.Care is needed in three places we hit while doing this locally:
considers claude leaves the sibling case behind.
otherwise a failed relaunch leaves a task with neither.
doing so.
Patch available
We carry a working fix locally (13 files, +921/-35, including a live end-to-end hooks test and teardown
coverage). We have no push access to this repository, so we cannot open a pull request. Happy to attach the
patch or the individual commits here if that is useful to you - say the word and we will paste them.
We are also carrying it as our only local deviation from upstream, and would rather drop it the moment an
upstream fix lands.