all posts

Agents and loops

Everyone has an agent that writes code now. That stopped being the interesting part some time ago.

The interesting part is what surrounds it: the thing that decides what to build next, judges whether the work is any good, and knows what to do when it isn't. An agent answers a question you asked. A loop keeps asking. Our launch post called shipped software a closed loop — someone imagines what's needed, encodes it once, and ships something frozen. Auto Loop v1 was our first attempt at opening that loop on the building side: you write down what you want, step away, and come back to software that got made.

This is the first of three posts. This one is about what v1 was and how it worked. The second is about what it actually built — nearly a hundred games, an e-reader OS, firmware, an iOS app that reached TestFlight. The third is about v2, and about the things v1 could not do.

A skill, not a service

The first decision was the one that shaped everything else: v1 shipped as a Claude Code skill, not a platform.

There was no hosted runtime, no container to configure, no adapter to write per language. It ran inside a Claude Code session on your own machine, in your own repository, with your own toolchain already installed. Starting a run meant typing /autoloop in the directory you were already working in.

That sounds like a modest packaging choice. It was the reason the whole thing generalized. A loop that runs where you already work inherits everything you already have — your compiler, your simulator, your test runner, your credentials, your hardware plugged into a USB port. We never taught it Kotlin, or ESP-IDF, or how to paginate an EPUB. We pointed it at a directory where those things worked and let it use them.

The cost of that decision shows up later. Hold onto it.

Writing down what you want

A run begins with a vision.json — the only artifact you author by hand, usually through an interview rather than a blank file.

It holds goals, which are the broad things you want, and scenarios, which are the specific things that must be true. A goal is "Advanced Nutrition." A scenario under it is "logging foods with protein, carbs and fat accumulates daily macro totals and computes ring progress toward per-macro goals."

The scenario is the unit that matters, and it carries three things beyond its description:

  • a rubric — what good looks like, in your words
  • a threshold — the score this scenario has to clear to count as met
  • a test — the command that proves it

All three are written before any code exists. This is the part people skip, and it is the whole design. A bar invented after the work is a bar the work has already cleared; it grades the result against a standard that was reverse-engineered from the result. Writing the bar first is the only way it can ever say no.

anatomy of a scenario
vision.json GOAL Advanced Nutrition 1 of 6 SCENARIO Macro tracking against goals 1 of 848 RUBRICwhat good looks like, in your words THRESHOLDthe score it has to clear to count as met TESTthe command that proves it ALL OF IT AUTHORED BEFORE ANY CODE EXISTS
A goal is broad; a scenario is the unit that gets scored. Vivora's vision carried six goals and 848 scenarios — every one of them with its own rubric, threshold and test, written at the start.

The vision is also not a document that gets read once at kickoff and then quietly diverges. It's the state the loop runs against for the entire run — the thing tasks are tagged to and scores are recorded against.

One turn

From the vision, the loop writes a plan: phases, then tasks inside them, each task tagged with the scenarios it advances. Tasks are kept small on purpose.

Then it runs a plain while loop until the tasks are gone. One turn looks like this:

Pick the next queued task and mark it running. The status flips before any code is written, not after, so a run that's watched from outside is watched honestly.

Dispatch exactly one implementation agent, scoped to that single task. Not the phase, not the plan — the task. One unit of work, one train of thought. The implementer writes code and it writes tests, and it does not report on itself; status is the orchestrator's job, and separating the two keeps the thing doing the work from also being the thing narrating it.

Require a real test per scenario. Not a claim that it works. An executable test that fails before the feature exists and passes after. If the implementer comes back saying it couldn't write a passing test for a scenario, that scenario stays unmet — the work may still be committed, but the claim isn't.

Commit the code and its tests together, so the evidence and the change are one object in the history rather than two things you have to correlate later.

Re-run the tests, score the scenarios the task touched against their rubrics, and record what happened — including bugs found, and a revision when the score comes in under the threshold.

Then take the next task.

one turn
pick the next task, mark it running commit code + tests together re-run tests, score each scenario against its rubric ONE IMPLEMENTATION AGENT implement this task — a real test per scenario dispatch one implementer code + tests come back then met — take the next task score short — revise
The top row is the loop session; it records status and never writes code. The bottom row is the one agent that does, scoped to a single task. The orange edge is what makes this a loop rather than a pipeline: a scenario that scores under its threshold goes back, and the run does not move on.

That's the whole machine. The reason to describe it so plainly is that there's nothing clever in it, and it worked anyway. Most of what made runs succeed was refusing to batch: one task at a time, one implementer at a time, one commit per unit, each scenario scored on its own terms.

What one task at a time buys you

It's tempting to fan out. A plan with sixty tasks and no dependencies between most of them looks like an obvious candidate for parallelism, and running them all at once is the version that demos well.

Sequential turns buy three things instead.

The first is a readable history. Each unit of work is one commit with its tests attached, tagged to the scenarios it advanced. When something is wrong three days later, the history is a series of small verifiable steps rather than a merge of many concurrent ones.

The second is that failure stays local. A task that goes wrong goes wrong by itself, gets scored short by itself, and gets revised by itself, without taking a batch down with it.

The third is that intent stays reachable. Every turn is picked from a plan that came from the vision. When work is spread across many simultaneous agents, the thing they're collectively building starts drifting from the thing you wrote down, and no single agent is in a position to notice.

The claim

Underneath all of it is one bet, and it's a falsifiable one: a loop does not need to be given the domain details — it can learn them.

If the vision carries the intent, and every scenario carries its own bar and its own test, then the machinery in between shouldn't care whether the thing being built is a browser app, a native mobile app, or firmware for a device on your desk. The vision changes. The loop doesn't.

That's a claim you can't argue your way to. You have to point the same machinery at genuinely different problems and see what comes back — which is what we spent July doing, across five projects, four kinds of software, and more than three thousand commits.

That's the next post.