feat(done): Add --phase-complete flag for gate-based phase handoffs

Add support for signaling phase completion when a polecat needs to wait
on a gate before continuing. The --phase-complete flag with --gate ID
allows polecats to hand off control while awaiting external conditions.

Changes:
- done.go: Add --phase-complete and --gate flags, PHASE_COMPLETE exit type
- protocol.go: Add Gate field to PolecatDonePayload
- handlers.go: Handle PHASE_COMPLETE by recycling session (keep worktree)
- beads.go: Add AddGateWaiter method for gate registration

This enables multi-phase molecule workflows with async coordination (bd-gxb4)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
furiosa
2026-01-02 16:41:39 -08:00
committed by Steve Yegge
parent 535bb8e4c2
commit f8b650cf82
4 changed files with 97 additions and 21 deletions

View File

@@ -28,6 +28,7 @@ type HandlerResult struct {
// HandlePolecatDone processes a POLECAT_DONE message from a polecat.
// For ESCALATED/DEFERRED exits (no pending MR), auto-nukes if clean.
// For PHASE_COMPLETE exits, recycles the polecat (session ends, worktree kept).
// For exits with pending MR, creates a cleanup wisp to wait for MERGED.
func HandlePolecatDone(workDir, rigName string, msg *mail.Message) *HandlerResult {
result := &HandlerResult{
@@ -42,6 +43,18 @@ func HandlePolecatDone(workDir, rigName string, msg *mail.Message) *HandlerResul
return result
}
// Handle PHASE_COMPLETE: recycle polecat (session ends but worktree stays)
// The polecat is registered as a waiter on the gate and will be re-dispatched
// when the gate closes via gt gate wake.
if payload.Exit == "PHASE_COMPLETE" {
result.Handled = true
result.Action = fmt.Sprintf("phase-complete for %s (gate=%s) - session recycled, awaiting gate", payload.PolecatName, payload.Gate)
// Note: The polecat has already registered itself as a gate waiter via bd
// The gate wake mechanism (gt gate wake) will send mail when gate closes
// A new polecat will be dispatched to continue the molecule from the next step
return result
}
// Check if this polecat has a pending MR
// ESCALATED/DEFERRED exits typically have no MR pending
hasPendingMR := payload.MRID != "" || payload.Exit == "COMPLETED"