summaryrefslogtreecommitdiff
path: root/internal/task/task.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-02-08 21:35:45 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-02-08 21:35:45 -1000
commit2e2b2187b957e9af78797a67ec5c6874615fae02 (patch)
tree1181dbb7e43f5d30cb025fa4d50fd4e7a2c893b3 /internal/task/task.go
Initial project: task model, executor, API server, CLI, storage, reporter
Claudomator automation toolkit for Claude Code with: - Task model with YAML parsing, validation, state machine (49 tests, 0 races) - SQLite storage for tasks and executions - Executor pool with bounded concurrency, timeout, cancellation - REST API + WebSocket for mobile PWA integration - Webhook/multi-notifier system - CLI: init, run, serve, list, status commands - Console, JSON, HTML reporters with cost tracking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/task/task.go')
-rw-r--r--internal/task/task.go100
1 files changed, 100 insertions, 0 deletions
diff --git a/internal/task/task.go b/internal/task/task.go
new file mode 100644
index 0000000..3796cf3
--- /dev/null
+++ b/internal/task/task.go
@@ -0,0 +1,100 @@
+package task
+
+import "time"
+
+type State string
+
+const (
+ StatePending State = "PENDING"
+ StateQueued State = "QUEUED"
+ StateRunning State = "RUNNING"
+ StateCompleted State = "COMPLETED"
+ StateFailed State = "FAILED"
+ StateTimedOut State = "TIMED_OUT"
+ StateCancelled State = "CANCELLED"
+ StateBudgetExceeded State = "BUDGET_EXCEEDED"
+)
+
+type Priority string
+
+const (
+ PriorityHigh Priority = "high"
+ PriorityNormal Priority = "normal"
+ PriorityLow Priority = "low"
+)
+
+type ClaudeConfig struct {
+ Model string `yaml:"model"`
+ ContextFiles []string `yaml:"context_files"`
+ Instructions string `yaml:"instructions"`
+ WorkingDir string `yaml:"working_dir"`
+ MaxBudgetUSD float64 `yaml:"max_budget_usd"`
+ PermissionMode string `yaml:"permission_mode"`
+ AllowedTools []string `yaml:"allowed_tools"`
+ DisallowedTools []string `yaml:"disallowed_tools"`
+ SystemPromptAppend string `yaml:"system_prompt_append"`
+ AdditionalArgs []string `yaml:"additional_args"`
+}
+
+type RetryConfig struct {
+ MaxAttempts int `yaml:"max_attempts"`
+ Backoff string `yaml:"backoff"` // "linear", "exponential"
+}
+
+type Task struct {
+ ID string `yaml:"id"`
+ Name string `yaml:"name"`
+ Description string `yaml:"description"`
+ Claude ClaudeConfig `yaml:"claude"`
+ Timeout Duration `yaml:"timeout"`
+ Retry RetryConfig `yaml:"retry"`
+ Priority Priority `yaml:"priority"`
+ Tags []string `yaml:"tags"`
+ DependsOn []string `yaml:"depends_on"`
+ State State `yaml:"-"`
+ CreatedAt time.Time `yaml:"-"`
+ UpdatedAt time.Time `yaml:"-"`
+}
+
+// Duration wraps time.Duration for YAML unmarshaling from strings like "30m".
+type Duration struct {
+ time.Duration
+}
+
+func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ dur, err := time.ParseDuration(s)
+ if err != nil {
+ return err
+ }
+ d.Duration = dur
+ return nil
+}
+
+func (d Duration) MarshalYAML() (interface{}, error) {
+ return d.Duration.String(), nil
+}
+
+// BatchFile represents a YAML file containing multiple tasks.
+type BatchFile struct {
+ Tasks []Task `yaml:"tasks"`
+}
+
+// ValidTransition returns true if moving from the current state to next is allowed.
+func ValidTransition(from, to State) bool {
+ transitions := map[State][]State{
+ StatePending: {StateQueued, StateCancelled},
+ StateQueued: {StateRunning, StateCancelled},
+ StateRunning: {StateCompleted, StateFailed, StateTimedOut, StateCancelled, StateBudgetExceeded},
+ StateFailed: {StateQueued}, // retry
+ }
+ for _, allowed := range transitions[from] {
+ if allowed == to {
+ return true
+ }
+ }
+ return false
+}