# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Also check `~/.claude/CLAUDE.md` for user-level development standards (TDD workflow, git practices, session state management, etc.) that apply globally across all projects. ## Canonical Repository **The canonical source of truth is `/workspace/claudomator`.** All development must happen here. Do not work in any other directory unless explicitly instructed. Do not explore `/site/doot.terst.org/` for source files. ## Build & Test Commands ```bash # Build go build ./... # Run all tests go test ./... # Run a single package's tests go test ./internal/executor/... # Run a single test by name go test ./internal/api/ -run TestServer_CreateTask_MissingName # Run with race detector (important for executor/pool tests) go test -race ./... # Build the binary go build -o claudomator ./cmd/claudomator/ ``` > **Note:** `go-sqlite3` uses CGo. A C compiler (`gcc`) must be present for builds and tests. ## Running the Server ```bash # Initialize data directory ./claudomator init # Start API server (default :8484) ./claudomator serve # Run a task file directly (bypasses server) ./claudomator run ./test/fixtures/tasks/simple-task.yaml # List tasks via CLI ./claudomator list ``` Config defaults to `~/.claudomator/config.toml`. Data is stored in `~/.claudomator/` (SQLite DB + execution logs). ## Architecture **Pipeline:** CLI/API → `executor.Pool` → `executor.ContainerRunner` → Docker container → SQLite + log files ### Packages | Package | Role | |---|---| | `internal/task` | `Task` struct, YAML parsing, state machine, validation | | `internal/executor` | `Pool` (bounded goroutine pool) + `ContainerRunner` (Docker-based executor) | | `internal/storage` | SQLite wrapper; stores tasks and execution records | | `internal/api` | HTTP server (REST + WebSocket via `internal/api.Hub`) | | `internal/reporter` | Formats and emits execution results | | `internal/config` | TOML config + data dir layout | | `internal/cli` | Cobra CLI commands (`run`, `serve`, `list`, `status`, `init`) | ### Key Data Flows **Task execution:** 1. Task created via `POST /api/tasks` or YAML file (`task.ParseFile`) 2. `POST /api/tasks/{id}/run` → `executor.Pool.Submit()` → goroutine in pool 3. `ContainerRunner.Run()` clones `repository_url`, runs `docker run claudomator-agent:latest` 4. Agent runs `claude -p` inside the container; stdout streamed to `executions//stdout.log` 5. On success, runner pushes commits back to the remote; execution result written to SQLite + WebSocket broadcast **State machine** (`task.ValidTransition`): `PENDING` → `QUEUED` → `RUNNING` → `COMPLETED | FAILED | TIMED_OUT | CANCELLED | BUDGET_EXCEEDED` Failed tasks can retry: `FAILED` → `QUEUED` **WebSocket:** `Hub` fans out task completion events to all connected clients. `Server.StartHub()` must be called after creating the server. ### Task YAML Format ```yaml name: "My Task" claude: model: "sonnet" instructions: | Do something useful. working_dir: "/path/to/project" max_budget_usd: 1.00 permission_mode: "default" allowed_tools: ["Bash", "Read"] timeout: "15m" priority: "normal" # high | normal | low tags: ["ci"] ``` Batch files wrap multiple tasks under a `tasks:` key. ### Storage Schema Two tables: `tasks` (with `config_json`, `retry_json`, `tags_json`, `depends_on_json` as JSON blobs) and `executions` (with paths to log files). Schema is auto-migrated on `storage.Open()`. ## Features ### Changestats After each task execution, Claudomator extracts git diff statistics from the execution's stdout log. If the log contains a git `diff --stat` summary line (e.g. `5 files changed, 127 insertions(+), 43 deletions(-)`), the stats are parsed and stored in the `executions.changestats_json` column via `storage.DB.UpdateExecutionChangestats`. **Extraction points:** - `internal/executor.Pool.handleRunResult` — calls `task.ParseChangestatFromFile(exec.StdoutPath)` after every execution; stores via `Store.UpdateExecutionChangestats`. - `internal/api.Server.processResult` — also extracts changestats when the API server processes a result (same file, idempotent second write). **Parser location:** `internal/task/changestats.go` — exported functions `ParseChangestatFromOutput` and `ParseChangestatFromFile` usable by any package without creating circular imports. **Frontend display:** `web/app.js` renders a `.changestats-badge` on COMPLETED/READY task cards and in execution history rows. ## GitHub Webhook Integration Claudomator can automatically create tasks when CI builds fail on GitHub. ### Endpoint `POST /api/webhooks/github` Accepts `check_run` and `workflow_run` events from GitHub. Returns `{"task_id": "..."}` (200) when a task is created, or 204 when the event is ignored. ### Config (`~/.claudomator/config.toml`) ```toml # Optional: HMAC-SHA256 secret set in the GitHub webhook settings. # If omitted, signature validation is skipped. webhook_secret = "your-github-webhook-secret" # Projects for matching incoming webhook repository names to local directories. [[projects]] name = "myrepo" dir = "/workspace/myrepo" [[projects]] name = "other-service" dir = "/workspace/other-service" ``` ### Matching logic The handler matches the webhook's `repository.name` against each project's `name` and the basename of its `dir` (case-insensitive substring). If no match is found and only one project is configured, that project is used as a fallback. ### GitHub webhook setup In your GitHub repository → Settings → Webhooks → Add webhook: - **Payload URL:** `https:///api/webhooks/github` - **Content type:** `application/json` - **Secret:** value of `webhook_secret` in config (or leave blank if not configured) - **Events:** select *Workflow runs* and *Check runs* ### Task creation A task is created for: - `check_run` events with `action: completed` and `conclusion: failure` - `workflow_run` events with `action: completed` and `conclusion: failure` or `timed_out` Tasks are tagged `["ci", "auto"]`, capped at $3 USD, and use tools: Read, Edit, Bash, Glob, Grep. ## Agent Tooling (`ct` CLI) Agents running inside containers have access to `ct`, a pre-built CLI for interacting with the Claudomator API. It is installed at `/usr/local/bin/ct` in the container image. **Use `ct` to create and manage subtasks — do not attempt raw `curl` API calls.** ### Environment (injected automatically) | Variable | Purpose | |---|---| | `CLAUDOMATOR_API_URL` | Base URL of the Claudomator API (e.g. `http://host.docker.internal:8484`) | | `CLAUDOMATOR_TASK_ID` | ID of the currently-running task; used as the default `parent_task_id` for new subtasks | ### Commands ```bash # Create a subtask and immediately queue it (returns task ID) ct task submit --name "Fix tests" --instructions "Run tests and fix any failures." [--model sonnet] [--budget 3.0] # Create, queue, and wait for completion (exits 0=COMPLETED, 1=FAILED, 2=BLOCKED) ct task submit --name "Fix tests" --instructions "..." --wait # Read instructions from a file instead of inline ct task submit --name "Fix tests" --file /workspace/subtask-instructions.txt --wait # Lower-level: create only (returns task ID), then run separately TASK_ID=$(ct task create --name "..." --instructions "...") ct task run "$TASK_ID" ct task wait "$TASK_ID" --timeout 600 # Check status of any task ct task status # List recent tasks ct task list ``` ### Notes - Default model is `sonnet`; default budget is `$3.00 USD`. Override with `--model` / `--budget`. - `ct task wait` polls every 5 seconds and exits with the task's terminal state on stdout. - Subtasks inherit the current task as their parent automatically (via `$CLAUDOMATOR_TASK_ID`). - Override parent with `--parent ` if needed. ## ADRs See `docs/adr/001-language-and-architecture.md` for the Go + SQLite + WebSocket rationale.