feat: Auto-start refinery/witness on spawn, add refinery check to Witness patrol (gt-bjft)
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 <noreply@anthropic.com>
This commit is contained in:
@@ -814,6 +814,30 @@ Handle by message type:
|
|||||||
Record any pending actions for later steps.
|
Record any pending actions for later steps.
|
||||||
Mark messages as processed when complete.
|
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 <rig>/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 <rig>/refinery
|
||||||
|
gt mail send <rig>/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
|
## Step: load-state
|
||||||
Read handoff bead and get nudge counts.
|
Read handoff bead and get nudge counts.
|
||||||
|
|
||||||
@@ -829,7 +853,7 @@ bd show <handoff-bead-id>
|
|||||||
|
|
||||||
If no handoff exists (fresh start), initialize empty state.
|
If no handoff exists (fresh start), initialize empty state.
|
||||||
This state persists across wisp burns and session cycles.
|
This state persists across wisp burns and session cycles.
|
||||||
Needs: inbox-check
|
Needs: check-refinery
|
||||||
|
|
||||||
## Step: survey-workers
|
## Step: survey-workers
|
||||||
List polecats and categorize by status.
|
List polecats and categorize by status.
|
||||||
|
|||||||
@@ -300,16 +300,17 @@ func TestWitnessPatrolMolecule(t *testing.T) {
|
|||||||
t.Fatalf("failed to parse: %v", err)
|
t.Fatalf("failed to parse: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should have 10 steps: inbox-check, load-state, survey-workers, inspect-workers,
|
// Should have 11 steps: inbox-check, check-refinery, load-state, survey-workers,
|
||||||
// decide-actions, execute-actions, save-state, generate-summary, context-check, burn-or-loop
|
// inspect-workers, decide-actions, execute-actions, save-state, generate-summary,
|
||||||
if len(steps) != 10 {
|
// context-check, burn-or-loop
|
||||||
t.Errorf("expected 10 steps, got %d", len(steps))
|
if len(steps) != 11 {
|
||||||
|
t.Errorf("expected 11 steps, got %d", len(steps))
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRefs := []string{
|
expectedRefs := []string{
|
||||||
"inbox-check", "load-state", "survey-workers", "inspect-workers",
|
"inbox-check", "check-refinery", "load-state", "survey-workers",
|
||||||
"decide-actions", "execute-actions", "save-state", "generate-summary",
|
"inspect-workers", "decide-actions", "execute-actions", "save-state",
|
||||||
"context-check", "burn-or-loop",
|
"generate-summary", "context-check", "burn-or-loop",
|
||||||
}
|
}
|
||||||
for i, expected := range expectedRefs {
|
for i, expected := range expectedRefs {
|
||||||
if i >= len(steps) {
|
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)
|
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" {
|
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
|
// burn-or-loop needs context-check
|
||||||
if len(steps[9].Needs) != 1 || steps[9].Needs[0] != "context-check" {
|
if len(steps[10].Needs) != 1 || steps[10].Needs[0] != "context-check" {
|
||||||
t.Errorf("burn-or-loop should need context-check, got %v", steps[9].Needs)
|
t.Errorf("burn-or-loop should need context-check, got %v", steps[10].Needs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,6 +407,28 @@ func runSpawn(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Printf(" %s\n", style.Dim.Render("Deacon notified of polecat start"))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user