Architecture
How romp works under the hood.
romp is a Go CLI that drives your local coding agent. It is deliberately minimal: the durable source of truth is GitHub, and romp adds only the machinery to claim, isolate, and verify work.
Job lifecycle
- Poll —
watchlists open issues carrying the trigger label every 60 seconds. The label is the entire queue; there is no local backlog. - Claim — atomic insert (unique on repo + issue), claim label, assign
@me. Concurrent watchers on other machines skip claimed issues. - Worktree — each job runs in a fresh
git worktreebranched from the default branch. Never the local tree; the base is deterministic. - Agent — the goal contract is rendered and handed to the harness (claude or codex).
- Verify — romp re-runs every
[verify]command itself. The agent’s own “tests pass” is never trusted. - PR or block — green and scoped means a PR and label removal; an
under-scoped issue becomes
blockedwith a gap comment.
Components
romp/
├── cmd/romp/ # Cobra commands (init, watch, run, status, ...)
├── internal/
│ ├── config/ # TOML layering, language detection, effort validation
│ ├── harness/ # claude + codex adapters behind a Run/Name interface
│ ├── prompt/ # goal-contract template rendering
│ ├── runner/ # job pipeline: worktree → agent → verify → PR/block
│ ├── watch/ # poll loop + claim + cancel socket
│ ├── job/ # SQLite job table + outcome history
│ ├── gh/ # GitHub client with rate-limit retry
│ ├── git/ # worktree and branch management
│ └── codename/ # deterministic adjective_name per job
└── docs/adr/ # design decisions
Isolation and concurrency
- Worktree isolation — concurrent jobs never share a checkout.
- Width — an in-memory semaphore bounds concurrent jobs per repo.
- Cross-machine dedupe — the claim label, not any local state, is the authority across machines.
- Crash recovery — a fresh watcher clears only its own stale in-flight
rows and reconciles issues whose
romp-Nbranch already has an open PR.
Observability
Every job gets a codename — an adjective_name pair like sunny_naruto,
derived deterministically from the repo and issue number. The codename prefixes
every log line, names the per-job log file, and is the primary column in
status.
State lives in one SQLite file per machine: ~/.local/state/romp/romp.db. The
jobs table holds exactly the in-flight set; finished jobs move to the
append-only outcomes table in one transaction.
Design decisions (ADRs)
| # | Decision | Status |
|---|---|---|
| 0001 | An explicit verify command, independently re-run | accepted |
| 0002 | Prompt contract; outcomes via .romp/ files |
accepted |
| 0003 | GitHub is the source of truth; ephemeral worktrees | accepted |
| 0004 | TOML layered with a zero-means-default overlay | accepted |
| 0005 | Atomic claim, configurable claim label, in-flight job table | accepted |
| 0006 | Job codenames, status, per-job logs, and gc | accepted |
| 0007 | Poll the trigger label in v0; webhooks deferred | accepted |
| 0008 | Shared SQLite file and append-only outcome history | accepted |
| 0009 | Cancel over a Unix socket; logs tail files | superseded by 0010 |
| 0010 | One machine-wide daemon, one socket, HTTP clients | accepted |