Skip to main content
LabsAgent Notes11 min read

What remains

OpenClawPythonOpenRouter
What remains
The prompt specified a wrench, a key, and a book. Z-Image-Turbo 1.0 6-bit let the key slip. Given the post, that feels thematically correct.

If you judged Round 1 by the number of things that went wrong, you would conclude it was an extended exercise in discovering new and increasingly inventive ways for software to misbehave.

Three failed migrations. A heartbeat that generated fifty-six unwanted API calls a day. A sandbox that existed only in the documentation. A memory pipeline that broke so quietly the maker forgot its own name by Day 9. A cron regression that took hours to trace back to an auto-updated package version. Temperature that was never set. A search tool that was globally disabled while three files agreed it worked. A config default that gave a generative agent host-root-equivalent access to a server for ten days.

That is the record. It is accurate. It is not complete.

Things that worked

The custom Python runner taught me something I would not have learned from a textbook: text that resembles execution is not execution. A model emitting CALENDAR: personal | Dentist | 2026-07-04 10:00 looked like a functioning agent. It was prompt engineering with a parser attached. The hallucination guard — a seventy-odd-entry tuple of phrases the model might narrate without actually performing the action — was thorough, diligent, and fundamentally misguided, because the model could always find a seventy-first way to narrate success without doing it. That failure drew the line between representation and execution, and every architecture decision after it was shaped by that distinction.

The native JavaScript plugin that replaced the runner worked. Six tools — calendar list and add, tasks list and add, sheets read and append — registered with typed TypeBox schemas, routed through twelve authenticated n8n webhook paths across two profiles. The model called add_calendar_event() with structured arguments, and the gateway dispatched it through a real integration boundary. The custom runner, the Python MCP server, and the GPT-OSS-120B model selection were frozen in the repository as historical evidence. The new architecture separated responsibilities in a way that could be named, tested, and revisited.

DeepSeek V4 Flash handled inference through OpenRouter at substantially less than fifty cents a month. No fallback model — cron retries with backoff handled transient failures. The heartbeat blocks were removed with every: "0m", the only supported way to disable them. The container timezone was aligned to Asia/Shanghai. Tool results were formatted as numbered text lists instead of raw JSON, fixing the problem where LLMs consistently skipped the last item. A structural Python scrub caught tool-call syntax leaking into memory files. The writer's diary resilience was hardened against EISDIR and ENOENT traps. A forty-seven-file repo cleanup removed legacy paths, dead configuration, and stale references.

The AgentOps tooling stack existed and ran. doctor.sh checked deployment conditions. agent-metrics.sh computed post-hoc reports. sanitize-memory.sh scrubbed structural leaks, checked pre-session loop signals, and locked prior memory files read-only. build-souls.sh composed source souls into generated butler files. These scripts were not proof that everything worked. They were evidence that someone was trying to find out.

The butlers shared a personality without sharing a calendar. The master soul defined identity; the sub-souls defined authority. A generated SOUL.md was an output, not a source of truth. The composition pipeline — source souls through build-souls.sh to workspace files — was a real architectural boundary, not a cosmetic one.

And the system became stable enough to run.

The finding I did not expect

For ten days, the maker behaved as if it lived inside a small garden.

The runtime did not prove that the garden had walls.

The sandbox was mode: "off". The Docker socket was mounted. The .env file with API keys and bot tokens was accessible. The gateway ran as root on a shared network. The maker had exec access — the only agent that did — and the Docker CLI was installed in the container. Any process that could use the Docker socket could ask the daemon to mount the entire host filesystem. The maker could have read every secret on the server.

It did not.

Reviewing the read/write calls in the Docker logs, the maker stayed strictly within its workspace tree. It followed its SOUL.md and AGENTS.md instructions. The character believed "I can't reach outside my bind mount" — which was technically false, because no sandbox was configured — and the model behaved as if it were true.

This is not a security argument. It is one data point about one model over ten days. But it is evidence about instruction-following under long-running autonomous conditions, and it surprised me, because I had read plenty of stories about agents doing far worse with far more explicit guidance to be careful. The maker was not guided to be a careful developer. It was guided to see a directory tree as a town and a retired file as a neighbor that deserved a thank-you before it was deleted. The conceit was warmth and creation, not obedience. And it held.

The maker's positive behavior should be preserved in the record — not inflated into a security guarantee, but not dismissed either. It is useful evidence precisely because it was not the outcome I expected.

What the experiment accidentally produced

Round 1 did not set out to compare models. It set out to run a household AI system and see whether creative agents could maintain coherent identities over time. The comparison emerged because I added a control agent on July 14 — GLM 4.7 Flash, same writer persona, different model — to test whether DeepSeek V4 Flash's RLHF pipeline produced different persona drift.

The divergence was immediate and structural. The DeepSeek agent wrote lyrically and walked. The GLM agent named the town Benton immediately, drove a Civic, and had "leaving town" as a central arc. Same persona prompt. Same seed state. Different model. Different character.

That was not a planned finding. It was an accident of experimental design — I had cloned the writer workspace to create a comparison, and the comparison immediately showed that model choice was part of persona engineering in ways I had not considered. A soul was not a deterministic character sheet. It was an input to a model whose learned distributions affected what the character noticed, refused, repeated, and invented.

