fix: wake pi crewmates after each turn - #12
Merged
Merged
Conversation
vipentti
pushed a commit
to vipentti/firstmate
that referenced
this pull request
Aug 5, 2026
* fix(spawn): fire pi turn-end on turn_end, not agent_end * no-mistakes(document): Document pi turn-end event
4 tasks
This was referenced Aug 7, 2026
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
Fix firstmate's pi crewmate turn-end hook. fm-spawn's generated pi extension listened on pi's 'agent_end' event, which (per pi 0.79.1 type docs) fires only once when the whole agent run exits - so the turn-end backstop never fired at per-turn boundaries for pi crewmates, leaving firstmate reliant on crewmate status appends alone and blind to an idle crewmate that stopped without writing status. Change the hook to 'turn_end', which fires after each turn the agent finishes, matching how the claude/codex/opencode adapters behave. One-line change to the pi-ext template in bin/fm-spawn.sh plus an explanatory comment; no other behavior change.
What Changed
bin/fm-spawn.shto listen forturn_endinstead ofagent_end, so firstmate receives a turn-ended signal after each completed pi turn rather than only when the whole agent run exits.turn_endfor per-turn idle detection.AGENTS.mdso future harness changes preserve the same watcher behavior.Risk Assessment
✅ Low: The branch is a narrow one-line event-name correction plus explanatory generated-code comments, with no broader control-flow or API surface changes.
Testing
Baseline bootstrap could not run due missing local firstmate config state, but the focused end-to-end spawn path was exercised:
fm-spawn.shgenerated and launched a pi extension usingturn_end; live pi execution stopped at the trust prompt, which I did not accept because it would change user-level trust outside the worktree boundary, so I supplemented it with a mocked extension event simulation proving the generated hook would touch the turn-ended sentinel. All focused checks passed and temporary worktree test scaffolding was removed.Evidence: Generated pi extension evidence
The emitted extension registerspi.on("turn_end", ...)and touchesstate/pi-hook-e2e.turn-ended; it contains nopi.on("agent_end", ...)registration.Evidence: Pi spawn pane transcript
Shows the real spawn command launchingpi -e /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.pi-ext.ts ...before pi displayed its project trust prompt.Evidence: Generated extension event simulation
registered_event=turn_end execFile_command=touch execFile_arg=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.turn-endedPipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
bin/fm-bootstrap.sh- attempted baseline bootstrap; it failed because this isolated worktree lacksconfig/crew-harnessparent state, so validation continued without creating persistent config.Created temporarydata/pi-hook-e2e/brief.mdandstate/, then ranbin/fm-spawn.sh pi-hook-e2e . pi --scoutto exercise the real pi spawn path.Capturedstate/pi-hook-e2e.pi-ext.tsand the tmux pane transcript into/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV18PMDX505TN87957JNNXTT.node -e '...'asserted the generated pi extension containspi.on("turn_end"...)and does not registerpi.on("agent_end"...).node -e '...'asserted the captured spawn transcript launchedpi -e .../state/pi-hook-e2e.pi-ext.ts.node <<'NODE' ... NODEloaded the generated extension text with a mocked pi event emitter and verified triggering the registered callback callstouchonstate/pi-hook-e2e.turn-ended.bin/fm-teardown.sh pi-hook-e2etore down the spawned scout task;git status --shortconfirmed no temporary worktree scaffolding remained.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.