Skip to content

fix: wake pi crewmates after each turn - #12

Merged
kunchenguid merged 2 commits into
mainfrom
fix/pi-turn-end
Jun 13, 2026
Merged

fix: wake pi crewmates after each turn#12
kunchenguid merged 2 commits into
mainfrom
fix/pi-turn-end

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

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

  • Updated the pi harness extension generated by bin/fm-spawn.sh to listen for turn_end instead of agent_end, so firstmate receives a turn-ended signal after each completed pi turn rather than only when the whole agent run exits.
  • Added an inline comment explaining why the pi hook must use turn_end for per-turn idle detection.
  • Documented the pi adapter requirement in AGENTS.md so 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.sh generated and launched a pi extension using turn_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 registers pi.on("turn_end", ...) and touches state/pi-hook-e2e.turn-ended; it contains no pi.on("agent_end", ...) registration.

// Firstmate turn-end signal; written by fm-spawn.
// Use "turn_end" (fires after each turn the agent finishes), not "agent_end"
// (fires once, only when the whole run exits): the watcher needs a signal at
// every turn boundary so an idle crewmate is surfaced, not just at shutdown.
import { execFile } from "node:child_process";
export default function (pi: any) {
  pi.on("turn_end", () => execFile("touch", ["/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.turn-ended"]));
}
Evidence: Pi spawn pane transcript

Shows the real spawn command launching pi -e /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.pi-ext.ts ... before pi displayed its project trust prompt.

treehouse get
01KV18PMDX505TN87957JNNXTT HEAD
❯ treehouse get
🌳 Setting up worktree...
🌳 Entered worktree at ~/.treehouse/01KV18PMDX505TN87957JNNXTT-b8697d/1/01KV18PMDX505TN87957JNNXTT. Type 'exit' to return.
01KV18PMDX505TN87957JNNXTT HEAD
❯ pi -e /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.pi-ext.ts "$(cat /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/data/pi-hook-e2e/brief.md)"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

 Trust project folder?
 /Users/kunchen/.treehouse/01KV18PMDX505TN87957JNNXTT-b8697d/1/01KV18PMDX505TN87957JNNXTT

 This allows pi to load .pi settings and resources, install missing project packages, and execute project extensions.

 → Trust
   Trust parent folder (/Users/kunchen/.treehouse/01KV18PMDX505TN87957JNNXTT-b8697d/1)
   Trust (this session only)
   Do not trust
   Do not trust (this session only)

 ↑↓ navigate  enter select  escape/ctrl+c cancel

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
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-ended

registered_event=turn_end
execFile_command=touch
execFile_arg=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV18PMDX505TN87957JNNXTT/state/pi-hook-e2e.turn-ended

Pipeline

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 lacks config/crew-harness parent state, so validation continued without creating persistent config.
  • Created temporary data/pi-hook-e2e/brief.md and state/, then ran bin/fm-spawn.sh pi-hook-e2e . pi --scout to exercise the real pi spawn path.
  • Captured state/pi-hook-e2e.pi-ext.ts and the tmux pane transcript into /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV18PMDX505TN87957JNNXTT.
  • node -e '...' asserted the generated pi extension contains pi.on("turn_end"...) and does not register pi.on("agent_end"...).
  • node -e '...' asserted the captured spawn transcript launched pi -e .../state/pi-hook-e2e.pi-ext.ts.
  • node <<'NODE' ... NODE loaded the generated extension text with a mocked pi event emitter and verified triggering the registered callback calls touch on state/pi-hook-e2e.turn-ended.
  • bin/fm-teardown.sh pi-hook-e2e tore down the spawned scout task; git status --short confirmed no temporary worktree scaffolding remained.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid merged commit d9ec890 into main Jun 13, 2026
3 checks passed
@kunchenguid
kunchenguid deleted the fix/pi-turn-end branch June 13, 2026 20:12
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
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