refinery

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 22, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package refinery provides the merge queue processing agent.

Package refinery provides the merge queue processing agent.

Index

Constants

View Source
const (
	StateStopped = agent.StateStopped
	StateRunning = agent.StateRunning
	StatePaused  = agent.StatePaused
)

State constants - re-exported from agent package for backwards compatibility.

Variables

View Source
var (
	ErrNotRunning     = errors.New("refinery not running")
	ErrAlreadyRunning = errors.New("refinery already running")
	ErrNoQueue        = errors.New("no items in queue")
)

Common errors

View Source
var (
	ErrMRNotFound  = errors.New("merge request not found")
	ErrMRNotFailed = errors.New("merge request has not failed")
)

Common errors for MR operations

View Source
var (
	// ErrInvalidTransition is returned when a state transition is not allowed.
	ErrInvalidTransition = errors.New("invalid state transition")

	// ErrClosedImmutable is returned when attempting to change a closed MR.
	ErrClosedImmutable = errors.New("closed merge requests are immutable")
)

State transition errors.

Functions

func ValidateTransition

func ValidateTransition(from, to MRStatus) error

ValidateTransition checks if a state transition from -> to is valid.

Valid transitions:

  • open → in_progress (Engineer claims MR)
  • in_progress → closed (merge success or rejection)
  • in_progress → open (failure, reassign to worker)
  • open → closed (manual rejection)

Invalid:

  • closed → anything (immutable once closed)

Types

type CloseReason

type CloseReason string

CloseReason indicates why a merge request was closed.

const (
	// CloseReasonMerged means the MR was successfully merged.
	CloseReasonMerged CloseReason = "merged"

	// CloseReasonRejected means the MR was manually rejected.
	CloseReasonRejected CloseReason = "rejected"

	// CloseReasonConflict means the MR had unresolvable conflicts.
	CloseReasonConflict CloseReason = "conflict"

	// CloseReasonSuperseded means the MR was replaced by another.
	CloseReasonSuperseded CloseReason = "superseded"
)

type Engineer

type Engineer struct {
	// contains filtered or unexported fields
}

Engineer is the merge queue processor that polls for ready merge-requests and processes them according to the merge queue design.

func NewEngineer

func NewEngineer(r *rig.Rig) *Engineer

NewEngineer creates a new Engineer for the given rig.

func (*Engineer) Config

func (e *Engineer) Config() *MergeQueueConfig

Config returns the current merge queue configuration.

func (*Engineer) IsBeadOpen

func (e *Engineer) IsBeadOpen(beadID string) (bool, error)

IsBeadOpen checks if a bead is still open (not closed). This is used as a status checker for mrqueue.ListReady to filter blocked MRs.

func (*Engineer) ListBlockedMRs

func (e *Engineer) ListBlockedMRs() ([]*mrqueue.MR, error)

ListBlockedMRs returns MRs that are blocked by open tasks. Useful for monitoring/reporting.

func (*Engineer) ListReadyMRs

func (e *Engineer) ListReadyMRs() ([]*mrqueue.MR, error)

ListReadyMRs returns MRs that are ready for processing: - Not claimed by another worker (or claim is stale) - Not blocked by an open task Sorted by priority score (highest first).

func (*Engineer) LoadConfig

func (e *Engineer) LoadConfig() error

LoadConfig loads merge queue configuration from the rig's config.json.

func (*Engineer) ProcessMR

func (e *Engineer) ProcessMR(ctx context.Context, mr *beads.Issue) ProcessResult

ProcessMR processes a single merge request from a beads issue.

func (*Engineer) ProcessMRFromQueue

func (e *Engineer) ProcessMRFromQueue(ctx context.Context, mr *mrqueue.MR) ProcessResult

ProcessMRFromQueue processes a merge request from wisp queue.

func (*Engineer) SetOutput

func (e *Engineer) SetOutput(w io.Writer)

SetOutput sets the output writer for user-facing messages. This is useful for testing or redirecting output.

type FailureType

type FailureType string

FailureType categorizes merge failures for appropriate handling.

const (
	// FailureNone indicates no failure (success).
	FailureNone FailureType = ""

	// FailureConflict indicates merge conflicts with target branch.
	FailureConflict FailureType = "conflict"

	// FailureTestsFail indicates tests failed after merge.
	FailureTestsFail FailureType = "tests_fail"

	// FailureBuildFail indicates build failed after merge.
	FailureBuildFail FailureType = "build_fail"

	// FailureFlakyTest indicates a potentially flaky test failure (may retry).
	FailureFlakyTest FailureType = "flaky_test"

	// FailurePushFail indicates push to remote failed.
	FailurePushFail FailureType = "push_fail"

	// FailureFetch indicates fetch of source branch failed.
	FailureFetch FailureType = "fetch_fail"

	// FailureCheckout indicates checkout of target branch failed.
	FailureCheckout FailureType = "checkout_fail"
)

func (FailureType) FailureLabel

func (f FailureType) FailureLabel() string

