A vast neon-lit archive wall of glyph-marked index drawers with a single red light line running from one open drawer to the tool of a robot arm, keeping its work on track while faint drifting paths fade beside it

My agents don't get dumber. Their context rots.

13 min read Updated October 17, 2026

Until May I started every new chat the same way: re-explaining the same things to the agent. Which project, what was already decided, what not to touch, how I like the work done. Every chat, from zero.

Now I type "recall project X, we're going to do Y in it" and about 30 seconds later the agent has the full context for the job. That is a memory system I designed and have run since May 2026, plus a set of rules that keep agents from quietly undoing what we decided.

I don't write the code for Dream Book, my dream-interpretation app and site. I run the system that writes it, with Claude Code and a fleet of agents. Most failures in that system look like model failures: a wrong assumption, a confident "done" on work that wasn't. The model is rarely what breaks. The context is.

It rots in two directions. The blank start: every session begins with an agent that knows nothing about what was locked, what is on review, or that "ok" from me is not a yes. Whatever it doesn't know, it fills with the most plausible guess. The slow drift: in a long session the agent starts with the right picture and loses it one reasonable step at a time. A criterion gets reinterpreted, a scope line gets softer. Nobody moved the goalposts. They moved anyway.

So the fix has two halves. Memory, so the agent knows what is true when it wakes up. Drift defenses, so it can't quietly move what was decided once it's awake. Below are the twelve plays I actually run, in order. Each one says what to do, which failure it prevents, how I do it, and a template you can copy.

Part one: memory, so the agent knows what's true

Layered diagram of the agent memory system: a recall request reads a thin index of 157 one-line pointers, which points into 228 single-fact memory files split by type (119 project, 59 feedback, 49 reference, 1 user), with a dormant sub-index and an archive offloaded to the side and the repo with CLAUDE.md, specs and the invariants file as the foundation.
The part that loads every session is tiny on purpose. The index is 157 pointers; the 228 facts are read only when the task touches them.

Play 1: One fact per file, typed and dated

What. Every memory is its own small markdown file with a short frontmatter: a name, a one-line description, a type.

Why. One big notes file gets loaded whole and skimmed. Small files get read only when the task touches them, and replaced cleanly when the fact changes.

How I do it. About 228 single-fact files. Types are blunt: 119 project (where a thing stands), 49 reference (where a thing lives), 59 feedback (how I want the work done), 1 user. Every date is absolute. Nothing says "yesterday"; it says "found 2026-10-13" or "2026-10-14: fix DONE". 671 wikilinks across 211 files turn the folder into a graph, so the memory about Dream Book's resume-trap bug links to its release, paywall and onboarding memories.

---
name: <short title>
description: <one line: what this is and its status>
type: project | feedback | reference | user
modified: <YYYY-MM-DD>
---
<the fact, with absolute dates>
Related: [[other-memory]]

Play 2: Keep the index to one-line pointers

What. One index file that loads every session, one line per memory, and each line points to a file.

Why. The index is paid for in every session. Put content in it and it bloats until the agent skims it. Kept as a table of contents, it lets the agent open only what the task needs. This is the play behind the 30 seconds.

How I do it. 157 lines. Status words like LOCKED, SHIPPED, PAUSED and ON REVIEW sit right in the line, so the agent sees the state before opening the file. Dormant clusters move out: on 2026-09-21 I moved 51 entries on a dormant topic into their own sub-index and left one line behind, "cluster dormant; return to main index if resumed". Old snapshots go to an archive memory with links back. For a large project, a thin anchor in the global index points to a project index, which points to per-repo files.

- [Title](file.md): one-line hook with STATUS and date
- [Dormant cluster](MEMORY_topic.md): N entries; dormant, pull back if resumed

Play 3: Turn every correction into a memory with a Why

What. When you correct an agent, the correction becomes a feedback file with two blocks: Why and How to apply.

