Documentation
¶
Overview ¶
Package deacon provides the Deacon agent infrastructure. The Deacon is an agent that monitors Mayor and Witnesses, handles lifecycle requests, and keeps Gas Town running.
Package deacon provides the Deacon agent infrastructure.
Package deacon provides the Deacon agent infrastructure.
Index ¶
- Constants
- Variables
- func HealthCheckStateFile(townRoot string) string
- func HeartbeatFile(townRoot string) string
- func SaveHealthCheckState(townRoot string, state *HealthCheckState) error
- func SessionName() string
- func Touch(townRoot string) error
- func TouchWithAction(townRoot, action string, healthy, unhealthy int) error
- func WriteHeartbeat(townRoot string, hb *Heartbeat) error
- type AgentHealthState
- func (s *AgentHealthState) CooldownRemaining(cooldown time.Duration) time.Duration
- func (s *AgentHealthState) IsInCooldown(cooldown time.Duration) bool
- func (s *AgentHealthState) RecordFailure()
- func (s *AgentHealthState) RecordForceKill()
- func (s *AgentHealthState) RecordPing()
- func (s *AgentHealthState) RecordResponse()
- func (s *AgentHealthState) ShouldForceKill(threshold int) bool
- type HealthCheckResult
- type HealthCheckState
- type Heartbeat
- type HookedBead
- type Manager
- type StaleHookConfig
- type StaleHookResult
- type StaleHookScanResult
- type StuckConfig
Constants ¶
const ( DefaultPingTimeout = 30 * time.Second // How long to wait for response DefaultConsecutiveFailures = 3 // Failures before force-kill DefaultCooldown = 5 * time.Minute // Minimum time between force-kills )
Default parameters for stuck-session detection.
Variables ¶
var ( ErrNotRunning = errors.New("deacon not running") ErrAlreadyRunning = errors.New("deacon already running") )
Common errors
var ( ErrAgentInCooldown = errors.New("agent is in cooldown period after recent force-kill") ErrAgentNotFound = errors.New("agent not found or session doesn't exist") ErrAgentResponsive = errors.New("agent is responsive, no action needed") )
Common errors for stuck-session detection.
Functions ¶
func HealthCheckStateFile ¶
HealthCheckStateFile returns the path to the health check state file.
func HeartbeatFile ¶
HeartbeatFile returns the path to the Deacon heartbeat file.
func SaveHealthCheckState ¶
func SaveHealthCheckState(townRoot string, state *HealthCheckState) error
SaveHealthCheckState saves the health check state to disk.
func SessionName ¶
func SessionName() string
SessionName returns the tmux session name for the deacon. This is a package-level function for convenience.
func Touch ¶
Touch writes a minimal heartbeat with just the timestamp. This is a convenience function for simple heartbeat updates.
func TouchWithAction ¶
TouchWithAction writes a heartbeat with an action description.
func WriteHeartbeat ¶
WriteHeartbeat writes a new heartbeat to disk. Called by the Deacon at the start of each wake cycle.
Types ¶
type AgentHealthState ¶
type AgentHealthState struct {
// AgentID is the identifier (e.g., "gastown/polecats/max" or "deacon")
AgentID string `json:"agent_id"`
// LastPingTime is when we last sent a HEALTH_CHECK nudge
LastPingTime time.Time `json:"last_ping_time,omitempty"`
// LastResponseTime is when the agent last updated their activity
LastResponseTime time.Time `json:"last_response_time,omitempty"`
// ConsecutiveFailures counts how many health checks failed in a row
ConsecutiveFailures int `json:"consecutive_failures"`
// LastForceKillTime is when we last force-killed this agent
LastForceKillTime time.Time `json:"last_force_kill_time,omitempty"`
// ForceKillCount is total number of force-kills for this agent
ForceKillCount int `json:"force_kill_count"`
}
AgentHealthState tracks the health check state for a single agent.
func (*AgentHealthState) CooldownRemaining ¶
func (s *AgentHealthState) CooldownRemaining(cooldown time.Duration) time.Duration
CooldownRemaining returns how long until cooldown expires.
func (*AgentHealthState) IsInCooldown ¶
func (s *AgentHealthState) IsInCooldown(cooldown time.Duration) bool
IsInCooldown returns true if the agent was recently force-killed.
func (*AgentHealthState) RecordFailure ¶
func (s *AgentHealthState) RecordFailure()
RecordFailure records that an agent failed to respond to a health check.
func (*AgentHealthState) RecordForceKill ¶
func (s *AgentHealthState) RecordForceKill()
RecordForceKill records that an agent was force-killed.
func (*AgentHealthState) RecordPing ¶
func (s *AgentHealthState) RecordPing()
RecordPing records that a health check ping was sent to an agent.
func (*AgentHealthState) RecordResponse ¶
func (s *AgentHealthState) RecordResponse()
RecordResponse records that an agent responded to a health check. This resets the consecutive failure counter.
func (*AgentHealthState) ShouldForceKill ¶
func (s *AgentHealthState) ShouldForceKill(threshold int) bool
ShouldForceKill returns true if the agent has exceeded the failure threshold.
type HealthCheckResult ¶
type HealthCheckResult struct {
AgentID string `json:"agent_id"`
Responded bool `json:"responded"`
ResponseTime time.Duration `json:"response_time,omitempty"`
ConsecutiveFailures int `json:"consecutive_failures"`
ShouldForceKill bool `json:"should_force_kill"`
InCooldown bool `json:"in_cooldown"`
CooldownRemaining time.Duration `json:"cooldown_remaining,omitempty"`
}
HealthCheckResult represents the outcome of a health check.
type HealthCheckState ¶
type HealthCheckState struct {
// Agents maps agent ID to their health state
Agents map[string]*AgentHealthState `json:"agents"`
// LastUpdated is when this state was last written
LastUpdated time.Time `json:"last_updated"`
}
HealthCheckState holds health check state for all monitored agents.
func LoadHealthCheckState ¶
func LoadHealthCheckState(townRoot string) (*HealthCheckState, error)
LoadHealthCheckState loads the health check state from disk. Returns empty state if file doesn't exist.
func (*HealthCheckState) GetAgentState ¶
func (s *HealthCheckState) GetAgentState(agentID string) *AgentHealthState
GetAgentState returns the health state for an agent, creating if needed.
type Heartbeat ¶
type Heartbeat struct {
// Timestamp is when the heartbeat was written.
Timestamp time.Time `json:"timestamp"`
// Cycle is the current wake cycle number.
Cycle int64 `json:"cycle"`
// LastAction describes what the Deacon did in this cycle.
LastAction string `json:"last_action,omitempty"`
// HealthyAgents is the count of healthy agents observed.
HealthyAgents int `json:"healthy_agents"`
// UnhealthyAgents is the count of unhealthy agents observed.
UnhealthyAgents int `json:"unhealthy_agents"`
}
Heartbeat represents the Deacon's heartbeat file contents. Written by the Deacon on each wake cycle. Read by the Go daemon to decide whether to poke.
func ReadHeartbeat ¶
ReadHeartbeat reads the Deacon heartbeat from disk. Returns nil if the file doesn't exist or can't be read.
func (*Heartbeat) Age ¶
Age returns how old the heartbeat is. Returns a very large duration if the heartbeat is nil.
func (*Heartbeat) IsFresh ¶
IsFresh returns true if the heartbeat is less than 5 minutes old. A fresh heartbeat means the Deacon is actively working or recently finished.
func (*Heartbeat) IsStale ¶
IsStale returns true if the heartbeat is 5-15 minutes old. A stale heartbeat may indicate the Deacon is doing a long operation.
func (*Heartbeat) IsVeryStale ¶
IsVeryStale returns true if the heartbeat is more than 15 minutes old. A very stale heartbeat means the Deacon should be poked.
func (*Heartbeat) ShouldPoke ¶
ShouldPoke returns true if the daemon should poke the Deacon. The Deacon should be poked if: - No heartbeat exists - Heartbeat is very stale (>5 minutes)
type HookedBead ¶
type HookedBead struct {
ID string `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
Assignee string `json:"assignee"`
UpdatedAt time.Time `json:"updated_at"`
}
HookedBead represents a bead in hooked status from bd list output.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles deacon lifecycle operations.
func NewManager ¶
NewManager creates a new deacon manager for a town.
func (*Manager) SessionName ¶
SessionName returns the tmux session name for the deacon.
func (*Manager) Start ¶
Start starts the deacon session. The deacon runs in a respawn loop for automatic recovery.
type StaleHookConfig ¶
type StaleHookConfig struct {
// MaxAge is how long a bead can be hooked before being considered stale.
MaxAge time.Duration `json:"max_age"`
// DryRun if true, only reports what would be done without making changes.
DryRun bool `json:"dry_run"`
}
StaleHookConfig holds configurable parameters for stale hook detection.
func DefaultStaleHookConfig ¶
func DefaultStaleHookConfig() *StaleHookConfig
DefaultStaleHookConfig returns the default stale hook config.
type StaleHookResult ¶
type StaleHookResult struct {
BeadID string `json:"bead_id"`
Title string `json:"title"`
Assignee string `json:"assignee"`
Age string `json:"age"`
AgentAlive bool `json:"agent_alive"`
Unhooked bool `json:"unhooked"`
Error string `json:"error,omitempty"`
}
StaleHookResult represents the result of processing a stale hooked bead.
type StaleHookScanResult ¶
type StaleHookScanResult struct {
ScannedAt time.Time `json:"scanned_at"`
TotalHooked int `json:"total_hooked"`
StaleCount int `json:"stale_count"`
Unhooked int `json:"unhooked"`
Results []*StaleHookResult `json:"results"`
}
StaleHookScanResult contains the full results of a stale hook scan.
func ScanStaleHooks ¶
func ScanStaleHooks(townRoot string, cfg *StaleHookConfig) (*StaleHookScanResult, error)
ScanStaleHooks finds hooked beads older than the threshold and optionally unhooks them.
type StuckConfig ¶
type StuckConfig struct {
PingTimeout time.Duration `json:"ping_timeout"`
ConsecutiveFailures int `json:"consecutive_failures"`
Cooldown time.Duration `json:"cooldown"`
}
StuckConfig holds configurable parameters for stuck-session detection.
func DefaultStuckConfig ¶
func DefaultStuckConfig() *StuckConfig
DefaultStuckConfig returns the default stuck detection config.