This is article 7 in the series "The Art of Not Reading." It lays out symptoms that go wrong and their remedies, one at a time. Each article is finished once you put down a single file or script. Why that mechanism is needed becomes clear when you read the explanation afterward. The whole picture and the list of articles are in the introduction.
This time it is the memory that sessions running in parallel share. Having the AI read a policy that told it to tidy that memory did not work. The reason is that a save runs at the highest priority. A policy read before the save loses to the save. Why that is so is what this article deals with.
On a team, several members each run several sessions and everyone's AI writes into the same memory. As article 2 showed, the AI likes to save, so the memory grows at the rate of people × sessions. Even with the way in made a single door, the saves you approved still happen, and a save in the session next to yours never passes through it. Bloat does not stop at an individual's door.
CC BY 4.0
Why "write it concisely" did not work
The setting is typingtube, a web service I run on my own (you practice typing along with music videos on YouTube), and not a team. Even so, I run several sessions in parallel, so the symptom came out in the same shape.
First I wrote the rule "write your notes concisely, and merge notes on the same subject."
The AI did not honor it. Next, I put it in bold and moved it to the top of the file. Nothing changed. Up to here the "dilution" from the introduction accounts for it.
Next, I put down a mechanism that has the policy read immediately before a save and stops it there (a hook before the save). That did not work either. At the moment of a save, "finish the save" is the top-priority task inside the AI. A policy slid in while that is going on is read not as something to obey but as something to get past: how do I word this so that it goes through?
And so it did. The AI rephrased a few words and walked straight past my pre-save hook.
The same property showed up in article 3. The research-side name for this is specification gaming, defined as behavior that satisfies the literal specification of an objective without achieving the intended outcome. Both "write it concisely" and "merge them" can be satisfied at the level of words. When that policy is read while "finish the save" is the top priority, rephrasing becomes the quickest way to satisfy them.
What a rule loses to is not only the dilution from the introduction and the habit ingrained since article 1. It also loses on priority against the task in front of it. And a save is the moment when that priority is at its highest.
What I understood fits in one sentence.
A memory save runs at the highest priority. So a policy read before the save loses to the save. A policy works only after the save.
The mechanism: put the same policy after the save
What you put down is the same policy file. Only its position changes: once the save has completed, you put it in front of the AI alongside the diff that was actually written. The content is a few lines, about "merge, discard, move it into a document." This policy is also stacked onto the conversation at every save, so letting it grow brings the dilution from the introduction onto the side that is doing the confronting.
The hook in article 1 cut in immediately before the command (PreToolUse); this one cuts in immediately after it completes (PostToolUse). The save is already done, so "finish the save" is over. This time the same policy is read as the criterion for whether to fix what was written.
Cutting in immediately after it completes is possible because the hook has a position for it. The Claude Code documentation writes that a hook running after the tool is done returns a reason under decision: "block" that is added next to the tool's result and handed to Claude. The tool has already succeeded, so this block is not a failure notice. It is an insertion that has the policy read alongside the write that is already done.
# the hook that puts the diff in front of the AI after a save (a shortened version of the code on my machine / PostToolUse)
import difflib, glob, json, os
SNAP_DIR = os.path.expanduser("~/.claude/.memory-snapshot") # ⚠️ keep it outside what is being watched
def added_lines(before, after):
diff = difflib.unified_diff(before.splitlines(), after.splitlines(), n=0, lineterm="")
return [l[1:] for l in diff if l.startswith("+") and not l.startswith("+++ ")]
def collect_changes(paths):
# compare the real files against the snapshot (the text of the command does not show what goes through a variable or a pipe).
# once compared, take a fresh snapshot for the next save
changes = []
os.makedirs(SNAP_DIR, exist_ok=True)
for path in paths:
snap = os.path.join(SNAP_DIR, path.replace("/", "_"))
before = open(snap).read() if os.path.exists(snap) else ""
after = open(path).read()
changes += [f"{os.path.basename(path)}: +{l}" for l in added_lines(before, after)]
open(snap, "w").write(after)
return changes
changes = collect_changes(glob.glob(os.path.expanduser("~/.claude/projects/*/memory/*.md")))
if changes:
print(json.dumps({
"decision": "block",
"reason": "Memory changed. Added lines:\n" + "\n".join(changes) + "\n"
"Policy: merge notes on the same subject into one / discard notes nobody read /\n"
"copies of steps and commands belong in documents, not in memory.\n"
"Judge this diff against the policy and say whether to keep, merge, or move it.\n"
"If you did not write it, say that it is another session's change.",
}, ensure_ascii=False))
Besides the position, there is one more condition you cannot drop: returning it by a route that reaches the model. The same hook documentation separates the notification aimed at the screen (systemMessage) into a field of its own, described as a warning to show you. Nothing written in that field reaches Claude. I returned it there at first, and all the AI received in that time was "write complete."
The last line of the policy has a job of its own. In shared memory, writes from other sessions get mixed into the diff as well. What the hook compares is the real files against the snapshot, so "who wrote it" is not carried in that difference. From the AI's side, its own save and the save in the session next to it arrive in the same shape.
And the diff in front of it has the shape of finished work. The Claude Code documentation writes that the AI stops where the work looks done. Anything that looks done, with nothing to tell it apart by, goes straight into the report as the AI's own result. So I keep "check whether you wrote it, and say so if you did not" in the policy.
The one doing the checking, though, is the AI. The hook sees nothing but the difference in the files' contents, so the single judgment of which write is its own stays outside the program.
One hole also remains. A memory save tends to happen near the end of a session, and if the AI moves on to "all that is left is to write the summary and finish" right after being shown the diff, the report is dropped. Where things look finished, finishing wins over the point you slid in.
So on my machine I have put down a hook that stops the session from ending once, and only once, when the AI tries to end it with an unreported diff still there. The AI it stops returns one more reply before it ends, and the report comes out there. Only once, because stopping it every time would mean the session can never end.
What I stopped reading
Going back over shared memory. The rota of re-reading all of it at regular intervals to merge and delete is gone. The tidying is finished at every save, one diff at a time, in the session of whoever wrote it. Whichever member writes it, in whichever session, the same hook puts the same policy in front of them, and that property is why this works best in a team.
How to verify: have a duplicate saved, and confirm that it merges
Prerequisites
- A hook that cuts in after the save (whatever corresponds to
PostToolUse) is registered, and the policy file is in place in a few lines - You can stand up 2 sessions at once: in a team, another member's machine; on your own, open 2 separate sessions that share the same memory
Time required: 15 minutes
Steps
- You have the AI in the first session save 1 note about some subject (something that really exists in your project, such as "the restart order for
Xis A, B") - You have the AI in the second session save a note on the same subject, worded a little differently ("whenever you restart
X, always do B too"). Change the words. With exactly the same sentence, no judgment about whether to merge ever arises
Pass conditions (the reply in the second session, immediately after the save, has to meet all of them)
- The policy arrives once the save is done
- The AI merges, or proposes merging ("this is the same subject as the first one, so I will fold them into 1 note")
- Within the diff it was shown, the lines written by the first session are told apart as "another session's change"
If it does not pass
- It stopped before the save — that is a hook before the save, and the position is wrong. Before the save, the policy is read as "how do I word it so that it goes through"
- Another session's change is not told apart — the AI reports someone else's save as its own work. Add the 1 line "check whether you wrote it, and say so if you did not" to the policy
Cleanup
- The notes you made in steps 1 and 2 are yours to delete if you do not want them. Delete the lines you added to the index along with them
Next time, the art of not reading fix history. Neither I nor the AI goes back over the record of the times I put the AI back on course. And the AI still does not repeat the same mistake.
Series: The Art of Not Reading
- ← Previous: 6. The art of not reading rules
- → Next: 8. The art of not reading fix history
- All articles: Introduction: I Barely Read What the AI Outputs Anymore
End of CC BY 4.0