Why. Without it you repeat yourself in every new chat. Without the Why, the agent follows the rule literally or not at all, and can't stretch it to a case you never described.

How I do it. These are the 59 feedback memories, about a quarter of the folder. 43 of them carry a Why, and 37 have both blocks. "A one-word reply to my proposal is not consent, re-ask in one sentence" is one. Another quotes me with a date: "Always run multi-agent flow in a self-check loop, and act as orchestrator yourself" (2026-08-13). I say it once. After that the index points to it, and the agent reads it whenever the work touches it.

<rule in one sentence>
**Why:** <the failure that made me write this>
**How to apply:** <when it triggers, what to do instead>

Play 4: Keep project truth in the repo, and look at code before memory

What. Global memory holds cross-project preferences and pointers. Specs, contracts and boundaries live in the project's git. When the agent looks something up, code comes before memory, because code tells the agent how things are now.

Why. Memory goes stale. Memory tells the agent what was true. Code tells it what is true. A memory trusted above the code is worse than no memory, because it looks grounded.

How I do it. Each project has a CLAUDE.md as its contract (13 of them plus a root router), specs in docs/, boundaries in an invariants file. Some memories are nothing but a pointer into the repo. When an agent resolves an open question, it walks a fixed lookup order, and memory is fourth of five, marked as less reliable than code. When two sources disagree, nobody wins by rank. The disagreement becomes an R2 question for me. I covered the assumption log that catches the same failure at the input gate in Formwork for agents.

Memory tells the agent what was true. Code tells it what is true.
Look up an open question in this order:
1. Invariants   2. Code   3. Docs, specs and decision notes
4. Memory (may be stale)   5. The human
Two sources disagree: raise an R2 question. Don't pick a side.
Five-rung lookup ladder for an open question: invariants, then code, then docs and specs, then memory tagged as possibly stale, then the human. A side rule shows that when two sources disagree, nobody wins and an R2 question goes to the human.
Code sits above memory. When two sources disagree, nobody wins by rank: it becomes an R2 question for me.

Part two: drift defenses, so the agent can't quietly move what was decided

Memory gets the agent to the right starting point. It does nothing about the drift after. For that, every decision needs a form the agent can be checked against, and someone other than the author does the checking.

Drift-defense pipeline from left to right: rank backlog, size by entry signals, spec with K-n criteria, interrogation through six lenses, explicit lock, two-way traced plan, task interrogation, implementation, an independent gate on another model, QA and deploy, then prod observation. A return loop sends what prod taught through a candidates file and human promotion into the invariants registry, which bounds the next spec and checks every diff. Five nodes are human gates.
Five human gates on the whole line. Everything else is agent-run, and what prod teaches loops back into the invariants registry.

Play 5: Size the process by entry signals

What. Pick the weight of the process from objective entry signals.

Why. "The feeling that a task is simple is not a signal." Run everything heavy and you drown. Run everything light and payments ship on vibes.

How I do it. Three modes. When in doubt, go higher: an extra stage costs an hour, a skipped one on a heavy task costs a prod incident. Every step is also allowed to say "this task doesn't need the pipeline".

ModeEntry signalSteps
Lightone file, one revert, prod untouchedimplement, QA
Standardseveral files, one servicespec, plan, implement, gate, QA
Heavyany of: DB migration, payments, cross-service contract, irreversible op, prod user datafull pipeline

Play 6: Lock the spec with checkable criteria

What. Every acceptance criterion gets an ID, a verification method and a HARD or SOFT label. Then the spec locks, and the lock is my explicit yes.

Why. A criterion you can't check is an opinion, and an agent will happily reinterpret an opinion halfway through a long session.

How I do it. At most 10 HARD criteria; past that, the spec gets cut into iterations. The "out of scope" section can't be empty, because an empty one means nobody decided where the edge is. "The spec is not edited after LOCK." A serious question found later opens a new iteration.

K-3 [HARD] <observable behaviour>
  Verify: <test, query or device step that proves it>
