Documentation
¶
Overview ¶
Package polecat provides polecat lifecycle management.
Package polecat provides polecat lifecycle management.
Index ¶
- Constants
- Variables
- func GetThemeNames(theme string) ([]string, error)
- func ListThemes() []string
- func PendingFile(townRoot string) string
- func PruneStalePending(townRoot string, maxAge time.Duration) (int, error)
- func SavePending(townRoot string, pending []*PendingSpawn) error
- type AddOptions
- type Manager
- func (m *Manager) Add(name string) (*Polecat, error)
- func (m *Manager) AddWithOptions(name string, opts AddOptions) (*Polecat, error)
- func (m *Manager) AllocateName() (string, error)
- func (m *Manager) AssignIssue(name, issue string) error
- func (m *Manager) CleanupStaleBranches() (int, error)
- func (m *Manager) ClearIssue(name string) error
- func (m *Manager) Get(name string) (*Polecat, error)
- func (m *Manager) List() ([]*Polecat, error)
- func (m *Manager) PoolStatus() (active int, names []string)
- func (m *Manager) ReconcilePool()
- func (m *Manager) ReleaseName(name string)
- func (m *Manager) Remove(name string, force bool) error
- func (m *Manager) RemoveWithOptions(name string, force, nuclear bool) error
- func (m *Manager) RepairWorktree(name string, force bool) (*Polecat, error)
- func (m *Manager) RepairWorktreeWithOptions(name string, force bool, opts AddOptions) (*Polecat, error)
- func (m *Manager) SetState(name string, state State) error
- type NamePool
- func (p *NamePool) ActiveCount() int
- func (p *NamePool) ActiveNames() []string
- func (p *NamePool) AddCustomName(name string)
- func (p *NamePool) Allocate() (string, error)
- func (p *NamePool) GetTheme() string
- func (p *NamePool) IsPoolName(name string) bool
- func (p *NamePool) Load() error
- func (p *NamePool) MarkInUse(name string)
- func (p *NamePool) Reconcile(existingPolecats []string)
- func (p *NamePool) Release(name string)
- func (p *NamePool) Reset()
- func (p *NamePool) Save() error
- func (p *NamePool) SetTheme(theme string) error
- type PendingSpawn
- type Polecat
- type State
- type Summary
- type TriggerResult
- type UncommittedWorkError
Constants ¶
const ( // DefaultPoolSize is the number of reusable names in the pool. DefaultPoolSize = 50 // DefaultTheme is the default theme for new rigs. DefaultTheme = "mad-max" )
Variables ¶
var ( ErrPolecatExists = errors.New("polecat already exists") ErrPolecatNotFound = errors.New("polecat not found") ErrHasChanges = errors.New("polecat has uncommitted changes") ErrHasUncommittedWork = errors.New("polecat has uncommitted work") )
Common errors
var BuiltinThemes = map[string][]string{
"mad-max": {
"furiosa", "nux", "slit", "rictus", "dementus",
"capable", "toast", "dag", "cheedo", "valkyrie",
"keeper", "morsov", "ace", "warboy", "imperator",
"organic", "coma", "splendid", "angharad", "max",
"immortan", "bullet", "toecutter", "goose", "nightrider",
"glory", "scrotus", "chumbucket", "corpus", "dinki",
"prime", "vuvalini", "rockryder", "wretched", "buzzard",
"gastown", "bullet-farmer", "citadel", "wasteland", "fury",
"road-warrior", "interceptor", "blackfinger", "wraith", "witness",
"chrome", "shiny", "mediocre", "guzzoline", "aqua-cola",
},
"minerals": {
"obsidian", "quartz", "jasper", "onyx", "opal",
"topaz", "garnet", "ruby", "amber", "jade",
"pearl", "flint", "granite", "basalt", "marble",
"shale", "slate", "pyrite", "mica", "agate",
"malachite", "turquoise", "lapis", "emerald", "sapphire",
"diamond", "amethyst", "citrine", "zircon", "peridot",
"coral", "jet", "moonstone", "sunstone", "bloodstone",
"rhodonite", "sodalite", "hematite", "magnetite", "calcite",
"fluorite", "selenite", "kyanite", "labradorite", "amazonite",
"chalcedony", "carnelian", "aventurine", "chrysoprase", "heliodor",
},
"wasteland": {
"rust", "chrome", "nitro", "guzzle", "witness",
"shiny", "fury", "thunder", "dust", "scavenger",
"radrat", "ghoul", "mutant", "raider", "vault",
"pipboy", "nuka", "brahmin", "deathclaw", "mirelurk",
"synth", "institute", "enclave", "brotherhood", "minuteman",
"railroad", "atom", "crater", "foundation", "refuge",
"settler", "wanderer", "courier", "lone", "chosen",
"tribal", "khan", "legion", "ncr", "ranger",
"overseer", "sentinel", "paladin", "scribe", "initiate",
"elder", "lancer", "knight", "squire", "proctor",
},
}
Built-in themes with themed polecat names.
Functions ¶
func GetThemeNames ¶
GetThemeNames returns the names in a specific theme.
func ListThemes ¶
func ListThemes() []string
ListThemes returns the list of available built-in themes.
func PendingFile ¶
PendingFile returns the path to the pending spawns file.
func PruneStalePending ¶
PruneStalePending removes pending spawns older than the given age. Spawns that are too old likely had their sessions die.
func SavePending ¶
func SavePending(townRoot string, pending []*PendingSpawn) error
SavePending saves the pending spawns to disk.
Types ¶
type AddOptions ¶
type AddOptions struct {
HookBead string // Bead ID to set as hook_bead at spawn time (atomic assignment)
}
AddOptions configures polecat creation.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles polecat lifecycle.
func NewManager ¶
NewManager creates a new polecat manager.
func (*Manager) Add ¶
Add creates a new polecat as a git worktree from the repo base. Uses the shared bare repo (.repo.git) if available, otherwise mayor/rig. This is much faster than a full clone and shares objects with all worktrees. Polecat state is derived from beads assignee field, not state.json.
Branch naming: Each polecat run gets a unique branch (polecat/<name>-<timestamp>). This prevents drift issues from stale branches and ensures a clean starting state. Old branches are ephemeral and never pushed to origin.
func (*Manager) AddWithOptions ¶
func (m *Manager) AddWithOptions(name string, opts AddOptions) (*Polecat, error)
AddWithOptions creates a new polecat with the specified options. This allows setting hook_bead atomically at creation time, avoiding cross-beads routing issues when slinging work to new polecats.
func (*Manager) AllocateName ¶
AllocateName allocates a name from the name pool. Returns a pooled name (polecat-01 through polecat-50) if available, otherwise returns an overflow name (rigname-N).
func (*Manager) AssignIssue ¶
AssignIssue assigns an issue to a polecat by setting the issue's assignee in beads.
func (*Manager) CleanupStaleBranches ¶
CleanupStaleBranches removes orphaned polecat branches that are no longer in use. This includes: - Branches for polecats that no longer exist - Old timestamped branches (keeps only the most recent per polecat name) Returns the number of branches deleted.
func (*Manager) ClearIssue ¶
ClearIssue removes the issue assignment from a polecat. In the transient model, this transitions to Done state for cleanup. This clears the assignee from the currently assigned issue in beads. If beads is not available, this is a no-op.
func (*Manager) Get ¶
Get returns a specific polecat by name. State is derived from beads assignee field: - If an issue is assigned to this polecat: StateWorking - If no issue assigned: StateDone (ready for cleanup - transient polecats should have work)
func (*Manager) PoolStatus ¶
PoolStatus returns information about the name pool.
func (*Manager) ReconcilePool ¶
func (m *Manager) ReconcilePool()
ReconcilePool syncs pool state with existing polecat directories. This should be called to recover from crashes or stale state.
func (*Manager) ReleaseName ¶
ReleaseName releases a name back to the pool. This is called when a polecat is removed.
func (*Manager) Remove ¶
Remove deletes a polecat worktree. If force is true, removes even with uncommitted changes (but not stashes/unpushed). Use nuclear=true to bypass ALL safety checks.
func (*Manager) RemoveWithOptions ¶
RemoveWithOptions deletes a polecat worktree with explicit control over safety checks. force=true: bypass uncommitted changes check (legacy behavior) nuclear=true: bypass ALL safety checks including stashes and unpushed commits
ZFC #10: Uses cleanup_status from agent bead if available (polecat self-report), falls back to git check for backward compatibility.
func (*Manager) RepairWorktree ¶
RepairWorktree repairs a stale polecat by removing it and creating a fresh worktree. This is NOT for normal operation - it handles reconciliation when AllocateName returns a name that unexpectedly already exists (stale state recovery).
The polecat starts with the latest code from origin/<default-branch>. The name is preserved (not released to pool) since we're repairing immediately. force controls whether to bypass uncommitted changes check.
Branch naming: Each repair gets a unique branch (polecat/<name>-<timestamp>). Old branches are left for garbage collection - they're never pushed to origin.
func (*Manager) RepairWorktreeWithOptions ¶
func (m *Manager) RepairWorktreeWithOptions(name string, force bool, opts AddOptions) (*Polecat, error)
RepairWorktreeWithOptions repairs a stale polecat and creates a fresh worktree with options. This is NOT for normal operation - see RepairWorktree for context. Allows setting hook_bead atomically at repair time.
func (*Manager) SetState ¶
SetState updates a polecat's state. In the beads model, state is derived from issue status: - StateWorking/StateActive: issue status set to in_progress - StateDone: assignee cleared from issue (polecat ready for cleanup) - StateStuck: issue status set to blocked (if supported) If beads is not available, this is a no-op.
type NamePool ¶
type NamePool struct {
// RigName is the rig this pool belongs to.
RigName string `json:"rig_name"`
// Theme is the current theme name (e.g., "mad-max", "minerals").
Theme string `json:"theme"`
// CustomNames allows overriding the built-in theme names.
CustomNames []string `json:"custom_names,omitempty"`
// InUse tracks which pool names are currently in use.
// Key is the name itself, value is true if in use.
InUse map[string]bool `json:"in_use"`
// OverflowNext is the next overflow sequence number.
// Starts at MaxSize+1 and increments.
OverflowNext int `json:"overflow_next"`
// MaxSize is the maximum number of themed names before overflow.
MaxSize int `json:"max_size"`
// contains filtered or unexported fields
}
NamePool manages a bounded pool of reusable polecat names. Names are drawn from a themed pool (mad-max by default). When the pool is exhausted, overflow names use rigname-N format.
func NewNamePool ¶
NewNamePool creates a new name pool for a rig.
func NewNamePoolWithConfig ¶
func NewNamePoolWithConfig(rigPath, rigName, theme string, customNames []string, maxSize int) *NamePool
NewNamePoolWithConfig creates a name pool with specific configuration.
func (*NamePool) ActiveCount ¶
ActiveCount returns the number of names currently in use from the pool.
func (*NamePool) ActiveNames ¶
ActiveNames returns a sorted list of names currently in use from the pool.
func (*NamePool) AddCustomName ¶
AddCustomName adds a custom name to the pool.
func (*NamePool) Allocate ¶
Allocate returns a name from the pool. It prefers names in order from the theme list, and falls back to overflow names when the pool is exhausted.
func (*NamePool) IsPoolName ¶
IsPoolName returns true if the name is a pool name (themed or numbered).
func (*NamePool) MarkInUse ¶
MarkInUse marks a name as in use (for reconciling with existing polecats).
func (*NamePool) Reconcile ¶
Reconcile updates the pool state based on existing polecat directories. This should be called on startup to sync pool state with reality.
func (*NamePool) Release ¶
Release returns a pooled name to the pool. For overflow names, this is a no-op (they are not reusable).
func (*NamePool) Reset ¶
func (p *NamePool) Reset()
Reset clears the pool state, releasing all names.
type PendingSpawn ¶
type PendingSpawn struct {
// Rig is the rig name (e.g., "gastown")
Rig string `json:"rig"`
// Polecat is the polecat name (e.g., "p-abc123")
Polecat string `json:"polecat"`
// Session is the tmux session name
Session string `json:"session"`
// Issue is the assigned issue ID
Issue string `json:"issue"`
// SpawnedAt is when the spawn was detected
SpawnedAt time.Time `json:"spawned_at"`
// MailID is the ID of the POLECAT_STARTED message
MailID string `json:"mail_id"`
}
PendingSpawn represents a polecat that has been spawned but not yet triggered.
func CheckInboxForSpawns ¶
func CheckInboxForSpawns(townRoot string) ([]*PendingSpawn, error)
CheckInboxForSpawns reads the Deacon's inbox for POLECAT_STARTED messages and adds them to the pending list.
func LoadPending ¶
func LoadPending(townRoot string) ([]*PendingSpawn, error)
LoadPending loads the pending spawns from disk.
type Polecat ¶
type Polecat struct {
// Name is the polecat identifier.
Name string `json:"name"`
// Rig is the rig this polecat belongs to.
Rig string `json:"rig"`
// State is the current lifecycle state.
State State `json:"state"`
// ClonePath is the path to the polecat's clone of the rig.
ClonePath string `json:"clone_path"`
// Branch is the current git branch.
Branch string `json:"branch"`
// Issue is the currently assigned issue ID (if any).
Issue string `json:"issue,omitempty"`
// CreatedAt is when the polecat was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is when the polecat was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
Polecat represents a worker agent in a rig.
type State ¶
type State string
State represents the current state of a polecat. In the transient model, polecats exist only while working.
const ( // StateWorking means the polecat is actively working on an issue. // This is the initial and primary state for transient polecats. StateWorking State = "working" // StateDone means the polecat has completed its assigned work // and is ready for cleanup by the Witness. StateDone State = "done" // StateStuck means the polecat needs assistance. StateStuck State = "stuck" // StateActive is deprecated: use StateWorking. // Kept only for backward compatibility with existing data. StateActive State = "active" )
type Summary ¶
type Summary struct {
Name string `json:"name"`
State State `json:"state"`
Issue string `json:"issue,omitempty"`
}
Summary provides a concise view of polecat status.
type TriggerResult ¶
type TriggerResult struct {
Spawn *PendingSpawn
Triggered bool
Error error
}
TriggerResult holds the result of attempting to trigger a pending spawn.
func TriggerPendingSpawns ¶
func TriggerPendingSpawns(townRoot string, timeout time.Duration) ([]TriggerResult, error)
TriggerPendingSpawns polls each pending spawn and triggers when ready. Returns the spawns that were successfully triggered.
type UncommittedWorkError ¶
type UncommittedWorkError struct {
PolecatName string
Status *git.UncommittedWorkStatus
}
UncommittedWorkError provides details about uncommitted work.
func (*UncommittedWorkError) Error ¶
func (e *UncommittedWorkError) Error() string
func (*UncommittedWorkError) Unwrap ¶
func (e *UncommittedWorkError) Unwrap() error