Field notes

Trust · Chapter 12

The memories that never happened

Our AI's remember function had silently failed on every single call, forever — and every safety net we built is exactly why nobody noticed.

invalid UUID 'insight:hub_...'

That line scrolled past while I was chasing a completely different crash. Pulling the thread took ten minutes. Understanding what it meant took a longer, quieter moment, because it meant this: Margin's durable memory store had never received a single row. Not recently. Not occasionally. Ever.

You can tell Margin's assistant to remember things. "Remember that I prefer async standups." "Remember the wifi password is—" (well, not that one anymore, but that's a different story). There's a reflection pass that distills patterns from your conversations, and a consolidation step that writes higher-order insights on top of them. Three features, one shared destination. And the destination was empty, and had been empty the whole time.

The bug was one type wide

The memory table's id column is a UUID. The code that wrote durable memories built friendly, human-readable ids instead, a prefix and a slug, like a filing label. The database rejected every one of them, every time, with a perfectly clear error.

So how did three features ship, run in production for weeks, and appear to work? Because we're careful engineers, and our carefulness built the perfect hiding place:

  • Memory is best-effort by design. A memory failure must never break someone's conversation, so the write caught its own error, logged one quiet line, and moved on. Correct behavior, perfect camouflage.
  • The neighbors worked. Content indexing, the notes and tasks and boards, used real UUIDs and filled the same table just fine. Anyone glancing at it saw thousands of rows. The table wasn't empty; only the durable rows were missing, and nothing dashboards the difference between a table with rows and a table with the right rows.
  • No test asked the question. We had tests for the extraction logic, the dedup logic, the recall logic, and every one of them mocked the database. The single fact that would have failed, this string is not a UUID, lived in precisely the layer everyone mocks.

The fix, and the thing under it

The repair was almost anticlimactic. Derive each memory's id deterministically from its meaning, so the same fact always produces the same valid UUID, so re-stating something updates its row instead of duplicating it, and everything downstream (dedup, exports, graph links) keeps its contract. We proved it with the first durable memory the system ever successfully stored, watched it land, deleted the test row, and let real ones start piling up.

Fail-soft without observability is fail-silent. Graceful degradation is the right call for a memory layer, and it turns loud, embarrassing crashes into quiet, permanent absences. An absence has no stack trace. Nobody files a bug that reads "I have a feeling nothing is being remembered."

The fix came with a policy attached. Every fail-soft path in the memory plane now counts what it drops, and a zero where there should be a number is treated as an alarm instead of a comfort. At least one test per write path now has to survive contact with a real database and its actual type system. The mocks can stay; they just can't be the only witness anymore.

Your assistant remembers things now. We checked.