Out of scope: <at least one line>

Play 7: Interrogate until two clean rounds

What. Before the lock, question the spec through fixed lenses, rank every question by blast radius, resolve what the sources can answer, write each answer into the spec, repeat.

Why. The gap you didn't ask about is the one the agent fills with a confident guess.

How I do it. Six lenses: data and migrations, interfaces and contracts, money and billing, failures and rollback, security and access, operations and observability. The loop stops after two consecutive rounds with no new R2 or R3 question, and the second round shuffles the lens order. Only the R2 and R3 questions the sources can't resolve reach me, in one batch, worst first; drip-feeding one per message is forbidden. "A resolution left in chat does not exist." Each task then gets a sibling loop with developer lenses before the first line of code, where "an answer without a source is not an answer". Why that dial runs the whole gate is in Formwork for agents.

RankMeaningEffect
R3irreversible, schema, paymentsblocks LOCK, always
R2changes criteria or scopeblocks LOCK
R1implementation detaildoesn't block, human never sees it
R0cosmeticignored
Interrogation loop with four stations: ask by lens across data, contracts, money, failures, security and operations; rank blast radius from R0 to R3; resolve from sources; write into the spec. A decision asks whether any new R2 or R3 question appeared; yes resets the count, two clean rounds in a row lead to an explicit human lock. Unresolved R2 and R3 questions go to the human in one batch, worst first.
One clean round is not enough. The loop stops after two in a row, with the lens order shuffled on the second.

Play 8: Trace both ways

What. Map every criterion to tasks and every task back to criteria.

Why. Drift shows up as a reasonable task nobody asked for.

How I do it. A criterion without a task is a hole in the plan. A task without a criterion is scope creep, and it blocks the plan gate. Every irreversible task carries a rollback step and a risk window.

K-1: T-1, T-2        T-1: K-1
K-2: (none) HOLE     T-4: (none) SCOPE CREEP, gate blocked

Play 9: An independent gate on a different model

What. The agent that wrote the work never grades it. A separate gate checks the diff against the task, the spec and the invariants.

Why. An author grading its own work in its own words reads what it meant to do.

How I do it. The gate runs on a different model from the implementer and fetches the spec from the source itself. If someone says "the spec says X", it checks the actual text. An invariant violation turns the gate red even if the criterion formally passes. "The gate reads the code, QA runs it", and QA runs even after a green gate. Done means two clean rounds in a row, the second on unchanged code. Four rounds without that and the work comes back to me.

Gate verdict per criterion: K-n PASS | FAIL + evidence
Red if: any HARD fails, or any invariant is violated
Done: 2 clean rounds in a row. Cap: 4 rounds, then human.

Play 10: Invariants with a mandatory "how to check"

What. Decisions that must hold for every future feature go into an invariants file in the repo.

Why. A spec covers one feature. Some boundaries have to survive all of them, and an invariant you can't check is a wish.

