What's happening
Session compaction summaries get persisted as Evidence / Searchable memories, which lands them in the automatic per-turn recall pool. Each summary is a multi-thousand-character digest of a whole session, so it lexically matches almost any query in that session's topic area. The result: these blobs sort to the top of the candidate pool on unrelated queries and push real, atomic memories out of the recall top-k.
How I found it
Offline recall calibration against a real ~693-memory store turned up 10 documents titled compaction-boundary (4-7 KB each, facet session-summary, anchor compaction, class evidence, recall_mode = searchable). On queries that had nothing to do with them, they still scored at the top of the pool, above every genuinely relevant doc. They were one of the main reasons recall precision was so low (roughly half the auto-loaded memories were noise).
Root cause
SessionMemoryCheckpointFactory.ForCompactionBoundary enqueues a checkpoint with IsCompactionBoundary = true.
MemoryCurationPipeline.ResolveMemoryClass classes those as Evidence.
MemoryCurationPipeline.ResolveRecallMode maps Evidence to Searchable.
SQLiteMemoryStore.SearchByPlanAsync (automatic recall) fetches recall_mode IN ('auto','searchable').
So compaction summaries end up auto-recalled.
Compaction doesn't depend on this memory
The summary compaction needs for continuity lives in the SessionCompacted event and the in-session history. SessionCompactionPipeline inserts it as a [session-summary] observation, and on the next pass ObservationPromptBuilder.ExtractPriorSummary pulls the prior summary back out of session history. The compaction-boundary memory is written separately, by a fire-and-forget checkpoint in LlmSessionActor.HandleCompactionWorkCompleted, purely for cross-session recall. Nothing reads it back during compaction, so taking it out of recall won't change compaction behavior.
Fix
Special-case compaction-boundary checkpoints to MemoryRecallMode.Manual in ResolveRecallMode, before the Evidence branch. Manual and Never are excluded from every recall query, so the record stays in the store but never auto-recalls. About two lines plus a unit test.
Follow-up, separate: a one-time migration or netclaw doctor pass to declass the compaction-boundary rows already sitting in existing stores.
Acceptance criteria
What's happening
Session compaction summaries get persisted as
Evidence/Searchablememories, which lands them in the automatic per-turn recall pool. Each summary is a multi-thousand-character digest of a whole session, so it lexically matches almost any query in that session's topic area. The result: these blobs sort to the top of the candidate pool on unrelated queries and push real, atomic memories out of the recall top-k.How I found it
Offline recall calibration against a real ~693-memory store turned up 10 documents titled
compaction-boundary(4-7 KB each, facetsession-summary, anchorcompaction, classevidence,recall_mode = searchable). On queries that had nothing to do with them, they still scored at the top of the pool, above every genuinely relevant doc. They were one of the main reasons recall precision was so low (roughly half the auto-loaded memories were noise).Root cause
SessionMemoryCheckpointFactory.ForCompactionBoundaryenqueues a checkpoint withIsCompactionBoundary = true.MemoryCurationPipeline.ResolveMemoryClassclasses those asEvidence.MemoryCurationPipeline.ResolveRecallModemapsEvidencetoSearchable.SQLiteMemoryStore.SearchByPlanAsync(automatic recall) fetchesrecall_mode IN ('auto','searchable').So compaction summaries end up auto-recalled.
Compaction doesn't depend on this memory
The summary compaction needs for continuity lives in the
SessionCompactedevent and the in-session history.SessionCompactionPipelineinserts it as a[session-summary]observation, and on the next passObservationPromptBuilder.ExtractPriorSummarypulls the prior summary back out of session history. Thecompaction-boundarymemory is written separately, by a fire-and-forget checkpoint inLlmSessionActor.HandleCompactionWorkCompleted, purely for cross-session recall. Nothing reads it back during compaction, so taking it out of recall won't change compaction behavior.Fix
Special-case compaction-boundary checkpoints to
MemoryRecallMode.ManualinResolveRecallMode, before theEvidencebranch.ManualandNeverare excluded from every recall query, so the record stays in the store but never auto-recalls. About two lines plus a unit test.Follow-up, separate: a one-time migration or
netclaw doctorpass to declass thecompaction-boundaryrows already sitting in existing stores.Acceptance criteria