I borrowed the rules of an AI that tidies memory. The worst drift was outside the notes

The post on September 19 ended with this: the map of my own environment had drifted in four places over three months, and the next thing I’d work on was a way to catch drift sooner. I built the catching part that same day, the 19th. I’ll get to it in the second half. First, the rules my upkeep runs on when it fixes drift, and what they found. I borrowed those rules in July, from one of three AI memory projects I looked at.

In the post where I added a big-picture layer to my external brain, I also evaluated open source projects, stole the ideas, and threw away the implementations. That one was about what to remember. This one is about what happens to memory after it’s written. Running the borrowed rules three times showed me that memory rots in three places, and the worst rot happened outside the notes.

I looked at three projects and came away with a rulebook

The three were rowboat, agenticow, and a repository where someone published how they set up memory for Claude Code. I had three subagents read them in parallel.

agenticow is infrastructure for branching vector memory like git: try a change, and merge it into the main line if it’s good. Branches, diffs, and history are things my vault already has for free, since it’s Markdown in git. The memory-setup repository was a step behind what my environment already does. Neither had a feature worth taking home. One sentence in the agenticow README did end up backing a decision later.

rowboat is a product that builds notes automatically from your email and meetings. The ingestion side is exactly the thing I refused in my first external brain post, letting the AI decide what to remember, so I skipped it. What I read was a single file on the side that tidies the notes afterward, note_curation.ts. The header comment opens like this: the note-creation side only ever appends, so quality decays as volume grows. Activity logs bloat, stale open items linger, contradictions pile up. It goes on to say that every serious agent memory system converges on a background consolidation pass, and lists Letta (formerly MemGPT) sleep-time compute, Stanford’s generative agents reflection, and Zep. It calls its own agent “the gardener.”

The body is a set of instructions for a curator that rewrites one note at a time, with a list of rules to follow:

  • No new facts. Everything in the output has to be derivable from the input note
  • No deleted substance. Compress, but keep decisions, commitments, and links inside the summaries
  • Downgrade unsupported inferences to dated observations
  • Rewrite past-due future tense into past tense. Don’t claim it happened unless the note shows it did
  • Move open items with no movement for 45 days into a dormant section instead of deleting them
  • Resolve contradictions newest-wins, keeping the old value as history
  • Stamp the frontmatter with a timestamp after curating

This runs automatically every day over the notes that need it.

What I removed was “run it silently every day”

I moved the rules almost as they were into obsidian-protocol, the skill that holds my conventions for writing to the vault. The only thing I changed was how it runs. Upkeep runs only when I tell it to. It rewrites one note per pass; Claude shows me a diff, I look at it and approve, and then it writes. Daily work logs are primary records, so they’re out of scope. Claude still does the tidying. It just doesn’t get to write silently.

If I don’t let the AI decide what to remember, letting it silently rewrite what I remembered amounts to the same thing. That’s why I took out the automation. The one sentence in the agenticow README backs this up. It cites their own experiment: having an LLM pick one answer out of a cheap model’s candidates did worse than a plain majority vote over the same candidates, even with a verifier gating it. From that they conclude that the decision to merge a memory change into the main line should be limited to things that can’t hallucinate, like tests, regexes, or a human check.

My conventions already had confidence marks. Unverified claims get (speculation). Things that can’t be confirmed and are waiting on a human get (needs confirmation). A (speculation) mark comes off only with deterministic evidence, such as code, logs, or execution results, or with my confirmation. An LLM rereading it and finding it plausible isn’t enough. Having an LLM judge the curator’s output would run straight into that rule.

Three runs, zero rejections

The first run was July 15, the day I moved the rules over. It folded seven completed TODO items into one line that says to see the daily logs, and in the inbox where an AI drops weekly post ideas, it marked one idea that had become a post as published. Two notes. Small.

The second and third ran back to back on September 18. The second targeted the TODO list, and for the first time an item untouched for 78 days moved into the dormant section. The third targeted the map of my environment, the same page that, as I wrote in “A product version of my hand-built external brain came out. I compared them and found nothing to add”, had drifted in four places over three months.

All three times, I looked at the diff and approved all of it as is. Not one change came up that I didn’t want. The allowed changes are narrowed to compressing, moving, turning old values into history, and cleaning up formatting. Adding or removing facts isn’t possible. Because the rules cut the range down, the diff can only come out in a shape I’d approve.

The drift was in three places

Sorting the four spots on the map by where you’d have to look to find them:

A sentence listing my public repositories disagreed with a table in the same note. You can see that from inside the note alone. A link to a note that doesn’t exist, you can catch by checking whether that file is in the vault. The change in how one skill is version-controlled, and the four recent repositories missing from the repo list, had no clue inside the note at all. The vault’s work logs from other days recorded the change and the new repositories, and only by cross-checking against those did they show up.

