Every morning around seven I open a page on port 4400 of a ten-year-old ThinkPad and find cards waiting. Someone drafted a post for X at 01:30. Someone checked at 00:50 whether the bottleneck had moved. Someone noticed that an article has been sitting unpublished for eleven days. None of these someones is a person. This is the machine room: a directory in my repository called Marketing-agency, the control room that runs the marketing for seitenhebel.de, emailmanager.ai, swiftrun.ai and, since last week, this site.
I have written about what one night looks like and what it costs. This post is about how the thing is built and how it got here. Everything below is measured from the repository this morning, not remembered. The short version: 94 days, 1,097 commits, and the real development was not that the machine can do more. It is that it stopped believing its own success reports.

Part I
How the machine room is built
The core is not a framework. It is a loop with one hard condition: a card closes only when the outside world confirms that something happened. Not when an agent reports it. Not when a step was ticked off in a checklist.
Here is the whole night as one picture. A scheduler starts a loop. The loop is a single claude -p call with a prompt, a list of tools it may use, and a time limit between eight and 45 minutes. It has to end in a JSON artifact that is checked against a schema; if the check fails there is one rescue attempt, then the run counts as failed. A generator turns the artifact into a card. The cockpit shows me the card. I decide, and my click does something in the outside world. And then the important part: a separate step called reconcile goes and asks the outside world whether that something is actually there.
One file holds the truth
Everything durable lives in one SQLite file with 16 tables. Not in the agents’ memory, not in a chat history, not in a folder of markdown files that looks organised until you need to query it. Tickets, ticket events, artifacts, runs, deals, proposals, and the cost of every run go into the same file, so that any question of the form “what actually happened last night” has exactly one place to be answered from.
The night in 75 minutes
Eleven loops are scheduled and run one after another. Six wait for an explicit order. Nine are switched off, and they stay in the configuration with the date and the reason, so that anyone can see they were switched off and not forgotten. Two days ago there were fourteen on the schedule. On 6 September I switched off five loops that had produced for thirty days without a single result leaving the house, and switched two of them back on once they had an exit.

Scheduled, 11
- doctrine-reviewdaily 00:30, only if expired
- strategy-impulse1st of month 00:30
- contrariandaily 00:40, only if channels changed
- diagnosisdaily 00:50, only if bottleneck moved
- analyticsSun 01:00
- seo-blogWed + Sun 01:00
- pr-cycleTue 01:10, 14-day cadence
- trend-briefingFri + Sat 01:10
- linkedinTue + Thu + Sat 01:20
- community-xdaily 01:30
- impulsedaily 01:45
On order only, 6
- video-strategy25 min
- video-draft30 min
- ads-google30 min
- ads-meta30 min
- ads-linkedin30 min
- ads-control25 min
Switched off, 9
- outreach17 Jul
- sales-followupoff
- sales-inbox6 Sep
- master-contentoff
- cro-hypothesesoff
- linkedin-dmoff
- community-reddit6 Sep
- community-fboff
- output-grader6 Sep
What a card can go through
A card has ten states, and the transitions between them are a closed list in code, not a convention. A transition that is not in the list throws an error instead of quietly happening. That sounds pedantic until the first time an agent tries to move a card from “dismissed” back to “queued” because its prompt told it to be helpful.
slugify(title). Rephrase a title and you create a new card and leave the old one immortal.Two machines, one gate
The same code base runs on two machines, and only one of them is allowed to start runs, spend money and act on the outside world. The workshop is a Mac Studio with git, where the dispatcher generates and ages cards but starts nothing. Production is a ThinkPad X260 under my desk, without git, a pure deploy copy, where the dispatch flag is set and the database is the truth. Deploying is an rsync, not a git pull: data arrives immediately, behaviour changes about four minutes later, after the restart.
Across both machines runs one gate. Agents deliver drafts. They send nothing, publish nothing, submit no form, open no account, spend no money and change no ticket state. Anything beyond that needs a paragraph in the rules file, committed before the run, that names the command, the files and the side effects. A “yes” in a chat window does nothing, and I learned that the expensive way: a rule only works where the executor is looking.

