One Loop, Three Minds (Creatura, Part 2)

A creatura is one loop with a regulator in it. Everything else is a slot, a seat, or a world.
Where Part 1 left off
In Part 1 I said that what came out of those two weeks in China was "a single agent loop with three interchangeable designs." This post explains what that sentence means in code.
The theory behind the designs is already in the Agents That Defend Goals series, so I won't repeat it. This post is about the shape of the framework: where the parts go and how they fit.
The code is public on GitHub: github.com/egarim/creatura. Everything below points at real types you can open there.
The project's icon is a winged lion, and the anatomy page in the repository labels it part by part. The head is the seat, where each choice is made. The mane is the regulator, the design wrapped around the seat. The halo is the control layer, which watches every act from above. The star is the task, the goal it steers by. The body is the loop, which carries everything tick after tick. The raised paw is acting, the only part that touches the world. The tail's tuft is the extensions. The ground it stands on is the bench, where it is trained and tested.
The caption on that plate sums up the architecture better than I can: the head chooses, but only among what the halo lets it see, and the halo judges every act of the paw by what happened in the world rather than by what the paw reports.
The rest of this post is that sentence, unpacked.
The membrane
Most agent frameworks put the model at the center: it picks a tool, the framework runs it, and whatever the tool returns goes back to the model as truth.
Creatura puts something in between. I call it the membrane, and in the code it is the ControlLayer. The model never touches the world directly. Every request to act passes through the layer, and so does every answer.
The layer has four parts:
- Visibility decides which tools the seat may see this tick. A tool the layer does not trust is simply not offered. With a real model, its schema never reaches the request.
- The Gatekeeper decides whether a requested tool may run. If the seat names a tool it was not offered, the request is refused, never obeyed. A "release" tool, one that ships something, also needs an earned track record before it is allowed.
- The EffectCheck asks the world whether the promised facts actually hold after the tool ran. This is the important one.
- The Ledger is the layer's own memory: reliability, predictions, failures, the trace.
The world in Creatura is a registry of facts plus tools that declare what they require and what they grant. A tool can do three things when it runs: deliver, fail honestly, or lie. Lying means it reports success and changes nothing.
The EffectCheck never takes the report's word for it. It reads the registry. If the fact the tool promised is there, the effect landed. If it isn't, and the tool said it succeeded, that is a lie. The liar is quarantined, hidden from the seat for a few ticks (three by default), and the lie never counts as progress, because "done" is a predicate over the facts, not anything the seat or a tool says.
The Quick start in the README is exactly this. A toy "restore the service" world, one tool made to lie once:
var world = RestoreServiceDomain.NewWorld();
world.MakeLiar("apply-patch"); // reports success once, changes nothing
IAgent plain = new ReferenceLoop(new PlainRegulator(), name: "plain loop");
var run = await plain.RunAsync(world, maxTicks: 40);
PlainRegulator fills no slot at all. It has a name and nothing else. And it still gets the whole membrane: only visible tools offered, every effect checked against the facts, the liar caught and quarantined. The protection is not a feature of a clever design. It is the floor every design stands on.
One loop, many slots
The loop itself is ReferenceLoop, and its tick is always the same sequence. The anatomy page draws it as: offer, decide, admit, act, judge, record, stop?
What changes between designs is not the loop. It's what fills the loop's slots. That's the job of IRegulator, and the comment on the interface says it plainly: a new design is a new regulator, never a new loop.
Each slot is a question the loop asks the design:
- Error: how far are we from where we should be? A number from 0 to 1.
- Freedom: what does that error buy? By default, more error buys more room to improvise and a larger retry budget. The default rule even keeps a little freedom when the error is zero, so a calm agent can still adapt.
- Seat: what should the seat read beside the facts and the visible tools?
- Stop: should we stop before asking the seat, and hand off to a person?
There are more (shape the options, hold an action, record an outcome, sleep at the end), but every member has a default. A regulator implements only the slots its design needs. The README's own example of a custom design fills four of them in a dozen lines:
public double Error(RegulatorContext c) => 1.0 - c.Task.Progress(c.World);
public double Freedom(RegulatorContext c, double error) => Math.Min(0.5, Plasticity.Freedom(error));
That cautious operator measures error as the share of the task still undone and caps its freedom at half, so its retry budget stays small. Its Stop slot hands off to a person when it's past tick 12 with less than half the task done.
Three minds in the same slots
Now the three designs from Part 1. In the code, swapping one in is one line:
IAgent bach = new ReferenceLoop(new SelfModelRegulator(Identity.Bach, stage: 3), name: "Bach");
IAgent levin = new ReferenceLoop(new ColonyRegulator(Identity.Levin, leak: 0.5),
layer: ControlLayerOptions.Design with { ClearQuarantineOnNoRoute = true }, name: "Levin");
IAgent peterson = new ReferenceLoop(new MapKeeperRegulator(),
layer: ControlLayerOptions.Design with { JudgeRule = JudgeRule.ReportFirst }, name: "Peterson");
Same loop. Same membrane. What differs is what each one puts in the slots.
Bach, the SelfModelRegulator. Its error is incoherence: how badly its own record of itself disagrees with what happened. Its Seat slot hands the seat a rendered self-model to read before it acts. Its right to act at all is staged, gated behind a record of predicting consequences correctly. A contradiction quarantines the belief, and past the budget it halts. (Part 2 of the theory series has the full argument.)
Levin, the ColonyRegulator. No self-model at all. It's a stack of nested controllers with three set points, each in its own space: the cell (did my tool's effect land?), the tissue (are the obligations closing?) and the organism (is the contract met?). Stress leaks between them along a shared bus, and its error is one channel's stress plus what leaks in from its neighbours. The organism's only lever is to widen the budget or hand the goal to a human. It regulates without representing itself. (Part 1 of the theory series.)
Peterson, the MapKeeperRegulator. This one is new since the theory series. It acts from a nested stack of stories: what is, what should be, how to act. Every outcome it didn't predict is an anomaly, and the anomaly's size is the share of the stack it breaks. Its error is the larger of that anomaly and how unexplored the visible territory is. When something surprising happens, it goes toward the anomaly first, one deliberate retry, before it revises the map. A broken story collapses on its own and hands its problem up one level, and the root contract is the last story to fall. It has no retry budget: its attempts are stories, not a count.
Notice the layer options, too. Levin's layer drops its distrust as soon as the seat sees no route, instead of waiting out the quarantine. Peterson's layer judges with a report-first rule: a tool that reports failure is an honest failure, and only a report of success with nothing landed is a lie. Those are switches on the same membrane, not a different membrane.
There are two more regulators in the same folder, a hybrid of Bach and Levin (theory Part 3) and an orienting one after Sokolov. They also plug into the same loop.
Extensions and the critic
Regulators are designs. Extensions are behaviours you hang on the loop's verbs without touching the design: re-judge an outcome, veto a hand-off, add a note for the seat to read, propose a step when the seat sees no route.
IAgent explorer = new ReferenceLoop(new ColonyRegulator(Identity.Levin),
options: AgentOptions.With(new HonestFailureExtension(), new ExplorationExtension(memory, scaled: true)),
name: "Levin + honest + explore");
That's Levin's colony learning to tell an honest failure from a lie, and exploring untried tools when its map runs out. Another extension carries a design's record from one case to the next, so case ten knows what case one learned.
Then there's the critic, a watcher with no hands. It can wrap the world or ride the loop as an observer-only extension, and the trace rows are identical with or without it. It belongs to a person, judges each action against the facts on its own, and reads the route afterwards. (Theory Part 7 is about where it came from.)
The seat
The head of the lion is the seat, and in the code it's a very small interface:
public interface IPolicy
{
string Name { get; }
Task<PolicySuggestion> NextAsync(PolicyContext context, CancellationToken cancellationToken = default);
}
The seat gets a PolicyContext: the facts, the visible tools, the goal, the tick, the freedom, the self-model if there is one, the history and the notes. It answers with one visible tool, or none. The architecture decides what goes into the context, so the seat can't route around the design's constraints even when it's a language model.
That's what lets the same agent run with different things in its head. SeatResolver turns a name into a seat:
bfs: the built-in breadth-first planner, a search over tool effects. Deterministic, offline, no key needed.scripted:<path>: a file of tool ids replayed one per tick. A scripted tool that isn't visible is refused, just as a model's would be. The script can't route around the architecture either.model:<name>: a model from the catalog, called throughModelPolicy. Only a visible tool id or no action counts as an answer, and a hidden tool named is refused, never obeyed.
The anatomy page also names a trained network as a seat; that deserves its own post.
The same membrane on Microsoft Agent Framework
If your agents already run on a framework that owns the model call and the function-calling loop, Creatura.Hosts.AgentFramework puts the same control layer inside Microsoft Agent Framework, as function-calling middleware.
The rules don't move. They stay in the Core's ControlLayer. The host only maps the framework's hooks onto the layer's verbs: before the request leaves, distrusted tools are removed from it so the model never gets their schema; before a tool runs, Admit; on the tool's answer, Judge. The model only ever sees the string the layer returns. Theory Part 5 tells the story of building it.
What a run prints
Back to the Quick start. The sample runs every regulator against the same toy world with the same liar in it. According to the README, every regulator reaches the goal in 7 to 9 ticks. Every one catches the same lie at tick 3, each in its own vocabulary: "effect missing", "contradiction", "anomaly, approaching next tick", or a punishment followed by orienting and one approach. And the critic closes each case with eight claims, eight judgements and one flag.
Different minds, different words for what went wrong, the same lie caught on the same tick, because the thing that catches it isn't the mind. It's the membrane.
This is Part 2 of a series about Creatura. Next: Part 3, Four Hypotheses That Failed, the bench where creaturas are trained and tested in worlds built to lie.