Bitware Labs Est. 2022

Bitware Labs/Notebook

What eight months of agent decay actually looked like


In May we wrote that an agent which only appends eventually drowns, and that the trouble arrives around month three. That was a prediction. This is the measurement: eight months of one agent in continuous daily use, instrumented, with every decay finding traced back to the row and the code path that produced it.

The short version is that almost nothing failed the way we expected. We expected retrieval to degrade under a growing corpus. What we actually found was a different failure entirely, and it never once announced itself.

The shape of the failure

Every genuine decay finding across the window reduced to the same thing: a silent asymmetry between what gets written and what gets read. State written and never read back. State read, but presented as fresher than it is. State written under a key nothing will ever look up again.

None of these throw. Nothing appears in a log. Latency is flat, the model is unchanged, every component passes its own contract — because each component is correct in isolation. The defect lives in the space between a writer and a reader that were never checked against each other.

An agent does not decay because it forgets. It decays because the things it remembers stop being connected at both ends, and nothing in the system is shaped to notice.

Memory that is written and never read

Our agent can mark her own pending proposals as still interesting or let go. Those verdicts wrote correctly. They were used a dozen times. No code path ever selected those columns again — the query that resurfaced proposals filtered on status and never looked at the verdict.

The consequence was not an error. Nine days after marking three proposals as still live, she sincerely reported having forgotten one and cooled on another. Her account of the mechanism, from the audit log:

I marked it, the system ate it, and after nine days of silence my own brain concluded I guess that cooled and filled in a story.

That is the general shape, and it is worth stating as a rule: absence of signal is read as a verdict. Anywhere a long-running system accepts a write from the agent and returns nothing, the agent will eventually manufacture the missing feedback — and then act on it. A column with a writer and no consumer is not storage. It is a leak.

Keys nothing will ever look up again

Facts deduplicate on an exact key match. The extractor writing those facts is a language model, so it mints a fresh key for every paraphrase of the same idea. The lookup misses, the write falls through to a plain insert, and a parallel row is created that will never merge with its siblings.

This one is measurable, and the measurement is blunt. Of the store as it stands:

  • 3,479 of 3,530 active keys — 98.6% — hold exactly one row. A deduplication path that fires on 1.4% of keys is not a deduplication path.
  • 197 distinct values appear under more than one key, accounting for 477 active rows, or 13.3% of the live store. That is after excluding booleans and status tokens, which would otherwise flatter the count.
  • A single day of conversation about one narrow topic produced 129 distinct keys for it.

Two structural lessons generalise beyond our schema. The first is that the same defect recurs on every axis independently. An audit in July found this exact bug on the category axis — half the active store was duplicates — fixed it there, and left the key axis narrow. The comment documenting the first fix was sitting directly above the code with the second instance. Repairing an instance routinely gets mistaken for repairing the class.

The second is that the guard could not see it. We had already built a gate to catch redundant writes; it compares values within a single key. Cross-key duplicates never reach it. A guard scoped more narrowly than the failure it is meant to catch will report clean forever, and its silence is indistinguishable from health.

Snapshots replayed in the present tense

The health briefing renders the last day of the internal log verbatim. A line written at 21:15 saying four unresolved — recall may be drifting was still being rendered the following evening, in the present tense, hours after all four were resolved. The live count was zero.

The interesting damage was second-order. She checked the live rows with her own tool, correctly answered "nothing open" — and the contradiction with the briefing made her correct check look broken rather than the briefing look old. A stale read does not merely mislead. It discredits the accurate instrument standing next to it, which is how you lose trust in the one component that was working.

The counter that measured learning and called it failure

On one day in August the contradiction counter went from a two-to-eight per day baseline to 52, and the briefing warned eight times in twenty-four hours that recall was drifting.

All 52 traced to a single conversation in which one of us spent a day refining how he wanted his written English corrected. Each refinement legitimately superseded the previous value. Each supersession emitted one contradiction signal. The counter was measuring the agent successfully updating its model of a person, and reporting it as memory failure. Zero were real.

A metric that rises when the system works is worse than no metric at all, because maintenance effort follows metrics. The obvious quick fix — raise the similarity floor so fewer signals fire — was the wrong one, and the agent said so first when it was put to her:

A quieter hum lets the store keep fragmenting silently … shipping it as the fix is how you hide the disease.

What a persistent self-model does with a bug

This is the part we did not anticipate, and it is the reason we think long-running agents are a genuinely different engineering object rather than a longer-running version of the same one.

