fix(spawn): skip treehouse get's transient cwd in worktree-discovery poll#648
Open
psacc wants to merge 3 commits into
Open
fix(spawn): skip treehouse get's transient cwd in worktree-discovery poll#648psacc wants to merge 3 commits into
psacc wants to merge 3 commits into
Conversation
…poll The worktree-discovery poll accepted the first non-project cwd, but treehouse get transits / and ~/.treehouse before it settles in the pooled worktree. herdr's foreground_cwd exposes those transients (tmux's pane_current_path reports the settled shell cwd and never did), so a herdr spawn resolved WT to / , failed the isolation gate, and aborted - orphaning the still-initializing treehouse get, which then held the pool's only slot forever with no crewmate (the single-slot wedge). Gate the poll on a new non-fatal is_isolated_worktree predicate (also the one validate_spawn_worktree now delegates to) so it skips the transients until the pane settles in a real worktree. Remember the last non-worktree cwd so a genuine tangle still gets the precise resolved-path refusal instead of a vague timeout, and make the poll budget configurable. No-op for tmux. Regression test drives real fm-spawn against a pane that reports / then the worktree; reproduces the exact 'resolved /' failure pre-fix.
5 tasks
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
Contribute a single, self-contained bug fix to the upstream firstmate repo (kunchenguid/firstmate) THROUGH this pipeline, so the PR carries the no-mistakes signature that upstream's "PR must be raised via no-mistakes" CI gate requires. This supersedes the existing hand-made PR #480 (#480), which is permanently blocked by that gate; the PR body should say "Supersedes #480" and reuse its problem/root-cause writeup.
The change is a cherry-pick of commit 2a5fa59, "fix(spawn): skip treehouse get's transient cwd in worktree-discovery poll". It repairs a herdr-backend single-slot wedge. Root cause:
treehouse gettransits intermediate cwds while it sets a worktree up (momentarily/and then the treehouse root) before it creates and enters the pooled worktree, and herdr's foreground_cwd exposes those transients (tmux's pane_current_path reports the settled cwd and never surfaced them, which is why only herdr wedged). bin/fm-spawn.sh's worktree-discovery poll previously accepted the FIRST cwd that merely differed from the project dir, so it captured/, handed that transient to validate_spawn_worktree, failed the isolation gate, and aborted the spawn, while the still-initializingtreehouse getcompleted and held the pool's only slot forever with no crewmate attached (so the next spawn also failed). The fix gates the poll on a new non-fatal is_isolated_worktree predicate (a real git-worktree root, physically distinct from the primary checkout, the same rule validate_spawn_worktree now delegates to), so it skips/and the treehouse root and keeps polling until the pane settles in the real worktree. It is a no-op for tmux, whose first observed non-project cwd already is that worktree.Deliberate decisions a reviewer should not flag as mistakes:
/, then the real worktree.Known environmental note (not a regression from this change): a pre-existing, unrelated test - "old fm-teardown.sh (scout, report present) should succeed" in tests/fm-backend.test.sh - fails when the
tasks-axibinary is not on PATH, because bin/fm-decision-hold.sh hard-refuses without a compatible tasks-axi. That test is present verbatim on upstream/main and is untouched by this change; any failure there is a missing-tool precondition in the environment, not caused by this fix.What Changed
bin/fm-spawn.sh's worktree-discovery poll no longer accepts the first cwd that merely differs from the project dir. It now gates on a new non-fatalis_isolated_worktreepredicate (a real git-worktree root physically distinct from the primary checkout — the same rulevalidate_spawn_worktreenow delegates to), so transient cwds thattreehouse gettransits while setting up a pooled worktree (momentarily/, then the treehouse root) are skipped and polling continues until the pane settles in the real worktree. This repairs a herdr-backend wedge where the poll captured/, failed the isolation gate, aborted the spawn, and left the pool's only slot held with no crewmate attached; tmux is unaffected since its first observed non-project cwd is already the settled worktree. The poll budget is configurable viaFM_SPAWN_WORKTREE_POLL_TRIES, documented indocs/configuration.mdanddocs/herdr-backend.md.tests/fm-backend.test.sh:test_spawn_worktree_poll_skips_treehouse_get_transient_cwd) driving the realbin/fm-spawn.shagainst a fake pane that returns the project dir, then a transient/, then the real worktree; the pre-fix script reproduces the exact live failure.tasks-axivia a shared helper intests/lib.sh(so they no longer require a globally installed binary), and watcher timing races intests/fm-watcher-lock.test.sh/bin/fm-watch-checkpoint.shwere fixed.Supersedes #480, which is blocked by upstream's raised-via-no-mistakes CI gate.
Risk Assessment
✅ Low: A single, well-bounded shell fix whose new predicate exactly mirrors the pre-existing fatal isolation check, with a dedicated regression test, a documented failure-mode writeup, and verified no adverse impact on any other test suite driving fm-spawn.sh.
Testing
Ran the affected suites at the target commit (all green, including the previously-failing teardown tests now hermetic via the tasks-axi stub on a machine without tasks-axi), then demonstrated the user intent end-to-end by swapping in the pre-fix bin/fm-spawn.sh and showing the new regression test reproduces the exact herdr wedge failure ("resolved '/'") that the fix eliminates; no visual evidence applies since this is a CLI/shell-script change with a transcript artifact instead.
Evidence: Repro transcript: wedge fails pre-fix, passes post-fix
PRE-FIX (a5a3652's bin/fm-spawn.sh): not ok - fm-spawn.sh should skip treehouse get's transient '/' cwd and settle on the real worktree error: treehouse get did not yield an isolated worktree (resolved '/'; worktree root 'none'; ...) — the exact live herdr single-slot wedge FIXED (a9f10f8): ok - fm-spawn.sh: the worktree-discovery poll skips treehouse get's transient intermediate cwds (/ , treehouse root) and resolves the real isolated worktreePipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/fm-spawn.sh:894- Genuine-tangle refusal latency increased from ~1s to the full poll budget (60s): the poll no longer accepts the first non-project cwd, so a pane permanently settled in a non-isolated path (e.g. inside the primary checkout) only gets validate_spawn_worktree's precise refusal after all poll_tries elapse via the moved_to fallback. The inline comment documents this as a deliberate tradeoff (cannot distinguish a settling transient from a permanent tangle in one read), so no action needed — just an operational latency note.bin/fm-spawn.sh:881- FM_SPAWN_WORKTREE_POLL_TRIES validation (case "$poll_tries" in ''|[!0-9]|0) misses multi-digit zero values like "00"/"000": with GNU seq,seq 1 00emits nothing so the poll loop never runs and the spawn immediately fails with "did not enter a worktree within 00s" (BSD seq would instead count down 1,0 and poll twice). It's an internal test-only knob, so impact is negligible; adding[ "$poll_tries" -gt 0 ] 2>/dev/null || poll_tries=60after the case would close the gap.🔧 **Test** - 1 issue found → auto-fixed ✅
command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"🔧 Fix: stub tasks-axi in teardown tests; fix watcher timing races
✅ Re-checked - no issues remain.
command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"Baseline: configured full-suite command (for t in tests/*.test.sh; do bash "$t"; done) already ran green before this roundbash tests/fm-backend.test.shat target — all ok, including newtest_spawn_worktree_poll_skips_treehouse_get_transient_cwdand the teardown conformance tests that previously needed a globally installed tasks-axi (not present on this machine; stub exercised)bash tests/fm-tangle-guard.test.sh,tests/fm-backend-orca.test.sh,tests/fm-backend-zellij.test.sh,tests/fm-watch-checkpoint.test.sh,tests/fm-watcher-lock.test.sh— all pass at targetPre-fix reproduction: swapped ingit show a5d3652:bin/fm-spawn.sh, re-ran the regression test — it fails with the exact live-bug errortreehouse get did not yield an isolated worktree (resolved '/'); restored the fixed script and it passes (worktree left clean)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.