Skip to content

fix(spawn): skip treehouse get's transient cwd in worktree-discovery poll#480

Closed
psacc wants to merge 1 commit into
kunchenguid:mainfrom
psacc:fm/herdr-fix-r5
Closed

fix(spawn): skip treehouse get's transient cwd in worktree-discovery poll#480
psacc wants to merge 1 commit into
kunchenguid:mainfrom
psacc:fm/herdr-fix-r5

Conversation

@psacc

@psacc psacc commented Jul 11, 2026

Copy link
Copy Markdown

Problem

herdr-backend spawns intermittently aborted with:

error: treehouse get did not yield an isolated worktree (resolved '/'; ...); refusing to launch

and — worse — left a stuck treehouse get holding the pool's only slot with no crewmate attached, so the next spawn also failed (the single-slot wedge). tmux-backend spawns were unaffected.

Root cause

treehouse get transits intermediate cwds while it sets the worktree up — it is momentarily at / and at the treehouse root (~/.treehouse) before it creates and enters the pooled worktree. bin/fm-spawn.sh's worktree-discovery poll accepted the first cwd that merely differed from the project directory, so it captured / (or ~/.treehouse), handed that transient to validate_spawn_worktree, and aborted — while the still-initializing treehouse get completed and held the slot forever.

herdr's foreground_cwd exposes those transients because it reads the actively-running foreground process's cwd; tmux's pane_current_path reports the settled interactive-shell cwd and never surfaced them — which is why only the herdr path wedged.

Fix

The poll now gates on a new non-fatal is_isolated_worktree <path> 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 ~/.treehouse and keeps polling until the pane settles in the actual worktree. No-op for tmux, whose first observed non-project cwd already is that worktree.

Testing

  • Added tests/fm-backend.test.sh:test_spawn_worktree_poll_skips_treehouse_get_transient_cwd, which drives the real bin/fm-spawn.sh against a fake pane whose polls return the project dir, then a transient /, then the real worktree. It reproduces the exact resolved '/' failure against the pre-fix poll and passes against the fix.
  • fm-backend and fm-tangle-guard suites green; shellcheck bin/*.sh bin/backends/*.sh tests/*.sh clean.
  • Reproduced live 3× against real herdr 0.7.3 + treehouse on a single-slot pool before the fix.

Opened as a draft for maintainer review before publishing.

…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.
@psacc

psacc commented Jul 16, 2026

Copy link
Copy Markdown
Author

Superseded by #648, which carries the same worktree-poll fix raised through the no-mistakes pipeline (the gate this PR could not pass). Closing in its favor.

@psacc psacc closed this Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant