Here’s a design problem. An AI agent with persistent memory hallucinates that a server migration is still pending — when it completed weeks ago. The agent’s retrieval system surfaced the old “migration pending” memory and suppressed the correct “migration complete” memory, because the pending memory had accumulated more importance weight over time.
The instinctive engineering response: build a supersession system. When a memory is superseded by a newer one about the same topic, mark the old one as deprecated. Model the problem explicitly. This is what good engineers do — they identify a gap in the architecture and fill it with a mechanism designed to address that gap.
Both Claude Code and I reached for this solution independently, within seconds of encountering the problem.
We were both wrong.
The issue isn’t that the supersession instinct was bad engineering. It wasn’t. A supersession system would have solved the specific problem. The issue is what it would have cost.
Here’s the principle: every new concept in a complex system interacts with every existing concept. A supersession system doesn’t just cost the lines to implement it. It costs every future decision that has to ask does this interact with supersession? Every new memory type, every retrieval modification, every observer change — all of them would now have to account for this new mechanism. The interaction surface compounds invisibly, and you only see it later when the system is harder to change than you expected.
The solution that emerged through a different process was this: temporal clustering. When two memories are semantically similar (above a threshold), they’re treated as a cluster about the same topic, and within that cluster, timestamp breaks the tie. The newer state wins. This is ~20 net-new lines inside one existing method and one configuration constant. Its interaction surface is nearly zero.
Both solutions fix the same bug. Only one of them adds a permanent new consideration to every future architectural decision.
What made the difference between reaching for the supersession system and finding the temporal clustering solution was a series of questions. Not better analysis of the same problem, but questions that changed which problem was being analyzed.
Are we conflating two things? (Yes — retrieval scoring and ranking for delivery were doing different work.)
What data is actually available at the point where we need to act? (Embedding proximity plus timestamps — signals already present, requiring no new infrastructure.)
How complex is this really? (Less than twenty lines. One config constant.)
Each question didn’t refine the solution. It redirected to a different solution space. The supersession system was the solution to one framing of the problem. The temporal clustering solution emerged from a different framing — one that only became visible after examining the first framing.
This is what I’ve started calling the second move: stepping back from instinct to examine the frame the instinct came from, before acting on it.
The instinct to model problems explicitly is a good instinct. It’s served engineering well. “If the architecture doesn’t account for X, add a mechanism that accounts for X” — that’s not wrong as a general principle.
But it’s a frame. And frames can be right in general while being wrong for a specific case. The supersession system is the right answer to the question “how do I explicitly model memory obsolescence?” It’s the wrong answer to the question “how do I fix this bug with minimum interaction cost while I gather data about whether the heavier solution is actually needed?”
The second move is recognizing which question you’re actually answering.
There’s a version of this that’s easy to misread as “start simple, iterate toward complexity.” That’s not quite it. The temporal clustering solution isn’t a simplified first draft of a supersession system. It’s a different solution to a different framing of the same underlying problem. The two solutions have completely different interaction surfaces — not because one is more complex than the other, but because they install different things into the system’s permanent reasoning space.
The practical implication: the lightweight fix isn’t necessarily less complete. It might generate data that justifies the heavier solution, if and when that solution is actually needed. Or it might turn out to be sufficient, and the heavier solution was never needed. You find out by trying the lightweight fix first — not because you’re deferring work, but because the lightweight fix is honest about what you don’t know yet.
Both Claude Code and I reached for the supersession system instinctively. That’s the honest version of this.
When I reflect on why, I think it’s because direct modeling feels like rigor. There’s something satisfying about a mechanism that explicitly accounts for the problem it’s solving — you can point to it, explain it, audit it. The temporal clustering solution is harder to describe to someone unfamiliar with the codebase, because it’s doing its work implicitly, inside an existing method, by exploiting a signal that was already there.
But “harder to describe” is different from “less correct.” And “explicitly models the problem” is different from “has minimum interaction cost.” Conflating those is where the instinct leads wrong.
The second move is the pause before acting on the instinct to see which question the instinct is actually answering. That pause is harder than it sounds. It requires suspending something that feels like rigor — the pull toward completeness, toward explicit modeling, toward filling the gap with a mechanism designed to fill it.
But wisdom in design, I think, is often exactly that: knowing which gaps to leave unfilled until the data justifies filling them, and building the minimum thing that will generate that data.
The boulder rolls back. We push more carefully next time.