# 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.ClaudeRunner` → `claude -p` subprocess → SQLite + log files ### Packages | Package | Role | |---|---| | `internal/task` | `Task` struct, YAML parsing, state machine, validation | | `internal/executor` | `Pool` (bounded goroutine pool) + `ClaudeRunner` (subprocess manager) | | `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. `ClaudeRunner.Run()` invokes `claude -p --output-format stream-json` 4. stdout streamed to `~/.claudomator/executions//stdout.log`; cost parsed from stream-json 5. Execution result written to SQLite; broadcast via WebSocket to connected clients **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. ## ADRs See `docs/adr/001-language-and-architecture.md` for the Go + SQLite + WebSocket rationale.