Files
gastown/internal/agent/state.go
mediocre f49197243d refactor: extract shared AgentStateManager pattern (gt-gaw8e)
- Create internal/agent package with shared State type and StateManager
- StateManager uses Go generics for type-safe load/save operations
- Update witness and refinery to use shared State type alias
- Replace loadState/saveState implementations with StateManager delegation
- Maintains backwards compatibility through re-exported constants

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 00:19:41 -08:00

77 lines
2.0 KiB
Go

// Package agent provides shared types and utilities for Gas Town agents
// (witness, refinery, deacon, etc.).
package agent
import (
"encoding/json"
"os"
"path/filepath"
"github.com/steveyegge/gastown/internal/util"
)
// State represents an agent's running state.
type State string
const (
// StateStopped means the agent is not running.
StateStopped State = "stopped"
// StateRunning means the agent is actively operating.
StateRunning State = "running"
// StatePaused means the agent is paused (not operating but not stopped).
StatePaused State = "paused"
)
// StateManager handles loading and saving agent state to disk.
// It uses generics to work with any state type.
type StateManager[T any] struct {
stateFilePath string
defaultFactory func() *T
}
// NewStateManager creates a new StateManager for the given state file path.
// The defaultFactory function is called when the state file doesn't exist
// to create a new state with default values.
func NewStateManager[T any](rigPath, stateFileName string, defaultFactory func() *T) *StateManager[T] {
return &StateManager[T]{
stateFilePath: filepath.Join(rigPath, ".runtime", stateFileName),
defaultFactory: defaultFactory,
}
}
// StateFile returns the path to the state file.
func (m *StateManager[T]) StateFile() string {
return m.stateFilePath
}
// Load loads agent state from disk.
// If the file doesn't exist, returns a new state created by the default factory.
func (m *StateManager[T]) Load() (*T, error) {
data, err := os.ReadFile(m.stateFilePath)
if err != nil {
if os.IsNotExist(err) {
return m.defaultFactory(), nil
}
return nil, err
}
var state T
if err := json.Unmarshal(data, &state); err != nil {
return nil, err
}
return &state, nil
}
// Save persists agent state to disk using atomic write.
func (m *StateManager[T]) Save(state *T) error {
dir := filepath.Dir(m.stateFilePath)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
return util.AtomicWriteJSON(m.stateFilePath, state)
}