My markdown folder was already a multi-agent system
A record of building and running my own personal AI assistant runtime. Part 2.
I run five to six AI coding-agent sessions at the same time. Each session takes on a different project, touches different files in the same repository, and makes its own commits. There was even a two-day stretch that produced 41 commits.
Running this way, I kept seeing the same family of accidents.
- I wrote the same phrase in two files, and the two values drifted apart.
- A file meant as a mirror came to hold newer information than the canonical file.
- A stale file-edit anchor sent an edit to the wrong line, twice.
- An entire daily record file was overwritten, erasing another session's records, twice.
I treated each as a separate accident and prescribed a separate fix. Re-read before writing for anchors; append only for records; never write mirrors by hand. Yet they kept happening.
Diagnosis: this is not a file-management problem
While looking for the cause, I read Cognition's “Don't Build Multi-Agents.” It offers two principles.
- Share context. Share the full trace, not fragments of messages.
- Actions entail implicit decisions, and conflicting decisions produce bad outcomes.
The second sentence describes my accident list exactly. All four types of accident happened because one session acted without knowing what another session had decided.
That changed my perspective. I had thought of myself as “a person who uses AI assistants,” but what I was actually operating was a multi-agent system. The parallel sessions were the agents, and the canonical markdown files were their only communication channel.
The sessions do not speak to one another. They read and write the same files. That is the entire system.
From this view, it also makes sense that accidents grow combinatorially rather than linearly. With N sessions, the number of potential pairs of conflicting decisions grows with N squared.
Then I learned what the 26KB really was
A canonical file containing my progress state was 100KB. Breaking it down produced this.
Total 100KB ≈ 25k tokens
Update header block 26KB (26%) ← change history, not state
Closed rows 16KB (30%) ← dropped, completed, results final
Longest row 3.5KB
Median row 535B
At the top of the file sat a 26KB update-history block. At first I saw it as garbage. Why would a state file contain history? Removing it would cut 26%.
After reading Cognition's first principle, I looked at it again. That 26KB was an attempt at precisely “full trace sharing.” It gave sessions a way to leave one another “what I did and why.” Without it, the next session sees state without context.
So it was not garbage. It was a necessary function placed in the wrong location.
The problem was that one file was doing two opposing jobs.
| Function | Desired property |
|---|---|
| State sharing | Small and current, because it is read in full every time |
| Trace sharing | Detailed and append-only, because decision context must not be lost |
Put both in one file and the state gets buried under history. That was the source of the 100KB. The reason to separate them is not token savings; it is this.
Prescription: I made rows into a contract
When state is stored as prose, a machine cannot read it. Measurement showed that among 94 live rows, it could parse an announcement date in 40% and the next action in 22%.
That was the direct cause of the accidents. Twelve overdue announcement items accumulated for months, and next actions were underreported, because I stored state as prose rather than data.
So I created a row contract.
<Status> · Deadline MM/DD HH:MM · Announcement YYYY-MM-DD · Next: <Action>
Free prose follows it. For an unknown value, I state it explicitly, such as Announcement not announced. Blank fields are prohibited because monitoring cannot catch them.
I do not migrate in a big bang. Whenever I touch a row, I upgrade only that row to the contract form before I leave it. A plan to fix all 94 rows at once will not be followed. Fix only the rows I touch, and the system converges naturally.
I measure compliance with a script. Without a number, I cannot tell whether it is converging.
What I did not adopt
This may be the more important part.
Returning to a single thread. This is Cognition's actual recommendation: do not build parallel agents; put a history-compression model on a single thread. I did not take it. Parallel operation is a requirement for me, not a mistake. Several projects are genuinely moving toward deadlines at the same time. Instead, I reduced the surface area for conflict with thin rows and separated files.
Parallel isolation with git worktree. This is the standard solution for parallel coding agents. I still use it in code repositories. I did not use it in this vault. The scheduler, hooks, and runtime hold fixed paths, so using worktrees without untangling path dependencies would silently kill the automation.
Adopting an external-memory framework. Tools such as Letta or mem0. I did not take them. The current bottleneck is document structure, not storage technology. I will not add runtime dependencies for a problem that file separation can solve. I took only one idea from Letta: put a character limit in the schema for an always-loaded block.
I think this order matters when deciding whether to adopt technology. First verify whether the problem yields to structure. I use a framework for problems that remain after the structure is fixed.
The same accident replayed in real time that day
As I was writing this diagnosis and about to commit it, another session committed first. That commit swept up the 492-line deletion I had staged.
The outcome was the same. Those 492 lines were scheduled for deletion and were deleted. But the attribution in the commit message is wrong. Later, when I trace why the file disappeared, I will inspect the wrong commit.
An item from a diagnostic document I wrote that day replayed in real time that same day. It was a little funny, and it was also proof that the problem remains unfixed.
Remaining holes
Append-only for record files is still a document rule. The accident has already happened twice, and there is no machine enforcement.
The next candidate is a hook: reject writes that reduce existing content. It would stop an attempt to overwrite an entire file at the tool-call stage.
I now know that moving from document rules to code enforcement is most of operating this system. The work is not writing better rules. It is making accidents impossible even when rules are not followed.
The question that stays
Something kept catching in my mind as I wrote this. I work alone. I do not have a team. Yet my list of problems exactly matches the problems of a team with several people touching the same codebase: where the canonical file is, who decided what, why a mirror drifted.
The moment I run agents in parallel, I am operating a distributed system even by myself. The problems of distributed systems do not disappear by making the tools better. They disappear only when I design the communication channel.
This is the question I need to ask: What exact path lets two actors in my system learn each other's decisions? If I cannot name that path, the system is not designed yet.