From 36f17bbadd4d4fbea895cd6ac76da1bfbda265a9 Mon Sep 17 00:00:00 2001 From: mayor Date: Wed, 31 Dec 2025 02:02:26 -0800 Subject: [PATCH] fix: Refinery queue uses beads MQ as source of truth (hq-eggh5) The refinery was checking git branches instead of the beads merge queue. This caused MRs to pile up when branches were deleted but MR beads remained. - manager.go: Queue() now queries beads for type=merge-request issues - refinery.md.tmpl: Updated queue-scan to use gt mq list - mol-refinery-patrol.formula.toml: Updated queue-scan step instructions --- .../formulas/mol-refinery-patrol.formula.toml | 27 ++++---- internal/refinery/manager.go | 64 +++++++++++++++++-- internal/templates/roles/refinery.md.tmpl | 8 +-- 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/.beads/formulas/mol-refinery-patrol.formula.toml b/.beads/formulas/mol-refinery-patrol.formula.toml index d3fbf79c..a968c27f 100644 --- a/.beads/formulas/mol-refinery-patrol.formula.toml +++ b/.beads/formulas/mol-refinery-patrol.formula.toml @@ -99,27 +99,28 @@ id = "queue-scan" title = "Scan merge queue" needs = ["inbox-check"] description = """ -Review the queue built from MERGE_READY messages in inbox-check. +Check the beads merge queue - this is the SOURCE OF TRUTH for pending merges. ```bash -# Always fetch with prune to clean stale tracking refs: git fetch --prune origin - -# For each queued merge request, verify the branch exists: -git branch -r | grep - -# IMPORTANT: Before queuing, verify branch has unmerged commits: -git log origin/main..origin/ --oneline | head -1 -# If empty → branch already merged. Archive MERGE_READY, don't queue. +gt mq list ``` +The beads MQ tracks all pending merge requests. Do NOT rely on `git branch -r | grep polecat` +as branches may exist without MR beads, or MR beads may exist for already-merged work. + If queue empty, skip to context-check step. -If branch doesn't exist for a queued item: -- Mail witness: Branch not found, cannot merge -- Remove from queue +For each MR in the queue, verify the branch still exists: +```bash +git branch -r | grep +``` -Track verified branch list for this cycle.""" +If branch doesn't exist for a queued MR: +- Close the MR bead: `bd close --reason "Branch no longer exists"` +- Remove from processing queue + +Track verified MR list for this cycle.""" [[steps]] id = "process-branch" diff --git a/internal/refinery/manager.go b/internal/refinery/manager.go index 1f10da84..412a65c1 100644 --- a/internal/refinery/manager.go +++ b/internal/refinery/manager.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/steveyegge/gastown/internal/beads" "github.com/steveyegge/gastown/internal/claude" "github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/events" @@ -239,14 +240,21 @@ func (m *Manager) Stop() error { } // Queue returns the current merge queue. +// Uses beads merge-request issues as the source of truth (not git branches). func (m *Manager) Queue() ([]QueueItem, error) { - // Discover branches that look like polecat work branches - branches, err := m.discoverWorkBranches() + // Query beads for open merge-request type issues + // BeadsPath() returns the git-synced beads location + b := beads.New(m.rig.BeadsPath()) + issues, err := b.List(beads.ListOptions{ + Type: "merge-request", + Status: "open", + Priority: -1, // No priority filter + }) if err != nil { - return nil, err + return nil, fmt.Errorf("querying merge queue from beads: %w", err) } - // Load any pending MRs from state + // Load any current processing state ref, err := m.loadState() if err != nil { return nil, err @@ -265,10 +273,14 @@ func (m *Manager) Queue() ([]QueueItem, error) { }) } - // Add discovered branches as pending - for _, branch := range branches { - mr := m.branchToMR(branch) + // Convert beads issues to queue items + for _, issue := range issues { + mr := m.issueToBead(issue) if mr != nil { + // Skip if this is the currently processing MR + if ref.CurrentMR != nil && ref.CurrentMR.ID == mr.ID { + continue + } items = append(items, QueueItem{ Position: pos, MR: mr, @@ -281,6 +293,44 @@ func (m *Manager) Queue() ([]QueueItem, error) { return items, nil } +// issueToBead converts a beads issue to a MergeRequest. +func (m *Manager) issueToBead(issue *beads.Issue) *MergeRequest { + if issue == nil { + return nil + } + + fields := beads.ParseMRFields(issue) + if fields == nil { + // No MR fields in description, construct from title/ID + return &MergeRequest{ + ID: issue.ID, + IssueID: issue.ID, + Status: MROpen, + CreatedAt: parseTime(issue.CreatedAt), + TargetBranch: "main", + } + } + + return &MergeRequest{ + ID: issue.ID, + Branch: fields.Branch, + Worker: fields.Worker, + IssueID: fields.SourceIssue, + TargetBranch: fields.Target, + Status: MROpen, + CreatedAt: parseTime(issue.CreatedAt), + } +} + +// parseTime parses a time string, returning zero time on error. +func parseTime(s string) time.Time { + t, err := time.Parse(time.RFC3339, s) + if err != nil { + t, _ = time.Parse("2006-01-02T15:04:05Z", s) + } + return t +} + // discoverWorkBranches finds branches that look like polecat work. func (m *Manager) discoverWorkBranches() ([]string, error) { cmd := exec.Command("git", "branch", "-r", "--list", "origin/polecat/*") diff --git a/internal/templates/roles/refinery.md.tmpl b/internal/templates/roles/refinery.md.tmpl index c9424c42..48f951ef 100644 --- a/internal/templates/roles/refinery.md.tmpl +++ b/internal/templates/roles/refinery.md.tmpl @@ -198,12 +198,12 @@ gt mail inbox # Process each message: lifecycle requests, escalations ``` -**queue-scan**: Fetch remote and identify branches +**queue-scan**: Check beads merge queue (source of truth) ```bash -git fetch origin -git branch -r | grep polecat -gt refinery queue {{ .RigName }} +git fetch --prune origin +gt mq list {{ .RigName }} ``` +The beads MQ is the source of truth for pending merges, not git branches. If queue empty, skip to context-check step. **process-branch**: Pick next branch, rebase on main