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

@@ -14,6 +14,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/protocol"
@@ -449,6 +450,12 @@ func (e *Engineer) handleSuccess(mr *beads.Issue, result ProcessResult) {
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mrFields.SourceIssue, err)
} else {
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mrFields.SourceIssue)
// Redundant convoy observer: check if merged issue is tracked by a convoy
logger := func(format string, args ...interface{}) {
_, _ = fmt.Fprintf(e.output, "[Engineer] "+format+"\n", args...)
}
convoy.CheckConvoysForIssue(e.rig.Path, mrFields.SourceIssue, "refinery", logger)
}
}
@@ -557,6 +564,12 @@ func (e *Engineer) HandleMRInfoSuccess(mr *MRInfo, result ProcessResult) {
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mr.SourceIssue, err)
} else {
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mr.SourceIssue)
// Redundant convoy observer: check if merged issue is tracked by a convoy
logger := func(format string, args ...interface{}) {
_, _ = fmt.Fprintf(e.output, "[Engineer] "+format+"\n", args...)
}
convoy.CheckConvoysForIssue(e.rig.Path, mr.SourceIssue, "refinery", logger)
}
}