FailureLabel returns the beads label for this failure type.

func (FailureType) ShouldAssignToWorker

func (f FailureType) ShouldAssignToWorker() bool

ShouldAssignToWorker returns true if this failure should be assigned back to the worker.

type MRStatus

type MRStatus string

MRStatus represents the status of a merge request. Uses beads-style statuses for consistency with the issue tracking system.

const (
	// MROpen means the MR is waiting to be processed or needs rework.
	MROpen MRStatus = "open"

	// MRInProgress means the MR is currently being merged by the Engineer.
	MRInProgress MRStatus = "in_progress"

	// MRClosed means the MR processing is complete (merged, rejected, etc).
	MRClosed MRStatus = "closed"
)

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles refinery lifecycle and queue operations.

func NewManager

func NewManager(r *rig.Rig) *Manager

NewManager creates a new refinery manager for a rig.

func (*Manager) FindMR

func (m *Manager) FindMR(idOrBranch string) (*MergeRequest, error)

FindMR finds a merge request by ID or branch name in the queue.

func (*Manager) GetMR

func (m *Manager) GetMR(id string) (*MergeRequest, error)

GetMR returns a merge request by ID from the state.

func (*Manager) ProcessMR deprecated

func (m *Manager) ProcessMR(mr *MergeRequest) MergeResult

ProcessMR is deprecated - the Refinery agent now handles all merge processing.

ZFC #5: Move merge/conflict decisions from Go to Refinery agent

The agent runs git commands directly and makes decisions based on output:

  • Agent attempts merge: git checkout -b temp origin/polecat/<worker>
  • Agent detects conflict and decides: retry, notify polecat, escalate
  • Agent runs tests and decides: proceed, rollback, retry
  • Agent pushes: git push origin main

This function is kept for backwards compatibility but always returns an error indicating that the agent should handle merge processing.

Deprecated: Use the Refinery agent (Claude) for merge processing.

func (*Manager) Queue

func (m *Manager) Queue() ([]QueueItem, error)

Queue returns the current merge queue. Uses beads merge-request issues as the source of truth (not git branches).

func (*Manager) RegisterMR

func (m *Manager) RegisterMR(mr *MergeRequest) error

RegisterMR adds a merge request to the pending queue.

func (*Manager) RejectMR

func (m *Manager) RejectMR(idOrBranch string, reason string, notify bool) (*MergeRequest, error)

RejectMR manually rejects a merge request. It closes the MR with rejected status and optionally notifies the worker. Returns the rejected MR for display purposes.

func (*Manager) Retry

func (m *Manager) Retry(id string, processNow bool) error

Retry resets a failed merge request so it can be processed again. The processNow parameter is deprecated - the Refinery agent handles processing. Clearing the error is sufficient; the agent will pick up the MR in its next patrol cycle.

func (*Manager) SessionName

func (m *Manager) SessionName() string

SessionName returns the tmux session name for this refinery.

func (*Manager) SetOutput

func (m *Manager) SetOutput(w io.Writer)

SetOutput sets the output writer for user-facing messages. This is useful for testing or redirecting output.

func (*Manager) Start

func (m *Manager) Start(foreground bool) error

Start starts the refinery. If foreground is true, runs in the current process (blocking) using the Go-based polling loop. Otherwise, spawns a Claude agent in a tmux session to process the merge queue.

func (*Manager) Status

func (m *Manager) Status() (*Refinery, error)

Status returns the current refinery status. ZFC-compliant: trusts agent-reported state, no PID/tmux inference. The daemon reads agent bead state for liveness checks.

func (*Manager) Stop

func (m *Manager) Stop() error

Stop stops the refinery.

type MergeConfig

type MergeConfig struct {
	// RunTests controls whether tests are run after merge.
	// Default: true
	RunTests bool `json:"run_tests"`

	// TestCommand is the command to run for testing.
	// Default: "go test ./..."
	TestCommand string `json:"test_command"`

	// DeleteMergedBranches controls whether merged branches are deleted.
	// Default: true
	DeleteMergedBranches bool `json:"delete_merged_branches"`

	// PushRetryCount is the number of times to retry a failed push.
	// Default: 3
	PushRetryCount int `json:"push_retry_count"`

	// PushRetryDelayMs is the base delay between push retries in milliseconds.
	// Each retry doubles the delay (exponential backoff).
	// Default: 1000
	PushRetryDelayMs int `json:"push_retry_delay_ms"`
}

MergeConfig contains configuration for the merge process.

func DefaultMergeConfig

func DefaultMergeConfig() MergeConfig

DefaultMergeConfig returns the default merge configuration.

type MergeQueueConfig

