fix: guard firstmate against primary worktree tangles#83
Merged
Conversation
Firstmate is a treehouse-pooled git repo of itself: the primary checkout and every crewmate worktree / secondmate home are linked worktrees of one repo. A crewmate sent to work firstmate-on-itself can branch and commit in the PRIMARY checkout instead of its own isolated worktree, stranding the primary on a feature branch (e.g. fm/readme-restructure-d3). Add two guards: Prevention (spawn-time): - fm-brief.sh: ship-brief Setup now opens with a worktree-isolation assertion ahead of the branch step. The crewmate confirms it is in its own treehouse worktree, and stops with `blocked: launched in primary checkout, not an isolated worktree` if it resolves to the primary. - fm-spawn.sh: after treehouse get, refuse to launch unless the resolved worktree is a genuine isolated worktree root distinct from the primary checkout (PROJ_ABS); abort with a clear error otherwise. Detection (immediate): - bin/fm-tangle-lib.sh: shared classifier. A NAMED non-default branch in the primary is the tangle; the default branch and detached HEAD (the legitimate state of worktrees and secondmate homes) stay silent. - fm-guard.sh: bordered alarm, in the watcher-down banner's spirit, on the very next fleet action when the primary is on a feature branch. - fm-bootstrap.sh: same assertion at session start as a TANGLE: line. Tests: tests/fm-tangle-guard.test.sh covers the lib classification, the guard banner, the bootstrap line, brief assertion ordering, and the spawn abort. fm-bootstrap and fm-watcher-lock guard tests now pin FM_ROOT so the ambient (feature-branch) checkout never leaks a TANGLE line into them. Docs: AGENTS.md sections 3, 7, 8, 11 document both guards.
471b5d2 to
129516d
Compare
leo1oel
added a commit
to leo1oel/nemo
that referenced
this pull request
Jun 26, 2026
…unchenguid#83) (#6) Port two functional upstream fixes, adapted to the herdr/Claude-only fork. kunchenguid#88 fm-send settle: after a successful text submit, pause FM_SEND_SETTLE seconds (default 1, 0 disables) before returning. A cleared composer only proves the text was submitted; the harness needs a beat to spin up the turn before its busy footer appears, so an immediate peek would otherwise see the stale idle pane (the fm-send false-positive symptom). Scoped to the text path only; the shared submit core the away-mode daemon uses never pays it. kunchenguid#83 worktree-tangle guard: firstmate is a git repo of itself (secondmate homes and self-on-self crewmate worktrees are linked herdr worktrees of FM_ROOT), so a crewmate can branch/commit in the PRIMARY checkout instead of its isolated worktree, stranding the primary on a feature branch. - bin/fm-tangle-lib.sh: pure-git classifier (named non-default branch in the primary is the tangle; default branch and detached HEAD stay silent). - fm-guard.sh: bordered ●-marked alarm on the next fleet action, checked first and independent of in-flight tasks. - fm-spawn.sh: refuse to launch unless the opened worktree is a genuine isolated worktree root distinct from the primary checkout. - fm-brief.sh: ship briefs open with a worktree-isolation assertion before the branch step. - AGENTS.md sections 7/8/11 document both guards. - tests/fm-tangle-guard.test.sh (new) covers lib classification, the guard banner, brief assertion ordering, and the spawn abort; fm-wake-queue guard tests pin FM_ROOT so the ambient feature-branch checkout cannot leak a banner.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Add two guards to firstmate that prevent and immediately detect a 'worktree tangle': the failure mode where a crewmate spawned to work on the firstmate repo itself branches/commits in the PRIMARY firstmate checkout (the repo root firstmate operates from) instead of its assigned disposable treehouse worktree, stranding the primary on a feature branch. This targets only the firstmate-on-itself case (projects have no such exposure).
Architecture insight that shapes the design: firstmate is a treehouse-pooled git repo of itself - the primary checkout AND every crewmate worktree / secondmate home are all linked git worktrees of one repo, and the primary is itself a writable linked worktree normally on the default branch (main). Therefore BRANCH NAME is the only valid discriminator: a named non-default branch checked out in the primary is the tangle; the default branch and detached HEAD (the legitimate resting state of worktrees and secondmate homes) are healthy. A 'linked-worktree vs main-worktree' test would be WRONG here because it would exempt the primary itself, so it was deliberately not used. The default branch name is resolved (origin/HEAD then main/master), not hardcoded.
GUARD 1 (prevention, spawn-time): (a) bin/fm-brief.sh - the ship-brief Setup opens with a worktree-isolation assertion ahead of the 'git checkout -b' step; the crewmate confirms via git rev-parse --show-toplevel (and a git-dir != git-common-dir linked-worktree test) it is in its own treehouse worktree, and if it resolves to the primary it STOPS and reports 'blocked: launched in primary checkout, not an isolated worktree'. (b) bin/fm-spawn.sh - after treehouse get resolves the worktree (WT), it asserts WT is a genuine isolated worktree distinct from the primary checkout (PROJ_ABS) with git -C WT rev-parse --show-toplevel canonicalizing to WT; otherwise aborts the spawn.
GUARD 2 (detection, immediate): new shared bin/fm-tangle-lib.sh provides fm_default_branch and fm_primary_tangle_branch. bin/fm-guard.sh emits a bordered alarm (like the watcher-down banner) when the primary (FM_ROOT) is on a feature branch, placed before the in-flight early-exit so it fires on the next fleet action; bin/fm-bootstrap.sh prints the same as a 'TANGLE:' problem line at session start.
Tests: new tests/fm-tangle-guard.test.sh covers lib classification, the guard banner, the bootstrap line, brief assertion ordering, and the spawn abort, hermetic over temp git repos/fakebins. tests/fm-bootstrap.test.sh and tests/fm-watcher-lock.test.sh now pin FM_ROOT so the new alarm stays inert there (the ambient checkout runs on a feature branch in CI).
Docs: AGENTS.md sections 3, 7, 8, 11 document both guards. Deliberately did NOT touch README.md, docs/, or CONTRIBUTING.md (a separate restructure is in flight there; changes are disjoint). Scope is firstmate-repo tooling only.
Note: a prior run was aborted; the only delta is a shell-lint fix - relocating '|| true' outside the $(cd && pwd -P) substitutions in fm-spawn.sh and converting an 'A && B || fail' and a bare fm_git_identity call in the new test to SC2015/SC2119-clean forms - so the repo CI shellcheck (stricter than the pipeline lint) passes. No behavior change; full local suite (13/13) green.
What Changed
Risk Assessment
✅ Low: Captain, the change is narrowly scoped to spawn/bootstrap/guard shell paths with focused coverage, and I found no material risks in the diff.
Testing
Captain, I exercised the new tangle guard tests, the adjacent bootstrap and watcher-lock suites, and a manual E2E transcript showing silent healthy states, the guard banner, the bootstrap TANGLE line, brief ordering, spawn refusal in the primary checkout, and allowed spawn into an isolated worktree; the worktree was clean afterward.
Evidence: Tangle guard E2E CLI transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
🔧 **Rebase** - 3 issues found → auto-fixed ✅
bin/fm-brief.sh- merge conflict rebasing onto origin/fm/tangle-guard-g5bin/fm-spawn.sh- merge conflict rebasing onto origin/fm/tangle-guard-g5tests/fm-tangle-guard.test.sh- merge conflict rebasing onto origin/fm/tangle-guard-g5🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
TMPDIR=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVZY23DPJ2R136369KQ4FQGH/tmp bash tests/fm-tangle-guard.test.shTMPDIR=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVZY23DPJ2R136369KQ4FQGH/tmp bash tests/fm-bootstrap.test.shTMPDIR=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVZY23DPJ2R136369KQ4FQGH/tmp bash tests/fm-watcher-lock.test.sh/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVZY23DPJ2R136369KQ4FQGH/run-tangle-guard-e2e.sh /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KVZY23DPJ2R136369KQ4FQGH /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVZY23DPJ2R136369KQ4FQGHgit status --short✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.