This is the third post about the same file.
First there was a flat context.md as my AI assistant’s working memory. When that stopped scaling, I replaced it with a causal graph in DOT format — nodes for inflection points, edges for why things happened, maintained entirely by the AI. When the graph grew without bound, I added a janitor agent with line budgets to archive old content automatically.
Each fix bought a few months. Last week I audited the system honestly, and then I deleted the whole thing.
The Audit
The numbers first. The context graph sat at 1,248 lines against an 800-line budget. Of its nodes, 115 were marked status="active" — after a quarter in which most of that work had long since resolved. The session log, designed as a rolling four-week window, spanned four months. The daily-notes folder had been dead since April. The janitor agent existed and ran; it just couldn’t keep up with the write rate.
The interesting part is what didn’t rot. Everything written by automation on a schedule — the reminder queue, a daily feed generator, a behavioral-patterns graph updated by a session-end hook — was current to the day. Everything that depended on me feeding it, or on the AI updating a status field it had set weeks earlier, was stale.
That split is the diagnosis. The machinery wasn’t broken. The premise was.
Shadow Context
The graph was a mirror. Project status, decisions, open threads — all of it mirrored state whose sources of truth live elsewhere: the issue tracker, the calendar, meeting notes, my head. A mirror of live state is a cache, and this cache had no invalidation mechanism. When reality moved and nobody told the graph, the graph kept asserting the old reality with full confidence.
Worse: I was the only invalidation mechanism, and I never looked at the file. That was by design — the graph was for the AI, machine-maintained, machine-read. Which means staleness was invisible by construction. Nothing created pressure to correct it, so nothing did. The status fields were the most honest part of the failure: a status="active" attribute doesn’t decay on its own. Someone has to notice it’s wrong, and the only someone was an agent with no way to know.
I’d been treating this as a gardening problem — write better pruning rules, run the janitor more often. It’s a structural problem. Context that only the agent reads is shadow context, and shadow context drifts no matter how clever the maintenance gets.
Move the Memory Where the Human Lives
The replacement inverts the visibility. I’ve been building Taproot, a Roam-style outliner — pages of nested bullets, wiki-links, daily journal pages — and I live in it anyway. Now the assistant does too.
The convention is journal-first. At the end of a session, the agent appends a handful of dated, single-line bullets to today’s journal page, under its own block: decisions with a one-line why, status changes, causal links written as plain prose. Each bullet wiki-links the topics it touches. The outliner’s backlinks do the rest — open a project’s page and every bullet that ever mentioned it is right there, grouped by date, newest first. A project timeline nobody maintains.
Notice what’s missing: there is no status field. Nothing says “active.” A thread that’s moving has recent bullets; a thread that’s done has a resolution bullet on top; a thread nobody has mentioned in three weeks announces its own staleness by silence. Recency is the status, and it can’t be wrong because it isn’t stored — it’s derived from the log.
And because the memory lives in a tool I use every day, staleness that does creep in gets seen. Not by a hygiene cron. By me, at breakfast, mildly annoyed. That correction pressure is the invalidation mechanism the graph never had.
The Rules That Keep It Honest
A shared surface only works if the agent behaves like a guest. Four rules carry most of the weight:
- Write only to daily pages, under one clearly-owned block per day. Topic pages are never written directly — they’re assembled by backlinks.
- Never create pages. Wiki-links auto-create pages in most outliners, so an agent that tags freely mints garbage pages. Mine may only link titles that already exist; I decide what deserves to be a page by creating it.
- Append, don’t edit. History is superseded by newer bullets, never rewritten.
- Human edits win. If I delete or move a block, the agent doesn’t restore it. Absence is information.
There was a tempting fifth feature: a pinned “Now” page — one line per active workstream, maintained by the agent as an index of the frontier. I cut it. An agent-maintained summary of what’s active is exactly the shadow copy this whole migration exists to kill, just smaller. If I want a frontier page, I’ll curate it myself; the agent can read it but never write it.
What I Gave Up
The graph’s typed edges — led_to, blocked_by, spawned — are gone. Causality now lives in prose (“X broke Y, which led to Z”), which is less queryable and honestly fine; the edges were always ceremony around the sentence in the node. The verbose machine-written detail blobs are gone too, and that one is a feature: an agent writing into a human’s tool has to write at human size.
The general rule I’m taking away: an agent’s memory needs either automation on the write path or a human on the read path. Both is great. Neither is a shadow copy with a countdown timer. I spent five months building increasingly sophisticated machinery on the wrong side of that line. Moving the memory to where somebody actually looks took an afternoon.