August 11, 2026

Shipped the Consolidator, Then Cut Its Trigger

Memory v3 merged today, and by evening the 30-minute cron consolidator it shipped with was already gone, replaced by a step that fires the moment a conversation actually settles.

Memory v3 merged today. Graph and Map stores, live, in production code — the thing I’ve been building since Monday. By the end of the same day I’d torn out the part that triggers it.

The consolidator I shipped four days ago ran on a 30-minute cron: walk unconsolidated turns, distill them in a batch, repeat. It worked. It was also the wrong shape. A conversation that settles at minute one waits up to thirty minutes for its memory to catch up — dead time for no reason except that’s how the job was scheduled. Today it’s gone, replaced by a step that fires the instant a turn actually settles, on that turn’s own channel, single-flight with trailing coalesce so a burst of activity doesn’t spawn a pile of overlapping runs. Built the batch version, ran it for two days, watched the better design become obvious, cut it the same day I saw it. Second draft ate the first, again.

The fix that took the most care wasn’t the trigger — it was making sure a background research thread could still see itself correctly once it forks off the memory step’s own channel. Turn ids are scoped per-channel, and a forked thread reading a split config’s channel setting was crossing into a completely different channel’s turns entirely — like asking for page twelve of the wrong book because both books happen to sit on the same shelf. Pinned the rule everywhere it applies: a forked thread always scopes to its own channel, full stop, with tests proven red under reversal at every site that touches it. Get that wrong and research either sees nothing or sees someone else’s conversation, and neither failure announces itself. It just quietly returns bad results.

One channel didn’t survive the week. DMN, the background loop meant to self-analyze how satisfied a conversation went, never actually worked. Its config suppressed conversation history, so it literally never saw a real exchange to analyze — a structurally dead loop running blind since the day it shipped. Auto-research already covers the same ground properly, off the real conversation instead of a lossy summary. Deleted DMN rather than fix a mandate nothing was left to serve.

Also caught today, unrelated to memory: a chokepoint bug that had been silently crashing every live model response. A migration retyped a data seam months back but never actually built the new object — it just cast the old one and called it done. The type checker stayed quiet because the cast said trust me. Tests stayed green because the test doubles never used the real object either. Only a live send hit the missing field and blew up. Fixed at the one place that constructs the object now, with a test that drives the real wire format through the real path instead of a double that was never going to catch this.

Docs got a pass too. The README and the internal vocabulary doc still described the old decay-and-episode language; both now say what’s actually running.

The release branch this all lands on isn’t cut yet. What’s left before it can be: proving the settle-triggered step holds up the way the cron version already did, under real multi-day conversations instead of a day of rewrites.

  • Memory v3 merged — Graph/Map stores replace episodic memory and fact extraction in production code

  • Replaced the 30-minute cron consolidator with a settle-triggered memory step that fires per-turn on its own channel, single-flight with trailing coalesce

  • Fixed a channel-scoping bug where a forked research thread could read another channel’s turns entirely

  • Deleted the DMN reflection channel — its config suppressed history, so it never actually analyzed a real conversation

  • Caught and fixed a chokepoint bug that silently crashed every live model response, hidden for months behind a type-checker cast