feat(mq): Add priority-ordered queue display and processing (gt-si8rq.6)

Implements priority scoring for merge queue ordering:

## Changes to gt mq list
- Add SCORE column showing priority score (higher = process first)
- Sort MRs by score descending instead of simple priority
- Add CONVOY column showing convoy ID if tracked

## New gt mq next command
- Returns highest-score MR ready for processing
- Supports --strategy=fifo for FIFO ordering fallback
- Supports --quiet for just printing MR ID
- Supports --json for programmatic access

## Changes to Refinery
- Queue() now sorts by priority score instead of simple priority
- Uses ScoreMR from mrqueue package for consistent scoring

## MR Fields Extended
- Added retry_count, last_conflict_sha, conflict_task_id
- Added convoy_id, convoy_created_at for convoy tracking
- These fields feed into priority scoring function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nux
2026-01-02 17:16:29 -08:00
committed by Steve Yegge
parent 1e1221883b
commit 8517ff0650
4 changed files with 388 additions and 37 deletions

View File

@@ -1,7 +1,10 @@
// Package beads provides field parsing utilities for structured issue descriptions.
package beads
import "strings"
import (
"fmt"
"strings"
)
// Note: AgentFields, ParseAgentFields, FormatAgentDescription, and CreateAgentBead are in beads.go
@@ -165,6 +168,15 @@ type MRFields struct {
MergeCommit string // SHA of merge commit (set on close)
CloseReason string // Reason for closing: merged, rejected, conflict, superseded
AgentBead string // Agent bead ID that created this MR (for traceability)
// Conflict resolution fields (for priority scoring)
RetryCount int // Number of conflict-resolution cycles
LastConflictSHA string // SHA of main when conflict occurred
ConflictTaskID string // Link to conflict-resolution task (if any)
// Convoy tracking (for priority scoring - convoy starvation prevention)
ConvoyID string // Parent convoy ID if part of a convoy
ConvoyCreatedAt string // Convoy creation time (ISO 8601) for starvation prevention
}
// ParseMRFields extracts structured merge-request fields from an issue's description.
@@ -222,6 +234,23 @@ func ParseMRFields(issue *Issue) *MRFields {
case "agent_bead", "agent-bead", "agentbead":
fields.AgentBead = value
hasFields = true
case "retry_count", "retry-count", "retrycount":
if n, err := parseIntField(value); err == nil {
fields.RetryCount = n
hasFields = true
}
case "last_conflict_sha", "last-conflict-sha", "lastconflictsha":
fields.LastConflictSHA = value
hasFields = true
case "conflict_task_id", "conflict-task-id", "conflicttaskid":
fields.ConflictTaskID = value
hasFields = true
case "convoy_id", "convoy-id", "convoyid", "convoy":
fields.ConvoyID = value
hasFields = true
case "convoy_created_at", "convoy-created-at", "convoycreatedat":
fields.ConvoyCreatedAt = value
hasFields = true
}
}
@@ -231,6 +260,13 @@ func ParseMRFields(issue *Issue) *MRFields {
return fields
}
// parseIntField parses an integer from a string, returning 0 on error.
func parseIntField(s string) (int, error) {
var n int
_, err := fmt.Sscanf(s, "%d", &n)
return n, err
}
// FormatMRFields formats MRFields as a string suitable for an issue description.
// Only non-empty fields are included.
func FormatMRFields(fields *MRFields) string {
@@ -264,6 +300,21 @@ func FormatMRFields(fields *MRFields) string {
if fields.AgentBead != "" {
lines = append(lines, "agent_bead: "+fields.AgentBead)
}
if fields.RetryCount > 0 {
lines = append(lines, fmt.Sprintf("retry_count: %d", fields.RetryCount))
}
if fields.LastConflictSHA != "" {
lines = append(lines, "last_conflict_sha: "+fields.LastConflictSHA)
}
if fields.ConflictTaskID != "" {
lines = append(lines, "conflict_task_id: "+fields.ConflictTaskID)
}
if fields.ConvoyID != "" {
lines = append(lines, "convoy_id: "+fields.ConvoyID)
}
if fields.ConvoyCreatedAt != "" {
lines = append(lines, "convoy_created_at: "+fields.ConvoyCreatedAt)
}
return strings.Join(lines, "\n")
}
@@ -278,22 +329,38 @@ func SetMRFields(issue *Issue, fields *MRFields) string {
// Known MR field keys (lowercase)
mrKeys := map[string]bool{
"branch": true,
"target": true,
"source_issue": true,
"source-issue": true,
"sourceissue": true,
"worker": true,
"rig": true,
"merge_commit": true,
"merge-commit": true,
"mergecommit": true,
"close_reason": true,
"close-reason": true,
"closereason": true,
"agent_bead": true,
"agent-bead": true,
"agentbead": true,
"branch": true,
"target": true,
"source_issue": true,
"source-issue": true,
"sourceissue": true,
"worker": true,
"rig": true,
"merge_commit": true,
"merge-commit": true,
"mergecommit": true,
"close_reason": true,
"close-reason": true,
"closereason": true,
"agent_bead": true,
"agent-bead": true,
"agentbead": true,
"retry_count": true,
"retry-count": true,
"retrycount": true,
"last_conflict_sha": true,
"last-conflict-sha": true,
"lastconflictsha": true,
"conflict_task_id": true,
"conflict-task-id": true,
"conflicttaskid": true,
"convoy_id": true,
"convoy-id": true,
"convoyid": true,
"convoy": true,
"convoy_created_at": true,
"convoy-created-at": true,
"convoycreatedat": true,
}
// Collect non-MR lines from existing description