Ouroboros · an infinite loop
borris.dev · GitHub ↗

Run your agent all night.

Claude Code · Codex · Pi · open source · Python 3.12+ · all state is files in your repo

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.

Fig. 1. The loop. In each iteration, the actor makes one change, the critic keeps or reverts it, and the scribe writes down what happened. This ring is a demonstration; in a run report, the highlighted stage is the one running now.
Roles
3no others
Harnesses
3equal citizens
Waits on you
0never blocks
Stops when
yousay so
Last verdict
continue

Usage

claude weekly window21%+1 this run
claude 5-hour window34%+7 this run

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

  • met
    Never block.No step of the loop waits for a human. The critic answers the actor’s questions as you would, from your question policy.
  • met
    Never 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.
  • met
    Always leave memory.Every iteration writes a record that the next one reads.
  • met
    Always commit.Every change lands on the ouroboros/<run> branch. No change, no commit.
  • met
    Harness-agnostic.Claude Code, Codex and Pi are equal. Each role can use a different one, with its own fallback chain.

Open problems

  • broken
    The agent asks a question and waits.Fixed: the critic answers it, every iteration.
  • broken
    The agent says “done” too early.Fixed: the critic refuses a claim of completion that the diff and the tests do not support.
  • broken
    The agent crashes or hits a limit, and stops.Fixed: limits are classified, reset times are parsed, and the role falls back to another harness.
  • open
    The 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

RoleWhat it doesVerbs
ActorPicks one unit of work from the critic’s message. Does it to the end, tests it, and reports it.orient show
CriticOne 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
ScribeRecords 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.

CounterCounts iterations thatFires at
no_productchanged only memory files, not the product8
no_frontierleft the frontier unchanged30

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

ObservedChange
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.

Stateclaims, written by a fold Recordsappend-only, one per unit engine correct offline wheelhouse bench command bench.csv rows expectimax bot 2048 in 5 of 20 r-0001directive r-0002engine r-0003bench r-0004expectimax r-0005packed boards fold f-0001 f-0002

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

#VerdictWhat happenedNextTookRecord
4continueAdded 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 minr-0005
3done, acceptedAdded 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 minr-0004
2continueAdded 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 minr-0003
1continueBuilt 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 minr-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.

Install
git clone https://github.com/theo-kirby/ouroboros.git
cd ouroboros
uv tool install --editable .
ouroboros skills install --user
In the repo you want worked on
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.

Read the source and the design ↗