6.9 KiB
AGENTS.md
Operational guidance for coding agents working in this repository. Keep changes small, match the current rewrite architecture, and prefer the documented daemon/API boundaries over behavior from the old TypeScript implementation.
Repo layout
backend/— Go rewrite of Agent Orchestrator: CobraaoCLI, loopback HTTP daemon, services, SQLite storage, lifecycle/reaper, runtime/workspace/agent/tracker adapters, terminal mux, and tests.frontend/— Electron + React supervisor wired to the daemon via the generated typed client. Treat it as a thin supervisor/UI surface; do not move daemon logic into it.docs/— current architecture/status notes. Start here before changing lifecycle, CLI, agents, storage, or daemon behavior.test/— external smoke/e2e assets, including the CLI fresh-install container check..github/workflows/— CI definitions. Mirror these commands locally when possible.
Commands
From the repo root unless noted:
npm run lint # backend go test ./... + golangci-lint v2.12.2
npm run frontend:typecheck # frontend TypeScript check
npm run sqlc # regenerate backend/internal/storage/sqlite/gen from queries/schema
npm run api # regenerate OpenAPI spec + frontend TS types (see API contract changes below)
npx @redwoodjs/agent-ci run --all # local workflow validation; requires Docker socket
Backend-specific checks:
cd backend
go build ./...
go test ./...
go test -race ./...
go vet ./...
go run ./cmd/ao start
Frontend-specific checks:
cd frontend
npm run typecheck
npm run build
Where to look first
README.md— current run/config/test quickstart.docs/README.md— docs index.docs/architecture.md— backend mental model, package layout, lifecycle/session/service boundaries, and load-bearing rules.docs/STATUS.md— what is shipped onmaintoday and what is still in flight.docs/cli/README.md— intended CLI shape: thin Cobra client over daemon HTTP, never direct storage/runtime access.docs/agent/README.md— agent adapter contract and hook behavior.CLAUDE.md— compatibility pointer for Claude Code; it directs agents back toAGENTS.md.
For code entry points:
- CLI commands:
backend/internal/cli/*.go; follow nearby command/test patterns before adding a new style. - HTTP controllers and DTOs:
backend/internal/httpd/controllers/. - Service read/write boundaries:
backend/internal/service/. - Domain vocabulary:
backend/internal/domain/. - Port contracts:
backend/internal/ports/. - SQLite queries/migrations/store:
backend/internal/storage/sqlite/. - Generated sqlc code:
backend/internal/storage/sqlite/gen/.
Coding conventions
- Keep every change surgical and directly tied to the task. Avoid drive-by cleanup, broad renames, formatting churn, speculative abstractions, and architectural refactors unless the task explicitly asks for them.
- Follow existing Go package boundaries. CLI code should call daemon HTTP routes through shared CLI client helpers; it should not open SQLite, spawn runtimes, or call adapters directly.
- Keep Cobra commands in the relevant command file and table-test them in the style of
backend/internal/cli/*_test.go. - Mirror existing response/request DTOs in the CLI instead of importing HTTP controller packages into CLI code, unless the package already establishes that dependency.
- Return usage errors as
usageErrorso CLI misuse exits 2; runtime/daemon failures should exit 1. - Preserve API error envelopes and request IDs when surfacing daemon errors.
- Use
context.Contextas the first argument for functions that do I/O or blocking work. - Do not add abstractions for one-off use cases. Add helpers only when they remove duplication across real call sites.
- Tests should cover the user-visible behavior and boundary being changed: happy path, validation/missing args, daemon error envelopes, and any destructive confirmation path.
Hard rules and boundaries
- The daemon is a loopback-only sidecar. Do not make the bind host configurable or expose it beyond
127.0.0.1. - The CLI is a thin client. Do not port old in-process TypeScript CLI behavior that bypasses daemon HTTP routes.
- Do not store derived/display session status. Status is derived from durable facts (
activity_state,is_terminated, PR/check/comment facts) at service read time. - Do not treat failed/unknown runtime probes as proof a session is dead.
- Do not force-delete dirty registered worktrees.
- Do not modify already-merged SQLite migrations. Add a new migration instead.
- Do not hand-edit
backend/internal/storage/sqlite/gen/*; changebackend/internal/storage/sqlite/queries/*or migrations and runnpm run sqlc. - SQLite change events come from DB triggers into
change_log; do not add parallel manual CDC emission from store methods unless the architecture changes explicitly. - Keep generated OpenAPI/API DTO drift in mind: controller response shapes live in
backend/internal/httpd/controllers/dto.goand tests may assert CLI/HTTP wire compatibility. - Do not add network calls to tests unless the package already has an integration/e2e pattern for them. Prefer
httptest, fakes, and injected dependencies. - Do not commit local run state, daemon data, temporary worktrees, build outputs, or credentials.
API contract changes
The daemon API is code-first. The OpenAPI spec and frontend TypeScript types are generated artifacts — edit the source, then regenerate.
Source files to edit:
backend/internal/httpd/controllers/dto.go— request/response shapes.backend/internal/httpd/apispec/specgen/build.go— operation registry; add aschemaNamesentry for any new named type.
Regenerate after editing:
npm run api # runs api:spec then api:ts in sequence
This is equivalent to running:
npm run api:spec # cd backend && go generate ./internal/httpd/apispec/...
npm run api:ts # npx openapi-typescript@7.4.4 backend/internal/httpd/apispec/openapi.yaml -o frontend/src/api/schema.ts
Verify:
cd backend && go test ./internal/httpd/... # spec drift + route/spec parity tests (does not cover schema.ts — that is checked by the api-drift CI job)
Commit openapi.yaml and frontend/src/api/schema.ts together with the Go changes. CI will regenerate both files and fail if the committed versions are out of date. The CLI hand-mirrored DTOs remain a deliberate manual boundary and are not generated.
PR hygiene
- Branch from
mainunless explicitly continuing an existing PR. - Keep one issue per PR. If asked for separate work, create a separate branch and PR.
- Use conventional commit messages (
feat:,fix:,docs:,test:,chore:). - Explain intentional omissions in the PR body, especially when the TypeScript original had more behavior than the Go rewrite domain currently supports.
- Run the narrowest relevant tests first, then the repo/CI commands that match the touched area.