Skip to content

Commit 79291a1

Browse files
joshuafontanyclaude
andcommitted
feat(searcher): include stored metadata on search hits
Hits from search_memories() carried only display fields (text, wing, room, similarity, distances); every field a consumer stamped at ingest was dropped in flattening. Downstream filters and re-rankers (session-aware recall, provenance rules, custom pipelines) need those fields to match on. Each hit now carries its stored metadata dict whole. Covered by a seeded round-trip test; internal underscore-prefixed fields still strip as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 208c307 commit 79291a1

3 files changed

Lines changed: 19 additions & 0 deletions

File tree

mempalace/searcher.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,11 @@ def search_memories(
13151315
"effective_distance": round(effective_dist, 4),
13161316
"closet_boost": round(boost, 3),
13171317
"matched_via": matched_via,
1318+
# The drawer's stored metadata rides through whole: consumers that
1319+
# filter or re-rank hits (session-aware recall, provenance rules,
1320+
# custom pipelines) need the fields they stamped at ingest — a hit
1321+
# flattened to display fields blinds every downstream filter.
1322+
"metadata": dict(meta or {}),
13181323
# Internal: retain the full source_file path + chunk_index so the
13191324
# enrichment step below doesn't have to reverse-lookup via
13201325
# basename-suffix matching (which silently collides when two

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ def seeded_collection(collection):
227227
"chunk_index": 0,
228228
"added_by": "miner",
229229
"filed_at": "2026-01-01T00:00:00",
230+
"origin_handle": "session-alpha",
230231
},
231232
{
232233
"wing": "project",

tests/test_searcher.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ def test_basic_search(self, palace_path, seeded_collection):
7373
assert len(result["results"]) > 0
7474
assert result["query"] == "JWT authentication"
7575

76+
def test_hits_carry_their_stored_metadata(self, palace_path, seeded_collection):
77+
"""Consumers that filter or re-rank hits need the fields they stamped at
78+
ingest; a hit flattened to display fields blinds every downstream filter."""
79+
result = search_memories("JWT authentication", palace_path)
80+
hit = result["results"][0]
81+
assert "metadata" in hit
82+
assert hit["metadata"].get("origin_handle") == "session-alpha"
83+
assert hit["metadata"].get("wing") == "project"
84+
# a drawer without custom stamps still carries its stored metadata whole
85+
other = search_memories("React frontend", palace_path)["results"][0]
86+
assert "origin_handle" not in other["metadata"]
87+
assert other["metadata"].get("room") == "frontend"
88+
7689
def test_wing_filter(self, palace_path, seeded_collection):
7790
result = search_memories("planning", palace_path, wing="notes")
7891
assert all(r["wing"] == "notes" for r in result["results"])

0 commit comments

Comments
 (0)