In my conventions, “no new facts” works as “don’t add facts that aren’t written anywhere in the vault,” and copying from another note’s records is allowed. rowboat says derivable from the input note. Same rule, but where you draw the edge of “input” changes how much drift you can pick up.

After approving the fixes for those four spots, I had Claude check on the real machine the items it had marked (needs confirmation), such as the public or private status of a repository I’d just made. While it was at it, I had it verify every symlink mentioned in the note.

One wasn’t a symlink. It was the instruction file for an unattended task that writes a news digest every morning and evening. The note said it was a symlink to the repository; it was actually a copy. Starting September 3, the Claude desktop app stopped opening instruction files that are symlinks, and on the 11th I had switched to deploying real copies. It’s the switch to copies I mentioned in one line last time. The switch itself was written down in another note in the vault, so this was still drift of the second kind, the kind cross-checking should catch. The third run just hadn’t looked at that note. The part with more to it was what was inside the copy.

Comparing the copy with the repository version, it was one line short. The missing line was the rule I added in July: don’t pass untrusted strings to WebFetch. A task that reads the web and email was running without exactly that rule. The reflog showed that the copy had been made on a work branch cut from a main that predated the rule. Fifteen minutes later I pulled main, but never redeployed. I approved, had Claude rerun the deploy script, confirmed both tasks’ copies matched the repository, and closed it out.

That was the third place. That the copy is one line shorter than the original isn’t written anywhere in the vault. You won’t find it by rereading the note any number of times, or by cross-checking it against the entire vault.

A diagram sorting the drift found during upkeep into three columns. Left, inside the note: a sentence listing public repositories disagreed with a table in the same note. Middle, between notes: a changed version-control path, four repositories missing from a list, a link to a nonexistent note, and a switch from symlink to copy. Right, notes versus reality: a deployed copy was missing one safety rule. The bottom row shows reach: rowboat's curator covers only the left column, my conventions cover left and middle. The right column isn't covered by any rule; a human saw the needs-confirmation marks and had the machine checked

Nothing in the rules says to go look outside the notes

rowboat’s curator gets four tools: read file, write file, grep, and list. It has no way to run a command and check the real machine. I left it out of the list above, but the rules do have a “(needs clarification).” It’s for when entries from the same point in time contradict each other and you can’t pick one. What that design produces, run daily, is a page with no contradictions inside it.

My conventions don’t say to go check reality either. It happened this time because the things that couldn’t be decided stayed as (needs confirmation) instead of getting filled in with guesses, they landed in front of me, and I said go check. The one who actually went and looked was Claude, which has a shell. All I did was tell it to go. The approval step was a pass-through all three times. What led to the find wasn’t the approve-or-reject decision so much as the diff and the (needs confirmation) marks being put in front of me.

A curator running silently every day could still leave (needs confirmation) marks. But if nobody looks at them, the copy with a missing line keeps running, and the note stays looking contradiction-free. In fact, right after I approved the third run, the environment map was a clean, consistent table, and it still said symlink.

I automated only the catching

Upkeep that only runs when told will sit untouched for three months if you forget to tell it. Everything found on September 18 turned up only after I said “run it.”

The next day, the 19th, I automated just the catching. A hook that runs when Claude Code starts counts dead links in the vault, TODOs that haven’t moved in a long time, (needs confirmation) and (speculation) marks older than 30 days, and notes nobody has tended in 90 days, then prints the counts on one line. It’s 150 lines of standard-library Python, and a scan takes 0.06 seconds. Its main job is to put neglected (needs confirmation) marks in front of me every time I start a session. Fixing works the same as before: I ask, look at the diff, and approve.

Four days later, today, startup said four dead links. The same four as the first run on the 19th. Showing them doesn’t fix them. The most the hook can do is give me the nudge to say “run it.” Of the three warnings about marks past 30 days, two were lines in the decision memo I wrote in July when I brought rowboat’s rules over, lines that explain how the marks are used. The hook fails to skip lines that describe only one of the two marks, and that’s a fix on the detection side.

Drift in what a note says can be fixed a few days late and still be fine. The missing safety rule wasn’t drift in a note. It was on the machine. Whether a deployed file matches its original doesn’t need reading, just a comparison, so on the 21st I added three lines to the same startup hook to compare them. That one doesn’t count anything; it warns the moment they differ.

The tidier the notes, the more someone has to order a reality check

Of the rules I borrowed, the ones that proved their worth over three runs were the prohibitions: don’t add, don’t delete, turn inferences back into observations. It was thanks to those prohibitions that the things that couldn’t be decided stayed as (needs confirmation).

If you’re going to let an AI tidy your memory, there are two things to check before how well it tidies. What it forbids. And, for what can’t be decided inside the notes, whose eyes it lands in and who sends someone out to check reality. The tidier the notes, the easier they are to believe. In my setup, the job of sending someone to check behind that believability is still held by a human.

comments powered by Disqus