How I do it. Two tests to get in: it constrains future work, and breaking it is expensive. Anything that "describes how it is, not how it must be" is a fact and goes elsewhere. Two failures look alike and are handled differently. A diff that breaks an invariant turns the gate red. An invariant whose own check fails on re-verification gets marked suspicious and raised as a serious question; it is never deleted silently. Example, INV-DB-22 on the Dream Book site: a meta description is never cut with slice(0,N) mid-word, because a broken snippet costs clicks. How to check: grep for slice(0, in meta contexts. Today there are 43 active invariants in 6 registries, 23 of them on the Dream Book site.

INV-<n>: <statement, must / never>
Source: <incident, spec or decision>   Date: <YYYY-MM-DD>
How to check: <command, query or test>   (mandatory)
Known exceptions: <or none>

Play 11: Agents propose, the human promotes

What. Agents never write to the invariants file. They append to a candidates file. A human promotes in batches.

Why. "A boundary an agent set for itself is overreach; it would silently steer all later specs."

How I do it. The bar for a candidate is low on purpose: "an observation, not a judgement". I promote after three questions. INV-DB-9 went that way. A new client analytics event appeared on 2026-08-03 and its share climbed from 17.1% to 42.6% over three days while the Android mix barely moved, 63.6% to 63.7% to 67.4%. People don't change behaviour in three days. The new script was still reaching returning visitors, and the analytics side closes its days in the same 48 hours. The rule: a new event's share can't be read for about 48 hours. How to check: re-measure the deploy day at +24h and +48h and expect a plateau. I promoted it on 2026-08-10. The Dream Book site's candidates file holds 16 entries right now.

Promote only if all three are yes:
1. Does it constrain future work?
2. Is it expensive to violate?
3. Can it be checked?

Play 12: Close the loop from prod

What. A deploy isn't done until observation is on the calendar. Whatever prod teaches, the gates included, becomes an invariant.

Why. Some mistakes start above every gate. Mine started in my own head. I decided to cut call-to-action surfaces and store-push triggers on the Dream Book site. The agent pushed back and tried to talk me out of it. I insisted. It went live on 2026-07-03, built exactly as I asked, every gate green. Every gate checked the code against the spec. Nothing checked the spec against the users. Within days, store conversion per session fell sharply, and I reverted on 2026-07-08, five days later.

The human gate is a place where things break too. The system tried to stop me, and only watching prod caught me. The rollback taught the rule that lasted. Copy on that site lives in S3, so a git revert alone doesn't bring it back. That became INV-DB-4: prod copy is rolled back through S3. Its "what the gates didn't catch" field records exactly that gap.

Every gate checked the code against the spec. Nothing checked the spec against the users.

How I do it. After a release I run the observation: a quick check of a few critical metrics, or a full review that lines dips up against deploy dates and commits. The signal doesn't have to be a dashboard. On 2026-10-13 a single one-star review led to the resume trap: people who closed the app before the paywall were thrown back into onboarding, 19% of the users who had already received an interpretation. The fix went through a spec and a plan with five tasks, and ten acceptance criteria passed on a real device. This is the reflection loop from You govern machines. You manage people. with an address: what prod teaches edits the registry.

Incident to invariant
What happened: <metric, before and after, dates>
What exactly the gates didn't catch: <the gap>
How to check: <the new check>

"An incident without a registry entry is unresolved."

Invariant lifecycle as a closed loop: a prod signal the gates missed, an agent-written candidate, a human check with three questions, a registry entry with a mandatory how-to-check, the gate checking diffs, and human prod observation feeding the next signal. Side branches show a diff that breaks an invariant turning the gate red, and a failed periodic re-check marking the entry suspicious instead of deleting it.
How prod becomes a rule. Agents propose candidates, I promote them, and an invariant whose own check fails is marked suspicious, never silently deleted.

Where the human sits

Counting it up, I sit at five gates. I rank the backlog, because the agent doesn't. I answer the unresolved R2 and R3 questions in one batch and give the explicit yes that locks a spec. I take the work back when four rounds don't converge. I promote invariants. I run the prod observation and decide what it means. Commits go out through me. Everything else is written down: about 2,535 lines of process the agents follow.

The honest limit: this is an operator's system. I don't claim to write the code, and I can't promise these plays survive every codebase. They hold on my projects because I keep correcting them, and every correction becomes another file.

Start here: three plays for day one

You don't need 230 files or twelve plays. Start with three.

  1. Play 1, one fact per file, typed and dated, with a one-line pointer in an index that loads every session. This is what turns "re-explain everything" into "recall the project".
  2. Play 9, an independent gate on a different model that reads the spec from the source. The author of the work stops being its judge.
  3. Play 10, an invariants file with a mandatory "how to check", written by you, proposed to by agents. Decisions that matter survive the next feature.

Add the rest when one of them breaks. Take the structure, bend it to your stack, and tell me what breaks. That's how mine got built.