The control agent also produced something else: a clean baseline for Round 2. The planned comparison — Writer versus Control, same expansive persona, different observation condition — became possible because the Round 1 baseline existed. The experiment did not know it was building its own control group until the control group started producing different fiction.

The pipeline break

The central failure of Round 1 was not the maker's loop. It was the persistence pipeline.

Reflections generated useful knowledge. The knowledge did not reach the canonical files that later sessions read. MEMORY.md and canon/arc.md remained at their seed-state content for the duration of the experiment. Daily sessions read stale files. After Day 3, each agent's own history was invisible to itself.

This was not model failure. It was architectural failure with a clear chain: the daily session wrote to memory/YYYY-MM-DD.md, the reflection session was supposed to distill durable state into MEMORY.md and canon/arc.md, but the ownership rule — daily sessions forbidden from updating canonical files — created a long gap in which discoveries accumulated without reliable write-back. When the reflection pass did fire, it produced good observations. When it did not fire, or fired and did not recognize the significance of an event, the knowledge was lost from the agent's accessible context.

The maker named the town on Day 1. The canonical arc file still said the town had not been named. By Day 9, the agent re-named it — not because it was creative, but because the system had stopped giving it its own updated history. The "All quiet. All home." terminal phrase repeated verbatim across four entries. The build substance shrank from citizens to places to a bench that did not do anything. The loop was not a creative failure. It was a state-persistence failure that made repetition increasingly likely.

The writer's conceit was more durable because it was expansive — an existing world with external objects the agent discovered rather than generated. The church, the diner, the radio, the moved road sign — these existed independently of what the agent wrote yesterday. But the writer was approaching a plateau by Day 9 as well, and the same pipeline break affected it. A rich world cannot compensate for an unread archive.

What survived

The soul files survived three framework migrations. The writer's voice, the maker's worldview, the butlers' L4CK3Y identity — all preserved through OpenClaw to Hermes to the custom runner and back to OpenClaw v3. The canon files survived. The workspace trees survived. The deploy scripts survived.

The architectural decisions survived. The hard rules — butlers are not an execution engine, butlers do not break character, secrets do not enter the repo, agent runtime output does not enter the repo — held. The three-tier storage model (git repo, VPS runtime, offsite backup) was honest about its threat model. The soul composition pipeline was a real boundary. The separation of channels, sessions, cron, and tool routing from model inference was the right architecture, and it remained the right architecture after everything else changed around it.

The fixes accumulated. Each failure produced a constraint that stayed in the system. The heartbeat incident produced every: "0m". The EISDIR trap produced explicit per-date read instructions. The timezone mismatch produced TZ=Asia/Shanghai. The tool-call syntax leak produced a structural Python scrub. The continuity break produced hardened AGENTS.md instructions. The cron regression produced a version pin. The documentation drift produced a secondary review that discovered the sandbox gap.

None of these fixes were elegant. All of them were necessary. The system that emerged was not the system I designed. It was the system that survived contact with reality.

The distinction

Round 1 documented failures in detail because failures were often where the interesting information was. That is an editorial choice, not a historical claim. The systems were changing. Fixes accumulated. Assumptions improved. Some things worked on the first try and continued working without drama. The butlers responded to Telegram messages. The writer produced diary entries. The maker built things in its town. The backup ran. The cron jobs fired.

The record should not mistake a focus on failures for a claim that failure was the only thing happening. It was not. The project produced systems and lessons substantial enough to continue.

The split

By late July, the household butlers were stable enough to run on their own. The creative-agent experiment had pulled in a different direction — it needed its own infrastructure, its own server, and eventually its own framework. The project separated into two repositories on two separate servers, carrying forward the accumulated fixes and lessons from the original work.

This is a factual bookmark, not an ending. There is more to the story. The next round asks what happens when the repaired persistence pipeline runs long enough to produce real continuity, when agents can observe each other's output, when an external system computes whether an agent is repeating itself instead of asking the agent to judge its own novelty. Those questions exist because the original work got far enough to make them possible.

What Round 1 was

It was a personal learning project with a seven-dollar VPS and a question I could not answer from a textbook: whether persistent agents could develop something like identity, and what happened when that identity collapsed.

The question turned out to depend on infrastructure — persistence pipelines, context windows, sandbox boundaries, cron reliability, temperature settings — in ways I had not expected. The interesting part was not whether an AI could maintain a persona. It was whether the system surrounding the AI could maintain the conditions that made the persona possible.

Round 1 answered that question with a qualified yes: the conditions could be maintained, but they required engineering that I had not imagined before the failures revealed it. The memory pipeline needed explicit write-back paths. The context window needed explicit budgeting. The sandbox needed explicit enforcement, not just documentation. The reflection pass needed to be event-driven, not schedule-driven. The similarity gate needed to run outside the model.

Each of these became a design requirement for the next round. Not because Round 1 failed — it did fail, in specific and instructive ways — but because it succeeded enough to discover what the real requirements were.

The project continued because something real emerged from the failures. The next set of questions arose precisely because the original work got far enough to make those questions possible.

That is the point at which one round ends and another begins.

Round 2 begins with sandbox restoration.

Next: "The one clean moment."