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
This commit is contained in:
@@ -99,27 +99,28 @@ id = "queue-scan"
|
|||||||
title = "Scan merge queue"
|
title = "Scan merge queue"
|
||||||
needs = ["inbox-check"]
|
needs = ["inbox-check"]
|
||||||
description = """
|
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
|
```bash
|
||||||
# Always fetch with prune to clean stale tracking refs:
|
|
||||||
git fetch --prune origin
|
git fetch --prune origin
|
||||||
|
gt mq list <rig>
|
||||||
# For each queued merge request, verify the branch exists:
|
|
||||||
git branch -r | grep <branch>
|
|
||||||
|
|
||||||
# IMPORTANT: Before queuing, verify branch has unmerged commits:
|
|
||||||
git log origin/main..origin/<branch> --oneline | head -1
|
|
||||||
# If empty → branch already merged. Archive MERGE_READY, don't queue.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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 queue empty, skip to context-check step.
|
||||||
|
|
||||||
If branch doesn't exist for a queued item:
|
For each MR in the queue, verify the branch still exists:
|
||||||
- Mail witness: Branch not found, cannot merge
|
```bash
|
||||||
- Remove from queue
|
git branch -r | grep <branch>
|
||||||
|
```
|
||||||
|
|
||||||
Track verified branch list for this cycle."""
|
If branch doesn't exist for a queued MR:
|
||||||
|
- Close the MR bead: `bd close <mr-id> --reason "Branch no longer exists"`
|
||||||
|
- Remove from processing queue
|
||||||
|
|
||||||
|
Track verified MR list for this cycle."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "process-branch"
|
id = "process-branch"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/claude"
|
"github.com/steveyegge/gastown/internal/claude"
|
||||||
"github.com/steveyegge/gastown/internal/config"
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/events"
|
"github.com/steveyegge/gastown/internal/events"
|
||||||
@@ -239,14 +240,21 @@ func (m *Manager) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Queue returns the current merge queue.
|
// 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) {
|
func (m *Manager) Queue() ([]QueueItem, error) {
|
||||||
// Discover branches that look like polecat work branches
|
// Query beads for open merge-request type issues
|
||||||
branches, err := m.discoverWorkBranches()
|
// 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 {
|
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()
|
ref, err := m.loadState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -265,10 +273,14 @@ func (m *Manager) Queue() ([]QueueItem, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add discovered branches as pending
|
// Convert beads issues to queue items
|
||||||
for _, branch := range branches {
|
for _, issue := range issues {
|
||||||
mr := m.branchToMR(branch)
|
mr := m.issueToBead(issue)
|
||||||
if mr != nil {
|
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{
|
items = append(items, QueueItem{
|
||||||
Position: pos,
|
Position: pos,
|
||||||
MR: mr,
|
MR: mr,
|
||||||
@@ -281,6 +293,44 @@ func (m *Manager) Queue() ([]QueueItem, error) {
|
|||||||
return items, nil
|
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.
|
// discoverWorkBranches finds branches that look like polecat work.
|
||||||
func (m *Manager) discoverWorkBranches() ([]string, error) {
|
func (m *Manager) discoverWorkBranches() ([]string, error) {
|
||||||
cmd := exec.Command("git", "branch", "-r", "--list", "origin/polecat/*")
|
cmd := exec.Command("git", "branch", "-r", "--list", "origin/polecat/*")
|
||||||
|
|||||||
@@ -198,12 +198,12 @@ gt mail inbox
|
|||||||
# Process each message: lifecycle requests, escalations
|
# Process each message: lifecycle requests, escalations
|
||||||
```
|
```
|
||||||
|
|
||||||
**queue-scan**: Fetch remote and identify branches
|
**queue-scan**: Check beads merge queue (source of truth)
|
||||||
```bash
|
```bash
|
||||||
git fetch origin
|
git fetch --prune origin
|
||||||
git branch -r | grep polecat
|
gt mq list {{ .RigName }}
|
||||||
gt refinery queue {{ .RigName }}
|
|
||||||
```
|
```
|
||||||
|
The beads MQ is the source of truth for pending merges, not git branches.
|
||||||
If queue empty, skip to context-check step.
|
If queue empty, skip to context-check step.
|
||||||
|
|
||||||
**process-branch**: Pick next branch, rebase on main
|
**process-branch**: Pick next branch, rebase on main
|
||||||
|
|||||||
Reference in New Issue
Block a user