feat(convoy): add redundant observers to Witness and Refinery

Per PRIMING.md principle "Redundant Monitoring Is Resilience", add convoy
completion checks to Witness and Refinery for redundant observation:

- New internal/convoy/observer.go with shared CheckConvoysForIssue function
- Witness: checks convoys after successful polecat nuke in HandleMerged
- Refinery: checks convoys after closing source issue in both success handlers

Multiple observers closing the same convoy is idempotent - each checks if
convoy is already closed before running `gt convoy check`.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dementus
2026-01-20 19:41:33 -08:00
committed by beads/crew/emma
parent 2b56ee2545
commit d0a1e165e5
3 changed files with 166 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/convoy"
"github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/mail"
"github.com/steveyegge/gastown/internal/rig"
@@ -264,6 +265,14 @@ func HandleMerged(workDir, rigName string, msg *mail.Message) *HandlerResult {
result.Handled = true
result.WispCreated = wispID
result.Action = fmt.Sprintf("auto-nuked %s (cleanup_status=clean, wisp=%s)", payload.PolecatName, wispID)
// Redundant convoy observer: check if completed issue is tracked by a convoy
if payload.IssueID != "" {
townRoot, _ := workspace.Find(workDir)
if townRoot != "" {
convoy.CheckConvoysForIssue(townRoot, payload.IssueID, "witness", nil)
}
}
}
case "has_uncommitted":
@@ -299,6 +308,14 @@ func HandleMerged(workDir, rigName string, msg *mail.Message) *HandlerResult {
result.Handled = true
result.WispCreated = wispID
result.Action = fmt.Sprintf("auto-nuked %s (commit on main, cleanup_status=%s, wisp=%s)", payload.PolecatName, cleanupStatus, wispID)
// Redundant convoy observer: check if completed issue is tracked by a convoy
if payload.IssueID != "" {
townRoot, _ := workspace.Find(workDir)
if townRoot != "" {
convoy.CheckConvoysForIssue(townRoot, payload.IssueID, "witness", nil)
}
}
}
}