From 5c69f77b456323619b7247cd2d0b7463d4174c37 Mon Sep 17 00:00:00 2001 From: Anto01 Date: Sun, 12 Apr 2026 06:34:27 -0400 Subject: [PATCH] fix: cap per-entry memory length at 250 chars in context band MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A 530-char program overview memory with confidence 0.96 was filling the entire 25% project-memory budget at equal overlap score (3 tokens), beating shorter query-relevant newly-promoted memories (confidence 0.5) on the confidence tiebreaker. The long memory legitimately scored well, but its length starved every other memory from the band. Fix: truncate each formatted entry to 250 chars with '...' so at least 2-3 memories fit the ~700-char available budget. This doesn't change ranking — the most relevant memory still goes first — but it ensures the runner-up can also appear. Harness fixture delta: Day 7 regression pass pending after deploy. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/atocore/memory/service.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/atocore/memory/service.py b/src/atocore/memory/service.py index 2979f50..34241e3 100644 --- a/src/atocore/memory/service.py +++ b/src/atocore/memory/service.py @@ -413,8 +413,17 @@ def get_memories_for_context( if query_tokens is not None: pool = _rank_memories_for_query(pool, query_tokens) + # Per-entry cap prevents a single long memory from monopolizing + # the band. With 16 p06 memories competing for ~700 chars, an + # uncapped 530-char overview memory fills the entire budget before + # a query-relevant 150-char memory gets a slot. The cap ensures at + # least 2-3 entries fit regardless of individual memory length. + max_entry_chars = 250 for mem in pool: - entry = f"[{mem.memory_type}] {mem.content}" + content = mem.content + if len(content) > max_entry_chars: + content = content[:max_entry_chars - 3].rstrip() + "..." + entry = f"[{mem.memory_type}] {content}" entry_len = len(entry) + 1 if entry_len > available - used: continue