Compression Is a Design Choice
Claude-mem’s compression stage answers a precise question: once a tool event has been captured, what part of it should become durable knowledge for a later agent? This chapter follows the observer from its synthetic input to its structured output and then into the local stores. The result is a practical distinction between a useful observation and an exact transcript: the former is searchable and affordable, but it is produced by a model, bounded by field limits, and shaped by an explicit representation contract.
The observer is a reducer, not a second developer
The observer does not receive permission to inspect the repository or conduct a fresh investigation. Claude-mem constructs a synthetic message from the captured event. It identifies the tool that ran, the time it occurred, the working directory when available, the parameters, and the outcome. The observer is then asked to record what the primary session learned, fixed, decided, configured, or deployed. That instruction establishes a division of labor. The primary agent performs the work; the observer reduces evidence from that work into a form that another session can use.
This distinction matters because a tool event contains both signal and procedure. A file read may contain the explanation of a bug, but it also contains formatting, repeated context, and lines that mattered only to the immediate decision. A shell command may prove that a migration succeeded, yet the complete output is rarely the most efficient way to tell a future agent what changed. The observer’s job is to preserve the durable consequence without claiming that every intermediate byte deserves equal status.
The default code mode gives that reduction a vocabulary. An observation can be typed as a bug fix, feature, refactor, change, discovery, decision, security alert, security note, or sensitive information. It also receives concepts such as how-it-works, why-it-exists, problem-solution, gotcha, pattern, and trade-off. These are two different axes. “Decision” says what kind of work the event represents; “trade-off” says how the knowledge can be understood. Keeping the axes separate makes it possible to search for architectural reasoning without confusing it with every change that happened to touch architecture.
The output fields reinforce the same idea. A title gives a compact retrieval handle. A subtitle supplies a short orientation. Facts are intended to be concise and self-contained. The narrative carries the fuller explanation of what happened and why it matters. Concepts provide controlled categories, and file lists connect the observation to the project surface it concerns. This structure is not a cosmetic form. It is what allows the same record to serve several later consumers: a compact search index, a timeline, a full observation view, and a context block inserted into a new prompt.
A session summary operates at another scale. When the primary session stops, the observer is switched into a summary mode and is required to return a <summary> root rather than an observation. The summary records the request, investigation, learning, completion state, next steps, and notes. That separate protocol prevents a broad session recap from being confused with a single tool-level discovery. A later session may need both: a summary to understand the overall arc and an observation to recover the specific reason a file was changed.
The observer is also told when not to create an observation. If a tool use is noise, it can return <skip_summary />. A successful skip is meaningful: it says the event reached the reducer and the reducer judged that no durable record was warranted. That is different from a transport failure, a provider refusal, or malformed output, where the system cannot assume that the event was considered successfully. The protocol therefore carries both content decisions and processing decisions.
The boundary becomes visible in a debugging example. Suppose an agent reads a configuration file, discovers that a stale environment variable is being inherited, removes it, and reruns a test. The observer might produce one observation describing the cause and remedy rather than three disconnected notes about a read, an edit, and a command. If the command is routine and adds no new understanding, it may be skipped. If the provider returns ordinary prose instead of the required structure, that prose is not automatically promoted to memory. The system prefers a smaller number of interpretable records over a faithful-looking pile of unclassified activity.
That preference creates a responsibility for the prompt itself. The mode tells the provider which types and concepts are valid. The output skeleton names the fields and gives guidance about facts, narrative, concepts, and files. The observer is told that it is watching another session and must not describe its own actions. These constraints reduce ambiguity, but they do not make the model deterministic. The response still has to be parsed, and the parser must decide whether the response contains an acceptable root and enough meaningful content to store.
The record survives in layers—and loses information at each one
The first loss can happen before parsing. Claude-mem bounds each parameters and outcome field to 16,000 characters. When a field is larger, the prompt keeps a head and tail and inserts an explicit elision marker. The choice preserves the beginning, where a path or command header often appears, and the end, where an error or final result may appear. It does not preserve the missing middle. The observer is instructed to describe only the visible portion and not infer the elided range.
Images receive a separate treatment. Inline image data, such as base64 content embedded in a tool payload, is removed from the observer prompt and replaced with a small description that records that the bytes were withheld and how large the source was. A normal URL can remain because it is short and may carry meaning; the expensive inline payload does not. This protects the observer’s context window and prevents a screenshot-heavy session from repeatedly replaying useless bytes. It also means the observer cannot be expected to remember visual detail that was deliberately withheld from it.
The provider’s response then passes through a parser that looks for an observation, a summary, or an explicit skip. A fenced XML response can be unwrapped, but arbitrary prose surrounding an apparent tag is not treated as a general license to accept anything. Observation blocks are read field by field. Missing types can fall back to the first type declared by the active mode, while an emitted type outside the mode is logged and preserved rather than silently rewritten. Concepts are cleaned so that the observation type does not masquerade as a concept, and empty observations are rejected unless limited unstructured text can be salvaged into a title and narrative.
Summary parsing is stricter in a different way. It requires a summary block with at least one meaningful progress field. An empty wrapper is not allowed to become a false positive. These checks are small but consequential: they prevent a response that merely resembles the protocol from clearing the live batch as if it had created durable knowledge.
When parsing succeeds, the local worker stores the result transactionally in SQLite. An observation row can contain its type, title, subtitle, facts, narrative, concepts, files read, files modified, prompt number, provider model metadata, and session/project linkage. A summary has its own fields for request, investigation, learning, completion, next steps, files, and notes. This structured form is the canonical local record. It is the place from which later views can be rebuilt, even if a retrieval index is temporarily unavailable.
The storage transaction also applies a narrow deduplication rule. For each observation, claude-mem computes a short SHA-256-derived content hash from the memory session identity, title, and narrative. A unique constraint on the memory session and that hash prevents the same generated content from being inserted repeatedly. The scope is important. This is protection against duplicate writes within an observer memory session, not a general truth engine. Two observations can describe the same subject differently, and two sessions can independently produce similar observations. Deduplication removes one class of repetition; it does not decide whether one record is stale or whether two records contradict each other.
The raw tool-use side index remains separate from that canonical observation. When a concrete tool-use identifier is available, the original input and response can be retained for a later raw lookup. That lookup is useful when a compressed narrative omits an exact command result or literal payload. It is not the default representation, and its best-effort write should not prevent the structured observation from being generated. The system is keeping a deeper evidence trail without making every future context injection pay for it.
SQLite can also maintain FTS5 indexes over observation and summary fields, with triggers that keep those indexes aligned with inserts, updates, and deletes. FTS5 is good at exact terms and works without a separate vector service when it is available. The search layer contains a substring fallback for scripts that the default tokenizer cannot segment usefully. These details illustrate a larger design principle: a searchable record needs an index suited to its language and query shape, but the index remains a projection of the canonical row.
Chroma is another projection, optimized for semantic matching. Claude-mem decomposes an observation into separate documents for its narrative, any legacy text field, and each fact. Summaries are split by request, investigation, learning, completion, next steps, and notes; prompts are indexed as single documents. Every document carries the SQLite row ID and metadata such as project, platform, type, session, timestamp, concepts, and file lists. A semantic query can therefore rank a fact or narrative that uses different words from the query, after which the worker hydrates the corresponding canonical row from SQLite.
That indexing strategy introduces a second timing boundary. SQLite storage can complete before Chroma synchronization does. Chroma writes are batched and use deterministic document identifiers, so retries can reconcile existing documents rather than blindly append another copy. Watermarks advance only after a complete write, and a failed row remains eligible for backfill. If Chroma is disabled, delayed, or unavailable, the local record is not thereby erased; text search or filter-only retrieval can still serve as a fallback. Conversely, a Chroma result is not independent evidence of a memory. It is a ranked pointer into the stored record.
The same event can therefore have several legitimate states. It can be captured but not yet interpreted. It can produce a valid observation that is durable in SQLite but not yet indexed semantically. It can generate an explicit skip and leave no observation. It can return malformed output that is consumed or retried according to the failure class. It can produce a compact observation whose raw source contained more detail than the model saw because of field elision. Saying that claude-mem “stored the command” collapses these distinctions and makes the system sound more complete than the evidence allows.
The strongest interpretation is more modest and more useful. Claude-mem turns selected activity into a structured account that later retrieval can navigate. The account is intentionally smaller than the source event, and its fields are designed for search and context delivery. To use it well, a reader must know which layer they are looking at: a generated observation, a canonical row, a keyword or vector index, or the deeper raw tool-use evidence. The next step is to follow one event across those layers and test exactly when it becomes durable, searchable, and recoverable.