--- name: agent_runner_api lang: go domain: agents version: 0.1.0 description: "Service Go que orquesta agentes Claude headless en git worktrees con DoD" tags: [service, agents, go, workflows, dod] icon: phosphor: "robot" accent: "#3b82f6" framework: "stdlib-http" entry_point: "main.go" dir_path: "apps/agent_runner_api" repo_url: "https://gitea.organic-machine.com/dataforge/agent_runner_api" uses_functions: [] uses_types: [] service: port: 8486 health_endpoint: /api/health health_timeout_s: 3 systemd_unit: agent_runner_api.service systemd_scope: user restart_policy: always runtime: systemd-user pc_targets: - aurgi-pc - home-wsl is_local_only: true e2e_checks: - id: build cmd: "CGO_ENABLED=1 go build -o agent_runner_api ." timeout_s: 120 - id: smoke cmd: "./agent_runner_api --port 8486 --db /tmp/agent_runner_api_e2e.db &" health: "http://127.0.0.1:8486/api/health" - id: tests cmd: "go test -count=1 ./..." --- ## Visual Backend puro, sin UI. Consume por skill_tree v2 + kanban_cpp. ## Endpoints - `GET /api/health` — `{status, port, db}`. - `POST /api/runs` — body `{issue_id?, card_id?, kanban_app?, mode, prompt?}` -> `{run_id, branch, worktree_path, sse_url}`. Crea worktree + lanza subprocess Claude (o `echo STUB:` si `AGENT_RUNNER_STUB=1`). - `GET /api/runs?status=&app=&since=` — lista runs filtrada. - `GET /api/runs/:id` — detalle + dod_items. - `GET /api/runs/:id/sse` — stream `text/event-stream` (events: `connected`, `status`, `evidence`, `validated`, `merged`, `aborted`). - `POST /api/runs/:id/evidence` — `{item_id|item_key, kind, payload_path?, payload_url?, payload_text?}`. Auto-crea `dod_item` si solo se da `item_key`. - `POST /api/runs/:id/evidence/:eid/validate` — `{validated_by}`. - `POST /api/runs/:id/merge` — TBD merge `auto/` a master `--no-ff` (gate: todos los `dod_items.required = 1` deben tener evidencia `validated`). - `POST /api/runs/:id/abort` — kill PID + `git worktree remove --force` + `branch -D` + `status=aborted`. ## Schema (5 migrations idempotentes via embed.FS) | Tabla | Para que | |---|---| | `workflows` | Templates de prompt + `dod_schema_json` | | `runs` | Run vivo: workflow/issue/card/kanban_app + branch + worktree_path + PID + status | | `worktrees` | 1 row por worktree creada, marcada `removed_at` al abort/merge | | `dod_items` | Items DoD del run (`pending|done|validated|failed`) | | `dod_evidence` | Evidencias adjuntas (`text|file|url`) con `validated_at/validated_by` | ## Lanzamiento ```bash cd apps/agent_runner_api CGO_ENABLED=1 go build -o agent_runner_api . ./agent_runner_api --port 8486 --db agent_runs.db --repo-root /home/lucas/fn_registry --worktrees-root /tmp ``` systemd-user: `systemctl --user enable --now agent_runner_api.service` (despues de copiar `agent_runner_api.service` a `~/.config/systemd/user/`). ## Gotchas - `git worktree add` falla si la rama ya existe -> el `Spawn()` la borra antes con `branch -D` (best-effort). - Worktree y main repo comparten `.git/hooks/` — pre-commit del main puede bloquear commits del agente; usar `--no-verify` documentado. - `claude --headless` requiere PATH correcto en systemd. Si `claude` no esta en `$PATH`, el subprocess cae automaticamente a `echo STUB:` (mismo comportamiento que `AGENT_RUNNER_STUB=1`). - Subprocess corre async — el handler HTTP devuelve `run_id` apenas inserta + lanza, no espera al exit. - SSE: clientes deben reconectar al cerrar conexion. Heartbeat cada 15s para mantener conexion abierta. ## Capability growth log - v0.1.0 (2026-05-18) — scaffold inicial: stdlib http, embed.FS migrations, SSE hub, spawn stub fallback, DoD gate en merge.