Documentation
¶
Overview ¶
Package polecat provides polecat workspace and session management.
Package polecat provides polecat lifecycle management.
Index ¶
- Constants
- Variables
- func GetThemeNames(theme string) ([]string, error)
- func IsSessionHeartbeatStale(townRoot, sessionName string) (stale bool, exists bool)
- func ListThemes() []string
- func RemoveSessionHeartbeat(townRoot, sessionName string)
- func ThemeForRig(rigName string) string
- func TouchSessionHeartbeat(townRoot, sessionName string)
- type AddOptions
- type CleanupStatus
- type Manager
- func (m *Manager) Add(name string) (*Polecat, error)
- func (m *Manager) AddWithOptions(name string, opts AddOptions) (_ *Polecat, retErr error)
- func (m *Manager) AllocateName() (string, error)
- func (m *Manager) AssignIssue(name, issue string) error
- func (m *Manager) CheckDoltHealth() error
- func (m *Manager) CheckDoltServerCapacity() error
- func (m *Manager) CleanupStaleBranches() (int, error)
- func (m *Manager) ClearIssue(name string) error
- func (m *Manager) ClonePath(name string) string
- func (m *Manager) DetectStalePolecats(threshold int) ([]*StalenessInfo, error)
- func (m *Manager) FindIdlePolecat() (*Polecat, error)
- func (m *Manager) Get(name string) (*Polecat, error)
- func (m *Manager) GetNamePool() *NamePool
- func (m *Manager) List() ([]*Polecat, error)
- func (m *Manager) PoolStatus() (active int, names []string)
- func (m *Manager) ReconcilePool()
- func (m *Manager) ReconcilePoolWith(namesWithDirs, namesWithSessions []string)
- func (m *Manager) ReleaseName(name string)
- func (m *Manager) Remove(name string, force bool) error
- func (m *Manager) RemoveWithOptions(name string, force, nuclear, selfNuke bool) (retErr 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) ReuseIdlePolecat(name string, opts AddOptions) (*Polecat, error)
- func (m *Manager) SetAgentState(name string, state string) error
- func (m *Manager) SetAgentStateWithRetry(name string, state string) 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 Polecat
- type SessionHeartbeat
- type SessionInfo
- type SessionManager
- func (m *SessionManager) Attach(polecat string) error
- func (m *SessionManager) Capture(polecat string, lines int) (string, error)
- func (m *SessionManager) CaptureSession(sessionID string, lines int) (string, error)
- func (m *SessionManager) Inject(polecat, message string) error
- func (m *SessionManager) IsRunning(polecat string) (bool, error)
- func (m *SessionManager) List() ([]SessionInfo, error)
- func (m *SessionManager) ListPolecats() ([]SessionInfo, error)
- func (m *SessionManager) SessionName(polecat string) string
- func (m *SessionManager) Start(polecat string, opts SessionStartOptions) error
- func (m *SessionManager) Status(polecat string) (*SessionInfo, error)
- func (m *SessionManager) Stop(polecat string, force bool) error
- func (m *SessionManager) StopAll(force bool) error
- type SessionStartOptions
- type StalenessInfo
- type State
- type Summary
- type UncommittedWorkError
Constants ¶
const ( // DefaultPoolSize is the number of name slots in the pool. // NOTE: This is a pool of NAMES, not polecats. Polecats are spawned fresh // for each task and nuked when done - there is no idle pool of polecats. // Only the name slots are reused when a polecat is nuked and a new one spawned. DefaultPoolSize = 50 // DefaultTheme is the default theme for new rigs. DefaultTheme = "mad-max" )
const SessionHeartbeatStaleThreshold = 3 * time.Minute
SessionHeartbeatStaleThreshold is the age at which a polecat session heartbeat is considered stale, indicating the agent process is likely dead. Configurable via operational.polecat.heartbeat_stale_threshold in settings/config.json.
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") ErrShellInWorktree = errors.New("shell working directory is inside polecat worktree") ErrDoltUnhealthy = errors.New("dolt health check failed") ErrDoltAtCapacity = errors.New("dolt server at connection capacity") )
Common errors
var ( ErrSessionRunning = errors.New("session already running") ErrSessionNotFound = errors.New("session not found") ErrIssueInvalid = errors.New("issue not found or tombstoned") )
Session 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.
var ReservedInfraAgentNames = map[string]bool{ "witness": true, "mayor": true, "deacon": true, "refinery": true, "crew": true, "polecats": true, }
ReservedInfraAgentNames contains names reserved for infrastructure agents. These names must never be allocated to polecats.
Functions ¶
func GetThemeNames ¶
GetThemeNames returns the names in a specific theme.
func IsSessionHeartbeatStale ¶ added in v0.9.0
IsSessionHeartbeatStale returns true if the session's heartbeat is older than the stale threshold, or if no heartbeat file exists.
When no heartbeat file exists, this returns false to avoid false positives during the rollout period where sessions may not yet be touching heartbeats. The caller should fall back to other liveness checks in that case.
func ListThemes ¶
func ListThemes() []string
ListThemes returns the list of available built-in themes.
func RemoveSessionHeartbeat ¶ added in v0.9.0
func RemoveSessionHeartbeat(townRoot, sessionName string)
RemoveSessionHeartbeat removes the heartbeat file for a session. Called during session cleanup.
func ThemeForRig ¶ added in v0.3.0
ThemeForRig returns a deterministic theme for a rig based on its name. This provides variety across rigs without requiring manual configuration.
func TouchSessionHeartbeat ¶ added in v0.9.0
func TouchSessionHeartbeat(townRoot, sessionName string)
TouchSessionHeartbeat writes or updates the heartbeat file for a polecat session. This is best-effort: errors are silently ignored because heartbeat signals are non-critical and should not interrupt gt commands.
Types ¶
type AddOptions ¶
type AddOptions struct {
HookBead string // Bead ID to set as hook_bead at spawn time (atomic assignment)
BaseBranch string // Override base branch for worktree (e.g., "origin/integration/gt-epic")
}
AddOptions configures polecat creation.
type CleanupStatus ¶ added in v0.2.2
type CleanupStatus string
CleanupStatus represents the git state of a polecat for cleanup decisions. The Witness uses this to determine whether it's safe to nuke a polecat worktree.
const ( // CleanupClean means the worktree has no uncommitted work and is safe to remove. CleanupClean CleanupStatus = "clean" // CleanupUncommitted means there are uncommitted changes in the worktree. CleanupUncommitted CleanupStatus = "has_uncommitted" // CleanupStash means there are stashed changes that would be lost. CleanupStash CleanupStatus = "has_stash" // CleanupUnpushed means there are commits not pushed to the remote. CleanupUnpushed CleanupStatus = "has_unpushed" // CleanupUnknown means the status could not be determined. CleanupUnknown CleanupStatus = "unknown" )
func (CleanupStatus) CanForceRemove ¶ added in v0.2.2
func (s CleanupStatus) CanForceRemove() bool
CanForceRemove returns true if the status allows forced removal. Uncommitted changes can be force-removed, but stashes and unpushed commits cannot.
func (CleanupStatus) IsSafe ¶ added in v0.2.2
func (s CleanupStatus) IsSafe() bool
IsSafe returns true if the status indicates it's safe to remove the worktree without losing any work.
func (CleanupStatus) RequiresRecovery ¶ added in v0.2.2
func (s CleanupStatus) RequiresRecovery() bool
RequiresRecovery returns true if the status indicates there is work that needs to be recovered before removal. This includes uncommitted changes, stashes, and unpushed commits.
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 ¶
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, retErr 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 themed pooled name (furiosa, nux, etc.) if available, otherwise returns an overflow name (just a number like "51"). The rig prefix is added by SessionName to create full session names like "gt-<rig>-51". After allocation, kills any lingering tmux session for the name (gt-pqf9x) to prevent "session already running" errors when reusing names from dead polecats.
func (*Manager) AssignIssue ¶
AssignIssue assigns an issue to a polecat by setting the issue's assignee in beads.
func (*Manager) CheckDoltHealth ¶ added in v0.6.0
CheckDoltHealth verifies that the Dolt database is reachable before spawning. Returns an error if Dolt exists but is unhealthy after retries. Returns nil if beads is not configured (test/setup environments). If read-only errors persist after retries, attempts server recovery (gt-chx92). Fails fast on configuration/initialization errors (gt-2ra).
func (*Manager) CheckDoltServerCapacity ¶ added in v0.6.0
CheckDoltServerCapacity verifies the Dolt server has capacity for new connections. This is an admission control gate: if the server is near its max_connections limit, spawning another polecat (which will make many bd calls) could overwhelm it. Returns nil if capacity is available, ErrDoltAtCapacity if the server is overloaded. Fails closed if the check errors — a server that can't report capacity is likely already under stress (gt-lfc0d).
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) DetectStalePolecats ¶ added in v0.2.1
func (m *Manager) DetectStalePolecats(threshold int) ([]*StalenessInfo, error)
DetectStalePolecats identifies polecats that are candidates for cleanup. A polecat is considered stale if: - No active tmux session AND - Either: way behind main (>threshold commits) OR no agent bead/activity - Has no uncommitted work that could be lost
threshold: minimum commits behind main to consider "way behind" (e.g., 20)
func (*Manager) FindIdlePolecat ¶ added in v0.9.0
FindIdlePolecat returns the first idle polecat in the rig, or nil if none. Idle polecats have completed their work and have a preserved sandbox (worktree) that can be reused by gt sling without creating a new worktree. Persistent polecat model (gt-4ac).
func (*Manager) Get ¶
Get returns a specific polecat by name. State is derived from beads assignee field + tmux session state: - If an issue is assigned to this polecat: StateWorking - If no issue but tmux session is running: StateWorking (session alive = still working) - If no issue and no tmux session: StateIdle (persistent, ready for reuse)
func (*Manager) GetNamePool ¶ added in v0.9.0
GetNamePool returns the manager's name pool for external use (e.g., pool init).
func (*Manager) PoolStatus ¶
PoolStatus returns information about the name pool.
func (*Manager) ReconcilePool ¶
func (m *Manager) ReconcilePool()
ReconcilePool derives pool InUse state from existing polecat directories and active sessions. This implements ZFC: InUse is discovered from filesystem and tmux, not tracked separately. Called before each allocation to ensure InUse reflects reality.
In addition to directory checks, this also: - Kills orphaned tmux sessions (sessions without directories are broken)
func (*Manager) ReconcilePoolWith ¶ added in v0.3.0
ReconcilePoolWith reconciles the name pool given lists of names from different sources. This is the testable core of ReconcilePool.
- namesWithDirs: names that have existing worktree directories (in use) - namesWithSessions: names that have tmux sessions
Names with sessions but no directories are orphans and their sessions are killed. Only namesWithDirs are marked as in-use for allocation.
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 selfNuke=true: bypass cwd-in-worktree check (for polecat deleting its own worktree)
ZFC #10: Uses cleanup_status from agent bead if available (polecat self-report), falls back to git check for backward compatibility.
func (*Manager) RepairWorktree ¶ added in v0.2.0
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 ¶ added in v0.2.0
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. After repair, uses new structure: polecats/<name>/<rigname>/
func (*Manager) ReuseIdlePolecat ¶ added in v0.9.0
func (m *Manager) ReuseIdlePolecat(name string, opts AddOptions) (*Polecat, error)
ReuseIdlePolecat prepares an idle polecat for new work using branch-only operations. Unlike RepairWorktreeWithOptions, this does NOT create/remove git worktrees. It simply creates a fresh branch on the existing worktree, which eliminates the ~5s overhead of worktree creation. Phase 3 of persistent-polecat-pool.md.
Steps:
- Verify polecat exists and worktree is accessible
- Fetch latest from origin
- Create fresh branch: git checkout -b <branch> <startPoint>
- Reset agent bead and set hook_bead atomically
- Return polecat in working state
func (*Manager) SetAgentState ¶ added in v0.6.0
SetState updates a polecat's state. In the beads model, state is derived from issue status: - StateWorking: issue status set to in_progress SetAgentState updates the agent bead's agent_state field. This is called after a polecat session successfully starts to transition from "spawning" to "working", making gt polecat identity show accurate status. Valid states: "spawning", "working", "done", "stuck", "idle"
func (*Manager) SetAgentStateWithRetry ¶ added in v0.6.0
SetAgentStateWithRetry wraps SetAgentState with retry logic. Returns an error after exhausting retries, but callers may choose to warn rather than fail — e.g., in StartSession where the tmux session is already running and failing hard would orphan it. Agent state is a monitoring concern, not a correctness requirement. Fails fast on configuration/initialization errors (gt-2ra).
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.
// ZFC: This is transient state derived from filesystem via Reconcile().
// Never persist - always discover from existing polecat directories.
InUse map[string]bool `json:"-"`
// 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 NAME SLOTS. IMPORTANT: This pools NAMES, not polecats. Polecats are spawned fresh for each task and nuked when done - there is no idle pool of polecat instances waiting for work. When a polecat is nuked, its name slot becomes available for the next freshly-spawned polecat.
Names are drawn from a themed pool (mad-max by default). When the pool is exhausted, overflow names use N format (just numbers). The rig prefix is added by SessionName to create session names like "gt-<rig>-N".
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 name slot to the available pool. Called when a polecat is nuked - the name becomes available for new polecats. NOTE: This releases the NAME, not the polecat. The polecat is gone (nuked). 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 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 SessionHeartbeat ¶ added in v0.9.0
SessionHeartbeat represents a polecat session's heartbeat file.
func ReadSessionHeartbeat ¶ added in v0.9.0
func ReadSessionHeartbeat(townRoot, sessionName string) *SessionHeartbeat
ReadSessionHeartbeat reads the heartbeat for a polecat session. Returns nil if the file doesn't exist or can't be read.
type SessionInfo ¶ added in v0.2.2
type SessionInfo struct {
// Polecat is the polecat name.
Polecat string `json:"polecat"`
// SessionID is the tmux session identifier.
SessionID string `json:"session_id"`
// Running indicates if the session is currently active.
Running bool `json:"running"`
// RigName is the rig this session belongs to.
RigName string `json:"rig_name"`
// Attached indicates if someone is attached to the session.
Attached bool `json:"attached,omitempty"`
// Created is when the session was created.
Created time.Time `json:"created,omitempty"`
// Windows is the number of tmux windows.
Windows int `json:"windows,omitempty"`
// LastActivity is when the session last had activity.
LastActivity time.Time `json:"last_activity,omitempty"`
}
SessionInfo contains information about a running polecat session.
type SessionManager ¶ added in v0.2.2
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager handles polecat session lifecycle.
func NewSessionManager ¶ added in v0.2.2
func NewSessionManager(t *tmux.Tmux, r *rig.Rig) *SessionManager
NewSessionManager creates a new polecat session manager for a rig.
func (*SessionManager) Attach ¶ added in v0.2.2
func (m *SessionManager) Attach(polecat string) error
Attach attaches to a polecat session.
func (*SessionManager) Capture ¶ added in v0.2.2
func (m *SessionManager) Capture(polecat string, lines int) (string, error)
Capture returns the recent output from a polecat session.
func (*SessionManager) CaptureSession ¶ added in v0.2.2
func (m *SessionManager) CaptureSession(sessionID string, lines int) (string, error)
CaptureSession returns the recent output from a session by raw session ID.
func (*SessionManager) Inject ¶ added in v0.2.2
func (m *SessionManager) Inject(polecat, message string) error
Inject sends a message to a polecat session.
func (*SessionManager) IsRunning ¶ added in v0.2.2
func (m *SessionManager) IsRunning(polecat string) (bool, error)
IsRunning checks if a polecat session is active and healthy. Checks both tmux session existence AND agent process liveness to avoid reporting zombie sessions (tmux alive but Claude dead) as "running".
func (*SessionManager) List ¶ added in v0.2.2
func (m *SessionManager) List() ([]SessionInfo, error)
List returns information about all sessions for this rig. This includes polecats, witness, refinery, and crew sessions. Use ListPolecats() to get only polecat sessions.
func (*SessionManager) ListPolecats ¶ added in v0.6.0
func (m *SessionManager) ListPolecats() ([]SessionInfo, error)
ListPolecats returns information only about polecat sessions for this rig. Filters out witness, refinery, and crew sessions.
func (*SessionManager) SessionName ¶ added in v0.2.2
func (m *SessionManager) SessionName(polecat string) string
SessionName generates the tmux session name for a polecat. Validates that the polecat name doesn't contain the rig prefix to prevent double-prefix bugs (e.g., "gt-gastown_manager-gastown_manager-142").
func (*SessionManager) Start ¶ added in v0.2.2
func (m *SessionManager) Start(polecat string, opts SessionStartOptions) error
Start creates and starts a new session for a polecat.
func (*SessionManager) Status ¶ added in v0.2.2
func (m *SessionManager) Status(polecat string) (*SessionInfo, error)
Status returns detailed status for a polecat session.
func (*SessionManager) Stop ¶ added in v0.2.2
func (m *SessionManager) Stop(polecat string, force bool) error
Stop terminates a polecat session.
func (*SessionManager) StopAll ¶ added in v0.2.2
func (m *SessionManager) StopAll(force bool) error
StopAll terminates all polecat sessions for this rig.
type SessionStartOptions ¶ added in v0.2.2
type SessionStartOptions struct {
// WorkDir overrides the default working directory (polecat clone dir).
WorkDir string
// Issue is an optional issue ID to work on.
Issue string
// Command overrides the default "claude" command.
Command string
// Account specifies the account handle to use (overrides default).
Account string
// RuntimeConfigDir is resolved config directory for the runtime account.
// If set, this is injected as an environment variable.
RuntimeConfigDir string
// Agent is the agent override for this polecat session (e.g., "codex", "gemini").
// If set, GT_AGENT is written to the tmux session environment table so that
// IsAgentAlive and waitForPolecatReady read the correct process names.
Agent string
}
SessionStartOptions configures polecat session startup.
type StalenessInfo ¶ added in v0.2.1
type StalenessInfo struct {
Name string
CommitsBehind int // How many commits behind origin/main
HasActiveSession bool // Whether tmux session is running
HasUncommittedWork bool // Whether there's uncommitted or unpushed work
AgentState string // From agent bead (empty if no bead)
IsStale bool // Overall assessment: safe to clean up
Reason string // Why it's considered stale (or not)
}
StalenessInfo contains details about a polecat's staleness.
type State ¶
type State string
State represents the current lifecycle state of a polecat.
Polecats are PERSISTENT: they survive work completion and can be reused. The four operating states are:
- Working: Session active, doing assigned work (normal operation)
- Idle: Work completed, session killed, sandbox preserved for reuse
- Stalled: Session stopped unexpectedly, was never nudged back to life
- Zombie: Session called 'gt done' but cleanup failed - tried to die but couldn't
The distinction matters: idle polecats completed their work successfully and are ready for new assignments. Stalled polecats failed mid-work. Zombies tried to exit but couldn't complete cleanup.
Note: These are LIFECYCLE states. The polecat IDENTITY (CV chain, mailbox, work history) and SANDBOX (worktree) persist across sessions. An idle polecat keeps its worktree so it can be quickly reassigned without creating a new one.
"Stalled" and "zombie" are detected conditions, not stored states. The Witness detects them through monitoring (tmux state, age in StateDone, etc.).
const ( // StateWorking means the polecat session is actively working on an issue. // This is the initial and primary state after sling. StateWorking State = "working" // StateIdle means the polecat completed its work and the session was killed, // but the sandbox (worktree) is preserved for reuse. An idle polecat has no // hook_bead and no active session. It can be reassigned via gt sling without // creating a new worktree. StateIdle State = "idle" // StateDone means the polecat has completed its assigned work and called // 'gt done'. This is normally a transient state - the session should exit // immediately after. If a polecat remains in StateDone, it's a "zombie": // the cleanup failed and the session is stuck. StateDone State = "done" // StateStuck means the polecat has explicitly signaled it needs assistance. // This is an intentional request for help from the polecat itself. // Different from "stalled" (detected externally when session stops working). StateStuck State = "stuck" // StateZombie means a tmux session exists but has no corresponding worktree directory. // This is a detected condition: the polecat was incompletely nuked or has a // session naming mismatch, leaving an orphaned tmux session. StateZombie State = "zombie" )
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 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