File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -451,6 +451,7 @@ def _invalidate_search_cache(self) -> None:
451451 self ._search_cache .clear ()
452452
453453 def _trim (self ) -> None :
454+ trimmed = False
454455 if len (self .facts ) > self .max_facts :
455456 keep = sorted (
456457 self .facts .values (),
@@ -466,13 +467,21 @@ def _trim(self) -> None:
466467 }
467468 self ._rebuild_indexes ()
468469 self ._pending_fact_ids .intersection_update (keep_ids )
470+ trimmed = True
469471 if len (self .edges ) > self .max_edges :
470472 keep_edges = sorted (
471473 self .edges .values (),
472474 key = lambda edge : (edge .weight , edge .created_at ),
473475 reverse = True ,
474476 )[: self .max_edges ]
475477 self .edges = {edge .id : edge for edge in keep_edges }
478+ trimmed = True
479+ if trimmed :
480+ # Trimming can happen during save after a search has populated the
481+ # cache. Any cached result may reference a fact or edge that was
482+ # just evicted, so it must not survive the capacity bound.
483+ self ._dirty = True
484+ self ._invalidate_search_cache ()
476485
477486 def save (self ) -> bool :
478487 """Persist the sidecar atomically; return whether the write succeeded."""
Original file line number Diff line number Diff line change @@ -214,6 +214,50 @@ def test_graph_search_cache_hits_and_invalidates_on_graph_mutation():
214214 assert store .stats ()["search_cache_misses" ] >= 2
215215
216216
217+ def test_graph_save_trim_invalidates_cached_evicted_facts (tmp_path : Path ):
218+ store = MemoryGraphStore (
219+ storage_path = tmp_path / "memory-graph.json" ,
220+ max_facts = 2 ,
221+ )
222+ evicted = store .add_fact (
223+ memory_id = "evicted" ,
224+ scope = "project" ,
225+ subject = "obsolete" ,
226+ predicate = "choice" ,
227+ value = "evict this fact" ,
228+ confidence = 0.1 ,
229+ observed_at = 1 ,
230+ )
231+ store .add_fact (
232+ memory_id = "kept-1" ,
233+ scope = "project" ,
234+ subject = "current" ,
235+ predicate = "choice" ,
236+ value = "keep this fact" ,
237+ confidence = 0.9 ,
238+ observed_at = 2 ,
239+ )
240+ store .add_fact (
241+ memory_id = "kept-2" ,
242+ scope = "project" ,
243+ subject = "current" ,
244+ predicate = "choice" ,
245+ value = "keep another fact" ,
246+ confidence = 0.9 ,
247+ observed_at = 3 ,
248+ )
249+
250+ assert [result .memory_id for result in store .search ("obsolete evict" )] == [
251+ evicted .memory_id
252+ ]
253+ assert store .stats ()["search_cache_size" ] == 1
254+
255+ assert store .save ()
256+ assert evicted .id not in store .facts
257+ assert store .search ("obsolete evict" ) == []
258+ assert store .stats ()["search_cache_hits" ] == 0
259+
260+
217261def test_graph_relation_prioritises_support_over_similarity ():
218262 store = MemoryGraphStore ()
219263 source = store .add_fact (
You can’t perform that action at this time.
0 commit comments