Skip to content

fm-spawn writes claude's per-task hooks into the project worktree, dirtying it and leaving permanent .git/info/exclude residue #2775

Description

@AlexAgenCheck

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:

  1. Firstmate writes into a project checkout, which AGENTS.md hard rule 1 otherwise forbids.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-prTriage: real bug or VISION-aligned feature, open for a PR

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions