Summary
MemoryStore.recent(limit, tags=...) silently drops tagged memories that fall outside a hard-coded 10× fetch window. A tagged memory can exist in the database and still be invisible to recent(tags=...).
Where
src/localmem_mcp/store.py — MemoryStore.recent() (lines 279–291)
rows = self._conn.execute(
"SELECT * FROM memories ORDER BY id DESC LIMIT ?",
(max(1, limit) * (10 if tag_list else 1),),
).fetchall()
memories = [_row_to_memory(row) for row in rows]
if tag_list:
memories = [m for m in memories if _has_tags(m, tag_list)]
return memories[:limit]
The tag filter is applied in Python after fetching only the newest limit × 10 rows. Any tagged memory older than that window is never seen.
Reproduction
store.add("the tagged one", tags=["keep"]) # added FIRST (oldest)
for i in range(60):
store.add(f"untagged memory {i}") # 60 newer untagged memories
store.recent(limit=5, tags=["keep"])
# → [] (BUG: the tagged memory exists and should be returned)
Expected vs actual
- Expected:
recent(limit=5, tags=["keep"]) returns the tagged memory (docs promise "Only consider memories carrying all of these tags").
- Actual: returns
[] because the tagged row is at position 61, beyond the 5 × 10 = 50 row window.
Impact
The MCP recall_memory tool (no memory_id) and the CLI recall -n N --tag ... path both surface this: an agent asking for recent tagged memories can get an empty result even though matching memories exist. Silent data invisibility in a memory tool is worse than an error.
Suggested fix
Filter by tags in SQL (e.g. a LIKE/instr predicate on the comma-joined tags column, or a proper tag table), or loop fetching until limit tagged rows are collected. Add a regression test with the tagged memory older than limit × 10 untagged rows.
Summary
MemoryStore.recent(limit, tags=...)silently drops tagged memories that fall outside a hard-coded 10× fetch window. A tagged memory can exist in the database and still be invisible torecent(tags=...).Where
src/localmem_mcp/store.py—MemoryStore.recent()(lines 279–291)The tag filter is applied in Python after fetching only the newest
limit × 10rows. Any tagged memory older than that window is never seen.Reproduction
Expected vs actual
recent(limit=5, tags=["keep"])returns the tagged memory (docs promise "Only consider memories carrying all of these tags").[]because the tagged row is at position 61, beyond the5 × 10 = 50row window.Impact
The MCP
recall_memorytool (nomemory_id) and the CLIrecall -n N --tag ...path both surface this: an agent asking for recent tagged memories can get an empty result even though matching memories exist. Silent data invisibility in a memory tool is worse than an error.Suggested fix
Filter by tags in SQL (e.g. a
LIKE/instrpredicate on the comma-joinedtagscolumn, or a proper tag table), or loop fetching untillimittagged rows are collected. Add a regression test with the tagged memory older thanlimit × 10untagged rows.