Part II
How it got here
The project is older than its first commit. It starts on 4 June with documents and writes its first line of code on 14 July. The 1,097 commits since then have two peaks and one conspicuous dip, and the commit titles tell a story that nobody plans: first features were built, then guards, and then it turned out that a good number of the guards were dead.
Six weeks in which not a single line ran
On 4 June four strategy documents for seitenhebel appear: an executive summary, customer segments, a funnel psychology audit and a money-back guarantee. The first one notes about itself that it was synthesised “during the gym session” from ten parallel deep analyses. Two days later a competitor pricing analysis joins them.
The directory itself is created on 20 June at 15:56. The next day, in a single day, eleven documents appear: architecture, strategy loop, operating model, premortem, quality and moat, tool options, go-to-market motions, partnerships, market intelligence, an interface map to my video tool and an architecture diagram. Two more follow on 22 June.
Together that is 18,773 words in 18 documents, written before anything ran. That they only show up in git on 14 July is a property of how they were imported, not of when they were written: they came in as a batch through one pull request.
The impulses come from the agents
Two commits on one day set everything up: a strategy foundation of documents and contexts, and ten agent loops with one generic runner. The guiding principle stands in the header comment of the runner and has survived 54 days: every loop ends in an artifact and a notification with one concrete action. Sending, money, captchas and logins are always the human’s.
From a sticky note to a backbone
On the second day the system gets a face: the cockpit with a “morning edition”. Two days later comes the decision that carries everything after it: tickets instead of a queue that is gone after a restart. The same day the deal CRM is born, and the outreach loop is shut down before it ever ran. Cold outreach is legally fraught in German-speaking markets, and the strategy does not carry it.
“Done” means live. Not: a file is lying around
The most productive phase of the project, and the one in which the central insight lands. The cockpit gets four exits instead of one dead end. The proposal valve appears. The rank of a proposal is computed instead of guessed. And then the measurement that the whole reconcile idea rests on: at that point the chain had never closed a single card.
On 27 July the project also gets its own rules file, whose stated job is to “tell the agent where it stands”. That sentence is literal: workshop or production, and what it is allowed to do in that place.
The rules do not reach the executor
The phase in which the project stops building features and starts testing its own rules. The finding is uncomfortable and repeats in several shapes: a lock that was not one. Vocabularies that were not unused but bypassed. Three exception lists, each justified on its own and together invisible.
The guard caught me first
The densest day in the project’s history. The handover stops being a shared file and becomes a journal of individual threads, because two parallel sessions had overwritten each other. The rules file stops being its own logbook: 56 closed openings move to an archive. The Buffer button appears, through which a LinkedIn draft finds its way out. And the re-measurement on the real machine finds two errors nobody had seen before.
Right after that come the test bench, “the automated verification the loops were missing”, and a critic with its own context. And the card learns a distinction it had blurred before:
The dead
After the quiet week comes an audit over roughly 21,000 lines of production code, and what it finds is the punchline of the whole chronicle: not broken features, but guardrails that had done nothing for weeks. An escaping routine that had been ineffective for 17 days. Two guards that shot the messenger instead of reporting an error. A loop that stood for three weeks in a directory without its dependencies and therefore structurally could not produce anything. Ten symlinks pointing to macOS paths that led nowhere on the Linux production machine, unnoticed since 24 March, because the test only checked them on the Mac.
The numbers turned on the tool
This morning I ran the query I had been avoiding. Thirty days of production, everything a human did at the cockpit, next to everything that was done to the tool in the same period.

It was never a usage problem, and that is what made it serious. I kept clicking. I cleared cards, I sent cards back, I left comments: 259 of them in thirty days. What had stopped was acceptance. Five cards went to done on 22 August, and then, for fifteen days, not one. In those same fifteen days, 273 commits went into the tool. As long as that ratio stands, every further guard improves a machine nobody takes work from.
So the machine room got a build freeze this morning. Until 20 September nothing gets built that is not one of three moves: switch off loops that have no exit, build exits that need no click, and let stale cards expire. Every commit in that window has to name which of the three it serves.
Then I ran the query again this afternoon, for this post. The number had moved. Between 08:07 and 11:27 today, thirteen cards went to done by hand, the first acceptance in fifteen days, and the commit count into the tool for the same day is zero. One morning is not a trend and I am not going to pretend it is. But it is the first data point that points the right way, and it arrived on the day the building stopped.
On 20 September two numbers get read: cards a human took to done since 6 September, and articles published across my sites, today 413 and last increased on 17 August. If both rise, the exit was the problem and the pause worked. If nothing rises, it was not the building either, and the next question is a different one from “which guard next”.
What the numbers say about the machine room
In the directory where the machine lives, test code is slightly longer than production code, a ratio of 1.01 to 1. That is not a style decision. It is the result of the chronicle above. In a system whose agents run unsupervised at night and whose reports nobody reads live, a claim without a measurement is worthless. That is why a card does not close on say-so, why a permission needs a committed paragraph, and why behind almost every rule in the rules file stands a date and a case.
What 94 days actually bought
Counted from the first document that is 94 days, from the first commit 54. The real development of this project is not that it can do more. It is that it stopped believing its own success reports. And the honest end of this post is that a machine that distrusts itself is still a machine nobody has accepted work from in fifteen days. The next fourteen days are about that, and the measurement is already on the calendar.
All numbers were measured from the repository on 6 September 2026, not estimated: commit counts from git log on the directory, loops and schedules from the loop configuration, tables from the database module, states from the transition table in the ticket module, proof types from their config file, lines with wc -l without node_modules. The prehistory comes from the creation lines of the documents themselves and the directory’s birth time. Commit titles are translated from German; the hashes are real and the originals are one git show away. The ledger of human state changes was measured on the production database on 6 September.
