From 23f9b65bf65b3d3677350a456e57294a4df810b9 Mon Sep 17 00:00:00 2001 From: Claudomator Agent Date: Wed, 11 Mar 2026 00:43:41 +0000 Subject: docs: add executor package documentation --- docs/packages/task.md | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 docs/packages/task.md (limited to 'docs/packages/task.md') diff --git a/docs/packages/task.md b/docs/packages/task.md new file mode 100644 index 0000000..3502f8a --- /dev/null +++ b/docs/packages/task.md @@ -0,0 +1,332 @@ +# Package: `internal/task` + +## Overview + +The `task` package defines the core domain model for Claudomator. A **Task** represents a discrete unit of work to be executed by a Claude agent — described by natural-language instructions, optional budget and timeout constraints, retry policy, and scheduling metadata. Tasks are authored as YAML files and progress through a well-defined lifecycle from `PENDING` to a terminal state. + +--- + +## Task Struct + +```go +type Task struct { + ID string `yaml:"id"` + ParentTaskID string `yaml:"parent_task_id"` + Name string `yaml:"name"` + Description string `yaml:"description"` + Agent AgentConfig `yaml:"agent"` + Timeout Duration `yaml:"timeout"` + Retry RetryConfig `yaml:"retry"` + Priority Priority `yaml:"priority"` + Tags []string `yaml:"tags"` + DependsOn []string `yaml:"depends_on"` + State State `yaml:"-"` + RejectionComment string `yaml:"-"` + QuestionJSON string `yaml:"-"` + CreatedAt time.Time `yaml:"-"` + UpdatedAt time.Time `yaml:"-"` +} +``` + +| Field | Type | Purpose | Default / Notes | +|--------------------|---------------|----------------------------------------------------------------------|------------------------------------------| +| `ID` | `string` | Unique identifier (UUID). | Auto-generated by `Parse` if empty. | +| `ParentTaskID` | `string` | ID of the parent task; empty for top-level tasks. | Optional. | +| `Name` | `string` | Human-readable name. **Required.** | Must be non-empty. | +| `Description` | `string` | Longer explanation shown in the UI. | Optional. | +| `Agent` | `AgentConfig` | Configuration passed to the Claude agent executor. | See [AgentConfig](#agentconfig-struct). | +| `Timeout` | `Duration` | Maximum wall-clock time for a single execution attempt. | `0` means no limit. | +| `Retry` | `RetryConfig` | Retry policy applied when a run fails. | See [RetryConfig](#retryconfig-struct). | +| `Priority` | `Priority` | Scheduling priority: `high`, `normal`, or `low`. | Default: `normal`. | +| `Tags` | `[]string` | Arbitrary labels for filtering and grouping. | Optional; defaults to `[]`. | +| `DependsOn` | `[]string` | IDs of tasks that must reach `COMPLETED` before this one is queued. | Optional; defaults to `[]`. | +| `State` | `State` | Current lifecycle state. Not read from YAML (`yaml:"-"`). | Set to `PENDING` by `Parse`. | +| `RejectionComment` | `string` | Free-text comment left by a reviewer when rejecting a task. | Not in YAML; populated by storage layer. | +| `QuestionJSON` | `string` | JSON-encoded question posted by the agent while `BLOCKED`. | Not in YAML; populated by storage layer. | +| `CreatedAt` | `time.Time` | Timestamp set when the task is parsed. | Auto-set by `Parse`. | +| `UpdatedAt` | `time.Time` | Timestamp updated on every state change. | Auto-set by `Parse`; updated by DB. | + +Fields tagged `yaml:"-"` are runtime-only and are never parsed from YAML task files. + +--- + +## AgentConfig Struct + +```go +type AgentConfig struct { + Type string `yaml:"type"` + Model string `yaml:"model"` + ContextFiles []string `yaml:"context_files"` + Instructions string `yaml:"instructions"` + ProjectDir string `yaml:"project_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"` + SkipPlanning bool `yaml:"skip_planning"` +} +``` + +| Field | Type | Purpose | +|----------------------|------------|---------------------------------------------------------------------------------------------------------------------------------| +| `Type` | `string` | Agent type classifier. Cleared on retry by `ResetTaskForRetry` so the task can be re-classified. | +| `Model` | `string` | Claude model ID (e.g. `"claude-sonnet-4-6"`). Cleared on retry alongside `Type`. | +| `ContextFiles` | `[]string` | Paths to files pre-loaded as context for the agent. | +| `Instructions` | `string` | Natural-language task instructions. **Required.** | +| `ProjectDir` | `string` | Working directory the agent operates in. | +| `MaxBudgetUSD` | `float64` | API spend cap in USD. `0` means no limit. Must be ≥ 0. | +| `PermissionMode` | `string` | Claude permission mode. Valid values: `default`, `acceptEdits`, `bypassPermissions`, `plan`, `dontAsk`, `delegate`. | +| `AllowedTools` | `[]string` | Explicit tool whitelist passed to the agent. | +| `DisallowedTools` | `[]string` | Explicit tool blacklist passed to the agent. | +| `SystemPromptAppend` | `string` | Text appended to the Claude system prompt. | +| `AdditionalArgs` | `[]string` | Extra CLI arguments forwarded verbatim to the Claude executable. | +| `SkipPlanning` | `bool` | When `true`, the agent skips the planning phase. | + +--- + +## RetryConfig Struct + +```go +type RetryConfig struct { + MaxAttempts int `yaml:"max_attempts"` + Backoff string `yaml:"backoff"` // "linear" or "exponential" +} +``` + +| Field | Type | Purpose | Default | +|---------------|----------|------------------------------------------------------------|-----------------| +| `MaxAttempts` | `int` | Total number of attempts (first run + retries). Min: 1. | `1` | +| `Backoff` | `string` | Wait strategy between retries: `linear` or `exponential`. | `"exponential"` | + +--- + +## YAML Task File Format + +A task file may contain a single task or a batch (see next section). Every supported field is shown below with annotations. + +```yaml +# Unique identifier. Omit to have Claudomator generate a UUID. +id: "fix-login-bug" + +# Display name. Required. +name: "Fix login redirect bug" + +# Optional longer description. +description: "Users are redirected to /home instead of /dashboard after login." + +agent: + # Agent type for routing/classification. Optional; auto-assigned if blank. + type: "claude" + + # Claude model ID. Optional; auto-assigned if blank. + model: "claude-sonnet-4-6" + + # Task instructions passed to the agent. Required. + instructions: | + Fix the post-login redirect in src/auth/login.go so that users are + sent to /dashboard instead of /home. Add a regression test. + + # Working directory for the agent process. + project_dir: "/workspace/myapp" + + # Files loaded into the agent's context before execution starts. + context_files: + - "src/auth/login.go" + - "docs/adr/0003-auth.md" + + # USD spending cap. 0 = no limit. + max_budget_usd: 1.00 + + # Claude permission mode. + # Values: default | acceptEdits | bypassPermissions | plan | dontAsk | delegate + permission_mode: "acceptEdits" + + # Tool whitelist. Empty = all tools allowed. + allowed_tools: + - "Read" + - "Edit" + - "Bash" + + # Tool blacklist. + disallowed_tools: + - "WebFetch" + + # Appended to the agent's system prompt. + system_prompt_append: "Always write tests before implementation." + + # Extra arguments forwarded verbatim to the Claude executable. + additional_args: + - "--verbose" + + # Skip the planning phase. + skip_planning: false + +# Maximum wall-clock time per attempt. Uses Go duration syntax: "30m", "1h", "45s". +# 0 or omitted means no limit. +timeout: "30m" + +retry: + # Total attempts including the first run. Must be >= 1. + max_attempts: 3 + # "linear" or "exponential" + backoff: "exponential" + +# Scheduling priority: high | normal | low +priority: "normal" + +# Arbitrary labels for filtering. +tags: + - "bug" + - "auth" + +# IDs of tasks that must reach COMPLETED before this task is queued. +depends_on: + - "setup-test-db" +``` + +--- + +## Batch File Format + +A batch file wraps multiple task definitions under a `tasks:` key. All top-level task fields are supported per entry. + +```yaml +tasks: + - name: "Step 1 — scaffold" + agent: + instructions: "Create the initial project structure." + priority: "high" + + - name: "Step 2 — implement" + agent: + instructions: "Implement the feature described in docs/feature.md." + depends_on: + - "step-1-id" + + - name: "Step 3 — test" + agent: + instructions: "Write and run integration tests." + depends_on: + - "step-2-id" + retry: + max_attempts: 2 + backoff: "linear" +``` + +`ParseFile` / `Parse` detect the batch format automatically: if unmarshaling into `BatchFile` succeeds and produces at least one task, the batch path is used; otherwise single-task parsing is attempted. + +--- + +## State Constants + +```go +type State string + +const ( + StatePending State = "PENDING" + StateQueued State = "QUEUED" + StateRunning State = "RUNNING" + StateReady State = "READY" + StateCompleted State = "COMPLETED" + StateFailed State = "FAILED" + StateTimedOut State = "TIMED_OUT" + StateCancelled State = "CANCELLED" + StateBudgetExceeded State = "BUDGET_EXCEEDED" + StateBlocked State = "BLOCKED" +) +``` + +| Constant | String value | Description | +|-----------------------|-------------------|-------------------------------------------------------------------------------------------------------| +| `StatePending` | `PENDING` | Newly created; awaiting acceptance for scheduling. | +| `StateQueued` | `QUEUED` | Accepted by the scheduler and waiting for an available executor. | +| `StateRunning` | `RUNNING` | An agent is actively executing the task. | +| `StateReady` | `READY` | Agent finished execution; output is awaiting human review/approval. | +| `StateCompleted` | `COMPLETED` | Approved and finished. **Terminal** — no further transitions. | +| `StateFailed` | `FAILED` | Execution failed (non-zero exit or unrecoverable error). Eligible for retry → `QUEUED`. | +| `StateTimedOut` | `TIMED_OUT` | Execution exceeded the configured `timeout`. Eligible for retry or resume → `QUEUED`. | +| `StateCancelled` | `CANCELLED` | Manually cancelled. Can be restarted by transitioning back to `QUEUED`. | +| `StateBudgetExceeded` | `BUDGET_EXCEEDED` | The `max_budget_usd` cap was reached during execution. Eligible for retry → `QUEUED`. | +| `StateBlocked` | `BLOCKED` | The running agent posted a question requiring a human response before it can continue. | + +--- + +## State Machine + +### Valid Transitions + +| From | To | Condition | +|---------------------|---------------------|-------------------------------------------------------------------| +| `PENDING` | `QUEUED` | Task accepted for scheduling. | +| `PENDING` | `CANCELLED` | Manually cancelled before queuing. | +| `QUEUED` | `RUNNING` | An executor picks up the task. | +| `QUEUED` | `CANCELLED` | Manually cancelled while waiting in queue. | +| `RUNNING` | `READY` | Agent finished; output ready for human review. | +| `RUNNING` | `COMPLETED` | Agent finished successfully with no review required. | +| `RUNNING` | `FAILED` | Agent exited with an error. | +| `RUNNING` | `TIMED_OUT` | Wall-clock timeout exceeded. | +| `RUNNING` | `CANCELLED` | Manually cancelled mid-execution. | +| `RUNNING` | `BUDGET_EXCEEDED` | API spend reached `max_budget_usd`. | +| `RUNNING` | `BLOCKED` | Agent posted a question requiring human input before continuing. | +| `READY` | `COMPLETED` | Reviewer approves the output. | +| `READY` | `PENDING` | Reviewer rejects the output; task sent back for revision. | +| `FAILED` | `QUEUED` | Retry: task re-queued for another attempt. | +| `TIMED_OUT` | `QUEUED` | Retry or resume: task re-queued. | +| `CANCELLED` | `QUEUED` | Restart: task re-queued from scratch. | +| `BUDGET_EXCEEDED` | `QUEUED` | Retry with adjusted budget. | +| `BLOCKED` | `QUEUED` | Answer provided; task re-queued to resume. | +| `BLOCKED` | `READY` | Question resolved; output is ready for review. | +| `COMPLETED` | *(none)* | Terminal state — no outgoing transitions. | + +--- + +## Key Functions + +### `ParseFile` + +```go +func ParseFile(path string) ([]Task, error) +``` + +Reads a YAML file at `path` and returns the parsed tasks. Supports both single-task and batch formats. Initialises defaults (UUID, priority, retry policy, initial state, timestamps) on every returned task. + +### `Parse` + +```go +func Parse(data []byte) ([]Task, error) +``` + +Same as `ParseFile` but operates on raw YAML bytes instead of a file path. + +### `ValidTransition` + +```go +func ValidTransition(from, to State) bool +``` + +Returns `true` if transitioning from `from` to `to` is permitted by the state machine. Used by `storage.DB.UpdateTaskState` to enforce valid transitions inside a transaction. + +### `Validate` + +```go +func Validate(t *Task) error +``` + +Checks a task for constraint violations. Returns a `*ValidationError` (implementing `error`) listing all violations, or `nil` if the task is valid. All violations are collected before returning so callers receive the full list at once. + +--- + +## Validation Rules + +| Rule | Error message | +|------------------------------------------------------------------------------|--------------------------------------------------------------------| +| `name` must be non-empty. | `name is required` | +| `agent.instructions` must be non-empty. | `agent.instructions is required` | +| `agent.max_budget_usd` must be ≥ 0. | `agent.max_budget_usd must be non-negative` | +| `timeout` must be ≥ 0. | `timeout must be non-negative` | +| `retry.max_attempts` must be ≥ 1. | `retry.max_attempts must be at least 1` | +| `retry.backoff`, if set, must be `linear` or `exponential`. | `retry.backoff must be 'linear' or 'exponential'` | +| `priority`, if set, must be `high`, `normal`, or `low`. | `invalid priority "…"; must be high, normal, or low` | +| `agent.permission_mode`, if set, must be one of the six recognised values. | `invalid permission_mode "…"` | -- cgit v1.2.3