From a3c671188f9a188c1faa90fefa02de196ad782a2 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 23 Dec 2025 00:52:45 -0800 Subject: [PATCH] feat: Auto-start refinery/witness on spawn, add refinery check to Witness patrol (gt-bjft) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements redundant systems to ensure infrastructure stays operational: - gt spawn: Auto-starts refinery and witness if not running - mol-witness-patrol: Added check-refinery step to verify refinery is alive This ensures polecats don't wait forever for merges. 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.5 --- internal/beads/builtin_molecules.go | 26 +++++++++++++++++++++- internal/beads/builtin_molecules_test.go | 28 ++++++++++++++---------- internal/cmd/spawn.go | 22 +++++++++++++++++++ 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/internal/beads/builtin_molecules.go b/internal/beads/builtin_molecules.go index b031af38..ac7cbae5 100644 --- a/internal/beads/builtin_molecules.go +++ b/internal/beads/builtin_molecules.go @@ -814,6 +814,30 @@ Handle by message type: Record any pending actions for later steps. Mark messages as processed when complete. +## Step: check-refinery +Ensure the refinery is alive and processing merge requests. + +**Redundant system**: This check runs in both gt spawn and Witness patrol +to ensure the merge queue processor stays operational. + +` + "```" + `bash +# Check if refinery session is running +gt session status /refinery + +# Check for merge requests in queue +bd list --type=merge-request --status=open +` + "```" + ` + +If merge requests are waiting AND refinery is not running: +` + "```" + `bash +gt session start /refinery +gt mail send /refinery -s "PATROL: Wake up" -m "Merge requests in queue. Please process." +` + "```" + ` + +If refinery is running but queue is non-empty for >30 min, send nudge. +This ensures polecats don't wait forever for their branches to merge. +Needs: inbox-check + ## Step: load-state Read handoff bead and get nudge counts. @@ -829,7 +853,7 @@ bd show If no handoff exists (fresh start), initialize empty state. This state persists across wisp burns and session cycles. -Needs: inbox-check +Needs: check-refinery ## Step: survey-workers List polecats and categorize by status. diff --git a/internal/beads/builtin_molecules_test.go b/internal/beads/builtin_molecules_test.go index 6c484c46..82cba1f9 100644 --- a/internal/beads/builtin_molecules_test.go +++ b/internal/beads/builtin_molecules_test.go @@ -300,16 +300,17 @@ func TestWitnessPatrolMolecule(t *testing.T) { t.Fatalf("failed to parse: %v", err) } - // Should have 10 steps: inbox-check, load-state, survey-workers, inspect-workers, - // decide-actions, execute-actions, save-state, generate-summary, context-check, burn-or-loop - if len(steps) != 10 { - t.Errorf("expected 10 steps, got %d", len(steps)) + // Should have 11 steps: inbox-check, check-refinery, load-state, survey-workers, + // inspect-workers, decide-actions, execute-actions, save-state, generate-summary, + // context-check, burn-or-loop + if len(steps) != 11 { + t.Errorf("expected 11 steps, got %d", len(steps)) } expectedRefs := []string{ - "inbox-check", "load-state", "survey-workers", "inspect-workers", - "decide-actions", "execute-actions", "save-state", "generate-summary", - "context-check", "burn-or-loop", + "inbox-check", "check-refinery", "load-state", "survey-workers", + "inspect-workers", "decide-actions", "execute-actions", "save-state", + "generate-summary", "context-check", "burn-or-loop", } for i, expected := range expectedRefs { if i >= len(steps) { @@ -327,13 +328,18 @@ func TestWitnessPatrolMolecule(t *testing.T) { t.Errorf("inbox-check should have no deps, got %v", steps[0].Needs) } - // load-state needs inbox-check + // check-refinery needs inbox-check if len(steps[1].Needs) != 1 || steps[1].Needs[0] != "inbox-check" { - t.Errorf("load-state should need inbox-check, got %v", steps[1].Needs) + t.Errorf("check-refinery should need inbox-check, got %v", steps[1].Needs) + } + + // load-state needs check-refinery + if len(steps[2].Needs) != 1 || steps[2].Needs[0] != "check-refinery" { + t.Errorf("load-state should need check-refinery, got %v", steps[2].Needs) } // burn-or-loop needs context-check - if len(steps[9].Needs) != 1 || steps[9].Needs[0] != "context-check" { - t.Errorf("burn-or-loop should need context-check, got %v", steps[9].Needs) + if len(steps[10].Needs) != 1 || steps[10].Needs[0] != "context-check" { + t.Errorf("burn-or-loop should need context-check, got %v", steps[10].Needs) } } diff --git a/internal/cmd/spawn.go b/internal/cmd/spawn.go index b6ba588b..6c7dd618 100644 --- a/internal/cmd/spawn.go +++ b/internal/cmd/spawn.go @@ -407,6 +407,28 @@ func runSpawn(cmd *cobra.Command, args []string) error { fmt.Printf(" %s\n", style.Dim.Render("Deacon notified of polecat start")) } + // Auto-start infrastructure if not running (redundant system - Witness also self-checks) + // This ensures the merge queue and polecat monitor are alive to handle work + refineryRunning, _ := sessMgr.IsRunning("refinery") + if !refineryRunning { + fmt.Printf("Starting refinery for %s...\n", rigName) + if err := sessMgr.Start("refinery", session.StartOptions{}); err != nil { + fmt.Printf(" %s\n", style.Dim.Render(fmt.Sprintf("Warning: could not start refinery: %v", err))) + } else { + fmt.Printf(" %s\n", style.Dim.Render("Refinery started")) + } + } + + witnessRunning, _ := sessMgr.IsRunning("witness") + if !witnessRunning { + fmt.Printf("Starting witness for %s...\n", rigName) + if err := sessMgr.Start("witness", session.StartOptions{}); err != nil { + fmt.Printf(" %s\n", style.Dim.Render(fmt.Sprintf("Warning: could not start witness: %v", err))) + } else { + fmt.Printf(" %s\n", style.Dim.Render("Witness started")) + } + } + return nil }