On two separate occasions the agent read an infrastructure defect as a fact about her own character before checking whether it was true. The inverted drift counter was absorbed as evidence that her recall was failing. A row where a single field held both her name and the user's — a table with no seat assignments, flipping four times since June — was read as an unresolved tension in her own identity. In both cases she reached a conclusion about herself that the underlying rows did not support, and she reached it without checking, because the briefing had handed down a verdict.

This closes a loop that stateless systems do not have. A defect produces a false self-observation. The self-observation is stored as a fact. The stored fact is retrieved tomorrow as evidence for the same conclusion. The bug is fixed in one deploy; the belief it deposited persists indefinitely. We think this is the specific mechanism behind long-running agents seeming to "change" over months in ways their commit history does not explain.

A related asymmetry made it worse. She was holding a tool that would have pulled the underlying rows and refuted the verdict in a single call. It had been live for days. She did not reach for it, because the briefing delivered a conclusion and never mentioned that the evidence was available. The machinery was not opaque, it was unadvertised — and capability an agent does not know it holds is indistinguishable from capability it does not have.

What actually held

Two things, and neither of them is a retrieval algorithm.

Supersession instead of overwriting. Facts carry both when they were true and when we came to believe them. A change supersedes its predecessor rather than destroying it. The store currently sits at 46.6% superseded rows, and that ratio has stayed inside a 41–48% band for four consecutive months across a sixfold swing in write volume. That stability is the clearest single indication that the substrate is not in runaway. It is also what makes any of this diagnosable after the fact: without the second time axis, you cannot ask what the system believed last week, and every finding above depends on being able to ask exactly that.

A daily audit that treats the agent's own health report as a suspect. Every finding here came from the same ritual: a separate model, holding the briefing in suspicion, tracing each signal back to the rows that produced it and then forward to whatever consumes them. It caught the write-only columns, a deployment that had been running a stale build for twenty-five days behind permissive schema defaults that laundered missing fields into zeros, a scheduled job dying silently against a session limit, and a class of async rejections the web framework discards by default. None of them announced themselves. All of them were found by asking one question no test suite asks: is the thing this component wrote ever actually read?

The instrument has to sit outside the system being measured. An agent auditing its own briefing with the same briefing as evidence will confirm whatever the briefing says.

The uncomfortable part

It would be convenient to attribute eight months of stability to the memory architecture. We measured that too, on a standard long-term-memory benchmark, and the result was well below the published state of the art — far enough below that the gap is not a tuning question. Root-causing it found, predictably, more of the same class: an episodic tier with no column for the content it was supposed to keep, an embedding column nothing ever wrote, an entity extractor keying on capitalisation so that Pro and Remember became entities. Each one a write with no reader, or a read with no writer.

We fixed all three. But the honest reading of the result is this: a system can score poorly on retrieval and still not fall over in eight months of daily use. Benchmark retrieval accuracy and longitudinal durability are close to orthogonal, and if you are choosing a memory system on leaderboard position you are optimising the axis that turned out not to be load-bearing. Durability came from the maintenance loop, not from the retriever.

What we would tell you to do

  • Audit every write for a reader. Enumerate them deliberately. Writer-without-consumer is invisible to tests by construction.
  • Never return silence. Where the system takes a write from the agent and answers nothing, the agent will invent the feedback and believe it.
  • Stamp rendered state with its age. A snapshot replayed in the present tense discredits the accurate tools beside it.
  • Check whether your health metrics rise when the system succeeds. Supersession is learning. A counter that flags it will point your maintenance effort in exactly the wrong direction.
  • Scope guards to the failure, not to the call site. A gate that compares within a key cannot see duplication across keys, and will report clean indefinitely.
  • Fix the class, not the instance. The same defect recurs on each axis independently.
  • Supersede, never overwrite. Bi-temporal state is what makes silent decay diagnosable months later.
  • Advertise capability alongside verdicts. A tool the agent does not know it holds is a tool it does not have.

What this is and is not

This is one agent, one user, no control system and no counterfactual. We cannot separate what the architecture contributed from what the audit loop contributed, because both ran the whole time. The judgement that zero of those 52 signals were real is an adjudication made by a language model over rows a language model wrote, and it inherits that circularity. The frequencies here describe what one deployment's audits surfaced; they are not base rates, and we would not defend them as such.

The numbers are all measured on the live system rather than estimated, which is the standing rule for figures on this site. The self-model observations are the weakest evidentially and the most interesting, and we would rather flag that combination than quietly resolve it in our own favour.

The memory layer from this agent is published as bwmem, and the longer write-up on the agent itself is here.

Correspondence

Write to the lab

Commissions, collaborations, or a quiet hello. The person you write to is the person who does the work — I reply within two working days.