An autonomous software loop. O watches its own issue board, dispatches coding
agents into isolated git worktrees, verifies their work with your real test
suite, auto-merges what passes, auto-reverts what regresses, budgets itself by
the day, and files its own follow-up issues — from failures it catches and from
a daily pass where it reflects on its own recent work. It runs as one native
daemon (swarmd) built on swarmrt.
There is no queue, no Postgres, no container — an embedded SQLite file is the
whole control plane.
┌──────────────────────────────────────────────┐
│ swarmd │
│ │
issue board ──┼─▶ tick ─▶ dispatch ─▶ agent in git worktree │
▲ │ (claude-code / swarm-code / sim) │
│ │ │ │
│ │ ▼ │
│ │ verify (your test suite) │
│ │ │ │ │
│ │ green│ │red │
│ │ ▼ ▼ │
│ │ --no-ff merge retry ─▶ Failed │
│ │ │ │ │
│ │ post-merge verify │ │
│ │ │ │ │ │
│ │ ok │ │ regressed │ │
│ │ ▼ ▼ ▼ │
│ │ Done revert + file follow-up │
└────────┼──────────────── fix issue ◀──── issue (auto) │
│ │
│ daily: rollover budgets · reflection pass │
└──────────────────────────────────────────────┘
O builds with the swarmrt toolchain. Clone swarmrt as a sibling of this repo (the default the Makefile expects), then build:
git clone https://github.com/skyblanket/swarmrt # sibling of this repo
git clone https://github.com/skyblanket/o
cd o
make # -> bin/swarmd (or: make SWARMRT=/path/to/swarmrt)
make check # hermetic end-to-end test suiteRun the daemon and the web UI together:
make dev # swarmd on :4100 + the React UI on :5173
open http://localhost:5173Point O at a repo it should maintain — including itself:
./scripts/seed-maintainer.sh # an "O Core" company that maintains this repo
./scripts/seed-example.sh # a sim-runtime demo company, no tokens spentThe control plane lives at ~/.o/o.db; per-issue worktrees live under
~/.o/workspaces/. Nothing else is created.
O edits real repositories on its own. The guard rails are the point.
- Grounded verifier. A change is judged only by running the company's real
verify_cmdin the worktree — never by the agent's own claim of success. A run that makes no commit, that touches a protected path, or whose verify command fails does not merge. Verdicts are recorded per run:red_no_commit,red_protected,red_tests, and so on. - Protected paths. Each company declares
protected_paths. An agent may not change them; a commit that does is rejected (red_protected) before it can merge. The test harness and this loop's own invariants live behind this fence, so an agent maintaining O cannot edit the checks that gate its merges. - Everything on a branch. Each issue runs on
o/<issue>in its own worktree. Green work lands with a--no-ffmerge that names the branch. If the merge regresses the base (post-merge verify goes red), O reverts the merge on the base branch and keeps the branch for forensics. - Budget circuit breakers. Every run is metered onto the company's daily and
monthly budgets. Hitting a cap pauses the company (
budget-daily/budget-monthly). A separate no-signal breaker pauses a company that burns budget across days without shipping a single verified-green merge. - Bounded self-filing. When O files its own follow-up work (a regression to fix, a repeated stall, a no-signal diagnostic, a reflection idea) it is guarded: depth-1 (an auto-filed issue that itself fails files no child, so a red loop can't fork-bomb), fingerprint dedup (one open issue per failure fingerprint), and a daily cap per company.
feedback/o-feedback.js is a dependency-free annotation layer for your dev app.
Toggle it (Alt+Shift+A), click any element, type what should change, batch a few,
send — O files a single origin='feedback' issue the loop picks up like any
other work item.
<script
src="/o-feedback.js"
data-endpoint="http://127.0.0.1:4100"
data-company="co_XXXXXXXX"></script>Endpoint contract:
POST {endpoint}/api/companies/{company}/feedback
{
"page_url": "http://localhost:3000/dashboard", // required, 400 if missing
"annotations": [ // required, non-empty, 400 if empty
{ "selector": "#hero > h1", "html": "<h1>…</h1>",
"styles": { "color": "rgb(0,0,0)" },
"instruction": "make the heading bigger" }
]
}
201 returns the created issue (origin: "feedback", one ### Annotation N
section per annotation in the body). Filed outside the self-file cap, so operator
feedback is never dropped. Full docs: feedback/README.md.
An agent does real work through a pluggable runtime, chosen per agent:
| runtime | what it is |
|---|---|
claude-code |
headless claude -p in the worktree (default). |
swarm-code |
the swarm-code agent, run in the worktree. |
sim |
a deterministic, no-token stub — drives the pipeline in tests. |
Backlog ─▶ In Progress ─▶ Verifying ─▶ Done
│ │
│ ├─▶ Failed (verify red, attempts exhausted)
│ └─▶ Reverted (regressed the base after merge)
└──────────────── Canceled (operator)
Every transition and decision is written to an append-only activity log
(GET /api/activity). Verbs include: run.dispatch, run.succeeded,
run.failed, run.failed_final, run.stalled, verify.start, verify.pass,
verify.fail, merge.queued, merge.done, merge.revert, merge.conflict,
merge.dirty, selffile.created, selffile.duped, selffile.capped,
selffile.skipped_depth, budget.reset, budget.exceeded, budget.no_signal,
reflect.spawn, reflect.filed, reflect.done, feedback.filed,
issue.create, issue.update, agent.hire, company.create.
The daemon reads its configuration from the environment. All are optional.
| variable | default | meaning |
|---|---|---|
O_DB |
~/.o/o.db |
control-plane SQLite path. |
O_PORT |
4100 |
HTTP port. |
O_CLAUDE_BIN |
claude on PATH |
claude-code binary for the claude-code runtime. |
O_SWARM_BIN |
swarm-code on PATH |
binary for the swarm-code runtime. |
O_MAX_TURNS |
50 |
per-run turn cap passed to claude-code. |
O_VERIFY_TIMEOUT_S |
600 |
wall-clock cap on a single verify command. |
O_GLOBAL_DAILY_CENTS |
unset | global daily spend ceiling across all companies; dispatch stops for the day when met. |
O_SELF_FILE_CAP |
10 |
max auto/reflect issues one company may file per day. |
O_NO_SIGNAL_DAYS |
2 |
consecutive no-signal days before the breaker pauses a company. |
O_SWARM_FLAT_CENTS |
25 |
flat per-run cost charged for a swarm-code run (it reports no telemetry yet). |
O_REFLECT |
unset | set 1 to enable the daily reflection pass. |
O_REQUIRE_APPROVALS |
unset | set 1 to route agent hiring through /api/approvals. |
curl http://localhost:4100/api/health
curl http://localhost:4100/api/companies
curl -X POST http://localhost:4100/api/companies \
-d '{"name":"Core","issue_prefix":"COR","repo":"/path/to/repo",
"verify_cmd":"make test","protected_paths":"ci/,scripts/",
"budget_daily_cents":5000,"budget_monthly_cents":100000}'
curl -X POST http://localhost:4100/api/companies/<id>/agents \
-d '{"name":"Maintainer","runtime":"claude-code","repo":"/path/to/repo"}'
curl -X POST http://localhost:4100/api/companies/<id>/issues \
-d '{"title":"first task","priority":"high"}'
curl http://localhost:4100/api/orchestrator # live scheduler snapshot
curl http://localhost:4100/api/runs # run history
curl http://localhost:4100/api/activity # activity log- Single-node. One daemon, one SQLite control plane, one machine. There is no clustering and no multi-writer coordination.
- The machine is the trust boundary. Agents run with
--dangerously-skip-permissionsinside their worktrees — they can run arbitrary commands as your user. Protected paths and the verify gate constrain what merges, not what an agent may do while working. Run O only against repos and on machines where that is acceptable. - No auth on the HTTP API. Bind it to localhost. Anyone who can reach the port can drive the loop.
- swarm-code cost is estimated. swarm-code emits no usage telemetry yet, so
its runs are metered at a flat per-run estimate (
O_SWARM_FLAT_CENTS) rather than true token cost.
O is written in the sw language and runs on the swarmrt runtime —
https://github.com/skyblanket/swarmrt.
MIT — see LICENSE.