Run your agent all night.
You write a long prompt. You go to bed. After 5 minutes the agent asks a question, and it waits 10 hours. Or it says “done” after 20 minutes, because it read the goal wrong. Or it hits a rate limit, and it stops.
Ouroboros makes all three impossible. You set the goal. It runs your agent on the repo again and again: one agent makes a change, a second agent checks it, a third writes down what happened. The loop continues until a stop condition that you set.
- Roles
- 3no others
- Harnesses
- 3equal citizens
- Waits on you
- 0never blocks
- Stops when
- yousay so
- Last verdict
- continue
Usage
From a real run (2048 bot, section 5). A subscription rations by window, so a run meters windows, not dollars. max_usage stops the run at a fraction of the weekly window.
Progress toward done
5 of 5 met · the non-negotiables; they win over every other rule
- metNever block.No step of the loop waits for a human. The critic answers the actor’s questions as you would, from your question policy.
- metNever stop early.Only a stop condition you configured ends a run. A usage limit switches to a fallback harness, or waits for the window to reset.
- metAlways leave memory.Every iteration writes a record that the next one reads.
- metAlways commit.Every change lands on the
ouroboros/<run>branch. No change, no commit. - metHarness-agnostic.Claude Code, Codex and Pi are equal. Each role can use a different one, with its own fallback chain.
Open problems
- brokenThe agent asks a question and waits.Fixed: the critic answers it, every iteration.
- brokenThe agent says “done” too early.Fixed: the critic refuses a claim of completion that the diff and the tests do not support.
- brokenThe agent crashes or hits a limit, and stops.Fixed: limits are classified, reset times are parsed, and the role falls back to another harness.
- openThe agent keeps busy but ships nothing.Watched, not solved: two mechanical counters name the loop and change the model. See 3.3.
These are the problems of an unattended agent, not of Ouroboros. The first three are closed.
Findings
3.1 Three roles, and no others
| Role | What it does | Verbs |
|---|---|---|
| Actor | Picks one unit of work from the critic’s message. Does it to the end, tests it, and reports it. | orient show |
| Critic | One read-only call after every actor turn. Grades the change against your quality bar, answers questions as you would, refuses false “done”, names loops, and writes the next prompt. A rejected change is reverted. | orient show check |
| Scribe | Records the unit in memory (a rejected one as a dead end), folds the memory when a fold is due, and keeps the run’s page up to date. | record fold render |
The actor reads, the critic reads and checks, the scribe writes. No agent can edit the charter.
3.2 The charter
Your goal lives in .ouroboros/goal.md. ouroboros design writes it with you, in an interview. The critic picks each next step from it.
# Goal: <name>
## Mission one paragraph: what and why, priorities in order
## Done criteria - [ ] claims about the world, not tasks
## Horizon ladder short-term, medium-term, long-term
## Constraints never do X; always keep Y green
## Question policy how to decide for me: "prefer the reversible option"
## Quality bar what the critic grades against
3.3 Motion is not progress
A loop that keeps working can ship nothing: records about records, audits of audits, plans that restate the last plan. All of these write files, so a diff looks like progress. One real run is the worked example:
- Iterations
- 201
- Lines changed
- 22,437
- Records written
- 128
- Frontier moved
- 1node
So Ouroboros counts two things that an agent cannot argue with, because an agent asked “are you looping?” always finds a reason why this time is different.
| Counter | Counts iterations that | Fires at |
|---|---|---|
no_product | changed only memory files, not the product | 8 |
no_frontier | left the frontier unchanged | 30 |
Step 1: name the loop in the next prompt. Step 2: change the model. There is no step 3. A loop is a thing to break out of, not a thing to die of.
3.4 What the first night taught
| Observed | Change |
|---|---|
| A session limit was not seen as temporary. 63 of 90 iterations were empty commits and pointless verdicts on 0-byte patches. | Limits are classified. The reset time is parsed, and the role falls back to another harness instead of waiting. |
| At iteration 50, the overseer rejected “done” with “0 of 3” when all three had shipped. It counted boxes in a static file. | Done criteria became gaps in memory. The critic judges against the memory, not against ticked boxes. |
| The merged branch had 173 commits; 132 were empty. | No change, no commit. |
Memory
Records are append-only. Each one says what it changes. State is a set of claims that cite records, and only a fold writes state. The next actor reads the state and the records not folded yet. Without a graph database, the same model lives as markdown files under .ouroboros/graph/, committed with the code.
workingopenrecordfolddashed line: a claim cites a record
Fig. 2. The memory of a real run. The 2048 bot run, simplified. One claim is still open: the critic found the offline test setup fragile and recorded it.Timeline
A real run: 2048 bot, 17 Oct 2026. The charter: build a correct 2048 game and a bot that plays it, then make the bot better, one change at a time, with every claim a number from a seeded benchmark. 4 iterations in 29 minutes; all 4 done criteria met; 0 reverts.
actorcriticscribekeptheld backrevertedmemory foldedbar height: time taken · x: iteration
| # | Verdict | What happened | Next | Took | Record |
|---|---|---|---|---|---|
| 4 | continue | Added bit-packed boards with 65536-row move tables; the bot got 3× faster, and depth 3 now gets median 40920 in 173 s. foldedCritic: The packed boards and move tables are correct and tested, depth 3 is benched honestly, and the medium-term rung “search deeper” is done. | Tuning the heuristic weights against the seeded benchmark. | 13 min | r-0005 |
| 3 | done, accepted | Added a depth-2 expectimax bot. It reaches 2048 in 14 of 20 seeded games in 21 s, with 9 new tests.Critic: 14 of 20 closes the last done criterion. The diff, the tests and the CSV row support the claims. | Speeding up moves with lookup tables, so that a depth-3 search fits in 5 minutes. | 9 min | r-0004 |
| 2 | continue | Added the seeded bench command, random and greedy baseline bots, and bench.csv with their first two rows.Critic: The bench command, the seeded repeatability, the CSV rows and the two bots all match the diff, and the tests are green. | Building an expectimax bot and benchmarking it on the 20 seeded games. | 2 min | r-0003 |
| 1 | continue | Built the 2048 game engine with 51 rule tests. foldedCritic: The engine and its 51 rule tests match the first done criterion. The diff supports the claims. | Building the seeded benchmark command, with two baseline bots. | 4 min | r-0002 |
The run continued after “done, accepted”: the charter’s horizon ladder still had rungs. Done is a verdict, not a stop condition.
Install and run
You need Python 3.12+, uv, and at least one agent CLI that is logged in: claude, codex or pi.
git clone https://github.com/theo-kirby/ouroboros.git
cd ouroboros
uv tool install --editable .
ouroboros skills install --user
ouroboros init # config and charter template
ouroboros design # write the charter with an agent
ouroboros run --for 8h # start the loop
ouroboros top # watch it
ouroboros report # read what happened
Start with a clean working tree. Read the report and the run branch before you merge. ouroboros watch pushes alerts and a digest every 4 hours to your phone.