14 The loops
This chapter is about keeping a domain’s two loops running when nobody opens a session: what a run does on its own, how you start one on your own machine and how a team sets one up on its shared instance. Crystalline has no model of its own, so somebody has to come by (Chapter 12), and here that somebody comes on a timer.
14.1 The heartbeat
Every domain has two lists in Crystalline. The reflect queue holds what the Reflection needs (a person’s change to weigh, a thread waiting for an answer, a page whose engrams moved), and the sweep holds what the knowledge needs (Chapter 10). Neither list does anything by itself: work leaves it only when an agent takes it, and in a session that’s when the reminder comes up and you say yes.
The loop is the heartbeat of the Reflection. It beats on a schedule you pick (every few minutes, every night, every Sunday), and each beat asks one question: is anything due? Most of the time the answer is no, and the beat ends right there. No agent starts and nothing is spent. When something is due, the beat wakes an agent, which works the list and goes back to sleep. Keep it beating and the question about Lambert’s change is in her thread the same evening. Without a loop, the domain looks fine for a week, and then you notice the threads getting older and the pages still naming last month’s pick. Fluid shows the pulse: the Reflection home says when the domain was last reflected, by which runner and how it ended, and the maintenance page does the same for the sweep.
If you write software, the loop most closely resembles a Kubernetes reconcile loop. The engrams and the pact are the desired state, the pages are the actual state and the queue is the difference. The loop works the difference until none is left, and it never assumes it’s done just because it ran. Most engineers know the event loop better, and that picture works too: the queue holds the events, the beat is the tick and the agent is the handler. The picture breaks in two places: nothing spins between beats, and taking an item doesn’t consume it (the queue is computed fresh each time, so an item stays until the agent has really settled it).
14.2 Two loops, one shape
Figure 12.2 showed the two lifecycles of a domain, and each gets a loop of its own. The reflect loop works the reflect queue: it weighs what people changed, answers threads and brings changed engrams into the pages. The evolve loop works the sweep, but only the mechanical findings, the ones that complete intent the archive already records.
For both, the runner first asks whether anything is due and ends at once when nothing is. Otherwise it takes the lease, so only one run of a loop works a domain at a time. It starts an agent with a fixed set of instructions and lets it work up to a cap (twenty items by default), then shares what changed and records how the run ended. What the agent doesn’t reach stays due for the next beat.
The reflect loop runs only where the domain’s Reflection lives. Every other instance says where it runs and never starts it, so five teammates with the same team domain don’t start five reflect runs. A team domain runs its scheduled evolve loop in one place too, its evolve home: the first scheduled run on an instance with a public address claims it, and that run’s pull request carries the claim to the team. Every other copy names the home and refuses a scheduled run, and the sweep in a session still works everywhere. A domain only you keep runs evolve wherever it lives.
14.3 What a run does without you
A run has nobody to ask, and its instructions say so: the agent never waits for a yes. When it needs a person’s answer, it asks where people will see it: the reflect agent opens a thread on the page, and the evolve agent names its question in the run’s summary. Anything that needs a yes is left for a person: a judgment finding, a delete, a move, a page a person wrote. The tools for deleting, moving, splitting and sharing are switched off for the run, and its instructions rule out the rest. An item no run can settle isn’t picked up again until it changes (a new message in its thread, a new edit), so a question waiting for Lambert doesn’t start a run every five minutes all day.
A run also reads only the domains everyone who reads its own domain may read, and it writes only into its own domain. Anybody who may comment on a page can ask a question in a thread, and the run that answers can’t draw that answer from a private domain the page’s readers may not open (Chapter 20).
On a team domain the run’s engram changes arrive as a pull request, never as a direct share, whatever the domain’s sharing setting says. That’s the same process a person goes through (Chapter 17): an agent working alone at night makes mistakes like anybody else, and the team reviews its work in the morning. The pull request carries only what the run wrote, and a file somebody was still working on is held back for that person. Reflection pages are never part of it (the domain’s home keeps them and backs them up as always).
A run that works ends with a short summary, and in the club it reads like this:
Done: 4
Left for a person: 1
Errors: 0
Thread on the Dune page: asked Lambert what moved Dune
from March to May. Left open for her.
A run that fails doesn’t hammer the domain: the loop waits longer after each failure in a row, and the first run that works ends the wait.
14.4 On your own machine
Your own machine is the entry level, and you have three ways to keep the beat:
- a session now and then, where you say yes when the reminder lists what’s due (you know this one already);
- a scheduled run, once a night or once a week, that does the same thing without you;
- a watcher that checks every minute and runs as soon as something is due, handy when people wait for answers.
A run on your own machine starts Claude Code as its agent (so Claude Code has to be installed) and acts as you. It can’t tell its own writes from yours, so don’t edit the domain on that machine while a run works it. On a machine only you use, that’s rarely a problem: the run happens at 6:30 on a Sunday, and you’re asleep. While it works, your own sessions on that machine read and write the way the run does, and every answer says so in one line.
Your books domain keeps growing, and the sweep you keep meaning to run hasn’t happened in months. You ask your agent to make it happen on its own: every Sunday morning the evolve loop should look at books and fix what it may fix. It sets up the schedule and runs the loop once to check, and the first run finds three links that point at titles you spelled differently in the reading log and fixes them.
The run leaves “Wait for the paperback” alone, although its review date has passed: whether the rule still holds is a judgment, and only you can make it. The next time you open a session, the sweep’s reminder brings it to you as a question. Most Sundays after that, the run is over within a second, since nothing’s due.
crystalline evolve run --domain books and crystalline reflect run --domain books run one beat: they check what’s due, take the lease, start claude with the shipped prompt and only the Crystalline MCP server, share, record and release the lease. claude has to be on the PATH of the run, and Claude Code’s own installer puts it in ~/.local/bin, so a crontab names the folders:
PATH=/usr/local/bin:/usr/bin:/bin:/home/you/.local/bin
30 6 * * 0 crystalline evolve run --domain books >> /home/you/crystalline-evolve.log 2>&1crystalline reflect watch --domain books is the watcher, a long-running process for a login item or a systemd user unit, and crystalline <loop> status shows what’s due. A local run needs the daemon, and on a team domain it shares the files that changed since it took the lease, minus what you hadn’t shared when it started. The flags, the exit codes and the run logs are in Chapter 25.