type MergeQueueConfig struct {
	// Enabled controls whether the merge queue is active.
	Enabled bool `json:"enabled"`

	// TargetBranch is the default branch to merge to (e.g., "main").
	TargetBranch string `json:"target_branch"`

	// IntegrationBranches enables per-epic integration branches.
	IntegrationBranches bool `json:"integration_branches"`

	// OnConflict is the strategy for handling conflicts: "assign_back" or "auto_rebase".
	OnConflict string `json:"on_conflict"`

	// RunTests controls whether to run tests before merging.
	RunTests bool `json:"run_tests"`

	// TestCommand is the command to run for testing.
	TestCommand string `json:"test_command"`

	// DeleteMergedBranches controls whether to delete branches after merge.
	DeleteMergedBranches bool `json:"delete_merged_branches"`

	// RetryFlakyTests is the number of times to retry flaky tests.
	RetryFlakyTests int `json:"retry_flaky_tests"`

	// PollInterval is how often to check for new MRs.
	PollInterval time.Duration `json:"poll_interval"`

	// MaxConcurrent is the maximum number of MRs to process concurrently.
	MaxConcurrent int `json:"max_concurrent"`
}

MergeQueueConfig holds configuration for the merge queue processor.

func DefaultMergeQueueConfig

func DefaultMergeQueueConfig() *MergeQueueConfig

DefaultMergeQueueConfig returns sensible defaults for merge queue configuration.

type MergeRequest

type MergeRequest struct {
	// ID is a unique identifier for this merge request.
	ID string `json:"id"`

	// Branch is the source branch name (e.g., "polecat/Toast/gt-abc").
	Branch string `json:"branch"`

	// Worker is the polecat that created this branch.
	Worker string `json:"worker"`

	// IssueID is the beads issue being worked on.
	IssueID string `json:"issue_id"`

	// SwarmID is the swarm this work belongs to (if any).
	SwarmID string `json:"swarm_id,omitempty"`

	// TargetBranch is where this should merge (usually integration or main).
	TargetBranch string `json:"target_branch"`

	// CreatedAt is when the MR was queued.
	CreatedAt time.Time `json:"created_at"`

	// Status is the current status of the merge request.
	Status MRStatus `json:"status"`

	// CloseReason indicates why the MR was closed (only set when Status=closed).
	CloseReason CloseReason `json:"close_reason,omitempty"`

	// Error contains error details if the MR failed.
	Error string `json:"error,omitempty"`
}

MergeRequest represents a branch waiting to be merged.

func (*MergeRequest) Claim

func (mr *MergeRequest) Claim() error

Claim transitions the MR from open to in_progress (Engineer claims it). Returns an error if the transition is not allowed.

func (*MergeRequest) Close

func (mr *MergeRequest) Close(reason CloseReason) error

Close closes the MR with the given reason after validating the transition. Returns an error if the MR cannot be closed from its current state. Once closed, an MR cannot be closed again (even with a different reason).

func (*MergeRequest) IsClosed

func (mr *MergeRequest) IsClosed() bool

IsClosed returns true if the MR is in a closed state.

func (*MergeRequest) IsInProgress

func (mr *MergeRequest) IsInProgress() bool

IsInProgress returns true if the MR is currently being processed.

func (*MergeRequest) IsOpen

func (mr *MergeRequest) IsOpen() bool

IsOpen returns true if the MR is in an open state (waiting for processing).

func (*MergeRequest) Reopen

func (mr *MergeRequest) Reopen() error

Reopen reopens a failed MR (transitions from in_progress back to open). Returns an error if the transition is not allowed.

func (*MergeRequest) SetStatus

func (mr *MergeRequest) SetStatus(newStatus MRStatus) error

SetStatus updates the MR status after validating the transition. Returns an error if the transition is not allowed.

type MergeResult

type MergeResult struct {
	Success     bool
	MergeCommit string // SHA of merge commit on success
	Error       string
	Conflict    bool
	TestsFailed bool
}

MergeResult contains the result of a merge attempt.

type ProcessResult

type ProcessResult struct {
	Success     bool
	MergeCommit string
	Error       string
	Conflict    bool
	TestsFailed bool
}

ProcessResult contains the result of processing a merge request.

type QueueItem

type QueueItem struct {
	Position int           `json:"position"`
	MR       *MergeRequest `json:"mr"`
	Age      string        `json:"age"`
}

QueueItem represents an item in the merge queue for display.

type Refinery

type Refinery struct {
	// RigName is the rig this refinery processes.
	RigName string `json:"rig_name"`

	// State is the current running state.
	State State `json:"state"`

	// PID is the process ID if running in background.
	PID int `json:"pid,omitempty"`

	// StartedAt is when the refinery was started.
	StartedAt *time.Time `json:"started_at,omitempty"`

	// CurrentMR is the merge request currently being processed.
	CurrentMR *MergeRequest `json:"current_mr,omitempty"`

	// PendingMRs tracks merge requests that have been submitted.
	// Key is the MR ID.
	PendingMRs map[string]*MergeRequest `json:"pending_mrs,omitempty"`

	// LastMergeAt is when the last successful merge happened.
	LastMergeAt *time.Time `json:"last_merge_at,omitempty"`
}

Refinery represents a rig's merge queue processor.

type State

type State = agent.State

State is an alias for agent.State for backwards compatibility.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL