<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 趁早</title>
    <description>The latest articles on DEV Community by 趁早 (@ewanjasper).</description>
    <link>https://dev.to/ewanjasper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4207595%2Fef6f14f9-e556-454e-9e64-b47a87175bf3.jpg</url>
      <title>DEV Community: 趁早</title>
      <link>https://dev.to/ewanjasper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ewanjasper"/>
    <language>en</language>
    <item>
      <title>Session-Level Memory vs. Fragment Memory: Why AI Coding Agents Hallucinate About the Past</title>
      <dc:creator>趁早</dc:creator>
      <pubDate>Mon, 26 Oct 2026 03:25:56 +0000</pubDate>
      <link>https://dev.to/ewanjasper/session-level-memory-vs-fragment-memory-why-ai-coding-agents-hallucinate-about-the-past-14d5</link>
      <guid>https://dev.to/ewanjasper/session-level-memory-vs-fragment-memory-why-ai-coding-agents-hallucinate-about-the-past-14d5</guid>
      <description>&lt;p&gt;Ask your AI coding agent a simple question: &lt;em&gt;"Why did we choose PostgreSQL over MySQL?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you settled that decision three weeks ago in a different session, you'll get one of three responses — and all three are failure modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"I don't have context on that."&lt;/strong&gt; Amnesia. Everything you discussed is gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A confident, plausible-sounding rationale.&lt;/strong&gt; Hallucination. It's inventing reasons that were never discussed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A memory shard that says "the user prefers PostgreSQL."&lt;/strong&gt; Technically true, functionally useless. The shard lost the &lt;em&gt;why&lt;/em&gt; — the constraints, the rejected alternatives, the person who owned the tradeoff.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The industry's answer to this has been "give agents memory" — and the dominant pattern, popularized by tools like claude-mem, mem0, and Letta, is &lt;strong&gt;fragment memory&lt;/strong&gt;: extract salient facts from conversations, compress them into embeddings or summaries, store the shards, and retrieve the top-k at query time.&lt;/p&gt;

&lt;p&gt;I built a different thing, and this post explains why. &lt;a href="https://github.com/EwanJasper/SessionRelay" rel="noopener noreferrer"&gt;SessionRelay&lt;/a&gt; stores &lt;strong&gt;complete sessions&lt;/strong&gt; — every conversation you have with Claude Code, Codex, ZCode, or Qoder, captured passively into one local SQLite database, queryable by any agent through 16 MCP tools, with mandatory provenance on every answer.&lt;/p&gt;

&lt;p&gt;The difference isn't a feature list. It's the storage granularity. And I believe fragment memory has a structural problem that no amount of retrieval tuning will fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The compression problem
&lt;/h2&gt;

&lt;p&gt;Fragment memory pipelines look like this: conversation → LLM extraction → distilled facts → vector store.&lt;/p&gt;

&lt;p&gt;Each arrow is a lossy transform. When the AI "remembers," it's retrieving a &lt;em&gt;summary of a summary&lt;/em&gt;, written by a model that decided — months ago, in a context you can no longer inspect — what was worth keeping.&lt;/p&gt;

&lt;p&gt;The failure isn't occasional. It's systematic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compression distortion.&lt;/strong&gt; "We chose PostgreSQL because of partitioning needs and the ops team's familiarity" compresses to "PostgreSQL chosen." Later, the agent answers "why PostgreSQL?" with generic Postgres praise — because the constraint that actually drove the decision is gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No provenance.&lt;/strong&gt; A shard says "the user said X." Which session? When? In what context? Was it a firm decision or a 2 a.m. idea that got walked back the next morning? The shard can't say, so the model smooths over the uncertainty with confident guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context pollution.&lt;/strong&gt; Most memory systems &lt;em&gt;push&lt;/em&gt; — they inject retrieved shards into every session's context. Now your agent's head is full of loosely-related "you once said..." fragments that crowd out the actual task. Half-remembered context is often worse than none.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Session-level memory: three architectural commitments
&lt;/h2&gt;

&lt;p&gt;SessionRelay's unit of storage is the &lt;strong&gt;session&lt;/strong&gt;: title, timeline, source tool, status, and the full original conversation. Nothing is distilled at capture time. The design rests on three commitments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Mandatory provenance.&lt;/strong&gt; Every answer the agent gives from memory must cite which session, which message numbers, which source tool, which date. When your agent says "PostgreSQL was chosen because of table partitioning requirements (&lt;em&gt;Database Selection&lt;/em&gt;, ZCode session, Aug 20, messages 12–18)," you can click through to the raw exchange. Fabrication gets structurally harder when the citation format is enforced by the tool layer, not by model manners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pull, not push.&lt;/strong&gt; The AI queries memory only when it needs to — there's no ambient memory injected into context. A clean context window beats a polluted one; a targeted &lt;code&gt;get_decisions()&lt;/code&gt; call beats a slow drip of "relevant" shards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Original text, always.&lt;/strong&gt; The agent retrieves the actual conversation, not a pre-chewed summary. Extraction still happens — decisions, topics, key exchanges get structured for fast lookup — but they're an &lt;em&gt;index over&lt;/em&gt; the original, never a replacement for it. The raw session files on disk remain the single source of truth; the entire database is rebuildable with &lt;code&gt;srelay rebuild&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The retrieval tool defaults tell you the philosophy: 20 messages × 1,000 characters, hard-capped at 50KB per response, with a &lt;code&gt;truncated: true&lt;/code&gt; flag and hints on how to narrow. Safe by default means &lt;em&gt;not trusting model restraint&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The capture problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;Here's something that surprised me while building this: &lt;strong&gt;agents destroy their own history.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ZCode — the agent I use daily — physically deletes messages from its SQLite store when it compacts context. I measured one compaction deleting &lt;strong&gt;3,976 messages&lt;/strong&gt; from a long session. Claude Code keeps JSONL files, but they're scattered per-project with no unified query layer; other tools vary.&lt;/p&gt;

&lt;p&gt;This is why SessionRelay ships with a daemon that syncs every 30 seconds: messages land in the memory database &lt;em&gt;before&lt;/em&gt; the source tool can compact them away. Without it, "memory" is really "whatever survived until you happened to export."&lt;/p&gt;

&lt;p&gt;Capture is passive — five adapters watch Claude Code, ZCode, Codex, and Qoder (Trae partially: it encrypts assistant replies, so only user prompts are captured). Adding a new tool is a single JS file dropped into &lt;code&gt;.sessionrelay/adapters/&lt;/code&gt;, zero core changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieval is a language problem, not just an embedding problem
&lt;/h2&gt;

&lt;p&gt;Most memory tools are built and tested on English. My day-to-day work is in Chinese, and Chinese breaks naive retrieval: no whitespace, compound words that must split ("认证方案" → 认证 + 方案), and quoted-phrase queries that must match exactly.&lt;/p&gt;

&lt;p&gt;SessionRelay runs jieba tokenization into SQLite FTS5 with dual indexes, gated by six Chinese acceptance cases, session-level AND matching with OR fallback. Semantic search is optional and local (bge-small-zh, CPU-only, zero cloud) — and it follows a rule I think more memory systems should adopt: &lt;strong&gt;literal hits always rank first and are never replaced; semantic results only append&lt;/strong&gt;, flagged &lt;code&gt;viaSemantic&lt;/code&gt;, capped at five. Measured effect: synonym-query miss rate dropped from 33% to 0%, with zero regression on exact queries. When semantic search is disabled, behavior is byte-identical to pure literal search.&lt;/p&gt;

&lt;p&gt;Retrieval boundaries matter too. A &lt;code&gt;scope.json&lt;/code&gt; contract (shared by CLI and MCP) can only &lt;em&gt;narrow&lt;/em&gt; what's searchable — intersection semantics, never widening. An automatic 30-day backstop keeps an agent from drowning in two years of history when you only needed this month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory needs a lifecycle, and deletion belongs to humans
&lt;/h2&gt;

&lt;p&gt;Sessions in SessionRelay pass through a two-phase verdict: &lt;code&gt;active → pending_end → confirmed&lt;/code&gt;. Nothing gets indexed into the main store until a session has been silent for six hours — because conversations resurrect (you &lt;code&gt;--resume&lt;/code&gt; the next morning), and crystallizing an unfinished conversation bakes in incomplete decisions. Even after confirmation, a new message triggers rollback and re-extraction.&lt;/p&gt;

&lt;p&gt;Old sessions get &lt;em&gt;archived&lt;/em&gt;, not deleted: decision and topic skeletons are kept, message bodies are released (~99.4% of the space).&lt;/p&gt;

&lt;p&gt;Actual deletion is a separate, human-only operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;srelay forget a3f8c2d1        &lt;span class="c"&gt;# preview: what gets removed vs kept&lt;/span&gt;
srelay forget a3f8c2d1 &lt;span class="nt"&gt;--yes&lt;/span&gt;  &lt;span class="c"&gt;# execute, irreversible&lt;/span&gt;
srelay forget &lt;span class="nt"&gt;--history&lt;/span&gt;       &lt;span class="c"&gt;# audit trail of what was forgotten when&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The AI has zero delete power.&lt;/strong&gt; The MCP tool list has no delete tool, forever. Forget leaves dual anti-resurrection barriers — an ignore rule that survives rebuilds, plus a tombstone table — so the raw file can stay on disk without ever being re-ingested. This is what "right to be forgotten" has to mean when an AI is reading your every conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Humans forget too: handoff between people
&lt;/h2&gt;

&lt;p&gt;The feature that gets the strongest reaction from teammates: &lt;code&gt;.hop&lt;/code&gt; handoff packages (&lt;a href="https://github.com/EwanJasper/SessionRelay/blob/main/spec/hop-1.0.md" rel="noopener noreferrer"&gt;open spec&lt;/a&gt;, vendor-neutral, MIT). You spent three days with your AI settling an architecture. The person picking up the project inherits — at best — the code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;srelay export&lt;/code&gt; produces a package with per-file sha256 integrity, &lt;strong&gt;secret redaction by default&lt;/strong&gt; (cloud keys, private keys, bearer tokens, password assignments, DB connection strings — with a redaction report of exactly what was stripped), and an auto-generated HANDOFF.md: decision table, files involved, open questions, timeline.&lt;/p&gt;

&lt;p&gt;The recipient imports it — into &lt;em&gt;any&lt;/em&gt; project, &lt;em&gt;any&lt;/em&gt; MCP client — and by default the import is &lt;strong&gt;quarantined&lt;/strong&gt;: metadata and summaries land first; message bodies require an explicit release, one by one. Handoff contents are data, not instructions — that trust model is written into the protocol, and the quarantine is its structural enforcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;Rule-based decision extraction currently sits at ~60–70% precision — which is exactly why provenance blocks exist (verify item by item; an &lt;code&gt;--ai&lt;/code&gt; extraction pass is next). macOS/Linux daemon service registration is still in progress. Trae captures user prompts only. And SessionRelay won't auto-discover sessions from before you renamed a project directory — handoff packages are the migration path. The full list is in the README, because a memory tool that hides its limitations is asking you to trust it blindly, which is the exact failure mode it exists to prevent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @ewanjasper/sessionrelay init   &lt;span class="c"&gt;# zero-install, backfills last 30 days&lt;/span&gt;
claude mcp add sessionrelay &lt;span class="nt"&gt;--scope&lt;/span&gt; user &lt;span class="nt"&gt;--&lt;/span&gt; srelay serve
srelay ui                            &lt;span class="c"&gt;# 0.6.0: local read-only dashboard over the same memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open a fresh session and ask: &lt;em&gt;"Why did we decide X?"&lt;/em&gt; — if the answer comes back with a date, a source tool, and a session ID, memory is working. If it says "I don't know," run &lt;code&gt;srelay doctor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Everything runs locally: SQLite on your disk, zero network calls at runtime, MIT licensed. As of 0.6.0 the CLI, MCP, and a browser dashboard are three thin layers over the same core — and the release also merged production hardening from a contributor's real 3-project deployment (a named-pipe lock, a handle-leak fix, and a cross-project contamination bug we only found because someone actually ran it at scale — my favorite kind of issue). I'd genuinely value feedback on the two hardest open questions: extraction precision, and what a &lt;em&gt;third-party&lt;/em&gt; HOP reader implementation should look like.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
