mempalace 3.7.1 · chromadb 1.5.6 · chroma backend · Windows x64 · MCP server
Full environment at the end.
Summary
When mempalace_search returns a result marked matched_via: "drawer+closet",
the text field does not contain the content of the drawer named by
drawer_id. Fetching that same id with mempalace_get_drawer returns different
content.
It is not only a rendering problem. The drawers returned are frequently unrelated to the query — they appear to be closet members surfaced positionally, wearing another member's text. So the caller loses the result slot as well as the attribution.
Results marked plain matched_via: "drawer" were correct in every case checked.
The stored data is fine. Drawers are intact and unduplicated. This is the search response being assembled wrongly.
Reproduction
mempalace_searchon any wing that has closets- Take any result whose
matched_viaisdrawer+closet mempalace_get_drawerwith that result's exactdrawer_id- Compare
Observed
Case 1 — four ids, one body of text
mempalace_search(query="anime guide episode", wing="anime-guide", limit=4)
All four results came back with byte-identical text — a passage about the
series The Dangers in My Heart — and four different drawer_ids. All four
were matched_via: "drawer+closet", all reporting drawer_index: 836 and
total_drawers: 1502.
mempalace_get_drawer("drawer_anime-guide_general_2a810bca4c25ba64c073c678")
returns something else entirely:
chunk_index : 626
line_start : 4702
line_end : 4714
content : "Guide regenerated — Banished from the Hero's Party slotted
into Tier 1 (Lighthearted Isekai), alphabetized to its B
position. ..."Different series, different topic, different region of the same source file.
Case 2 — smaller and fully enumerable
A 4.8 KB markdown file, freshly mined, 8 drawers total.
mempalace_search(source_file="C:\\Users\\drobe\\mempalace\\japanese-study\\files-inventory.md")
returned three results
with identical text, three distinct ids, each matched_via: "drawer+closet"
and each reporting drawer_index: 1, total_drawers: 8.
mempalace_list_drawers on the same wing shows those ids hold different chunks:
chunk_index line_start line_end
0 1 18
1 15 32
2 30 45
3 43 55
4 52 66
5 65 79
6 76 92
7 91 108Eight chunks, indices 0-7, a normal sliding window. Nothing duplicated.
Case 3 — unrelated drawers returned as top hits
mempalace_search(query="ko-so-a-do demonstratives kore sore are dore grid"),
no wing filter, limit 10. Five of the ten results were distinct ids from one
transcript, all showing the same text — a passage about building a
demonstratives grid. Plausible on its face.
mempalace_get_drawer on two of those five:
drawer_japanese-study_general_673331436ef8fb0318098e3a
chunk_index 497 lines 5771-5790
"const DAKU_CYCLE_MAP = (() => { ... ['か','が'],['き','ぎ'], ..."
drawer_japanese-study_general_45c9ad061870447f9ed75545
chunk_index 496 lines 5751-5776
"const FLICK_KATA = [ ['ア','ウ','イ','オ','エ'], ..."Both are JavaScript flick-keyboard layout arrays from an unrelated part of the file, adjacent to each other, with no bearing on the query. Half the result set was spent on them.
The closet they arrived through claims a line range nowhere near them:
closet_preview : "round/circular objects"|Claude;Cowork;Cleaned;Transcript;Japanese
|2026-07-24:L1-L28|→drawer_japanese-study_general_3d07366d251bcb43dc553c4a,
drawer_japanese-study_general_fc58f40713c490750a713ed5, ...
those drawers : lines 5751-5790, in source_file
japanese_learning_session_7-24-2026-COMPLETE-cleaned.mdA closet describing lines 1-28 listing members that live at line 5751 suggests the member list is itself misassigned, not merely rendered wrong.
What is NOT wrong
Ruled out, so the report is not confused with a storage or dedup issue:
- Drawer counts are accurate. Wing
cowork-agents: 60 drawers, 60 distinct content previews, zero repeats. Every source file's drawer count equals its ownchunk_totalexactly (BRIEFING.mdclaims 15, has 15). - No duplicate drawers. Both cases above have distinct
chunk_indexvalues and distinct line ranges. - Closet membership looks correct. Member ids in the closet preview resolve to real drawers in the right wing.
closet_boostranks correctly. In case 2 the boost surfaced the right file at the top, which is the intended behaviour.
Possible lead
In both cases every mismatched result reported the same drawer_index and
total_drawers as its siblings — 836/1502 in case 1, 1/8 in case 2. That looks
like the response is populating text once per closet and repeating it across
every member returned, rather than reading each drawer's own document.
Impact
Two costs, and the second is the worse one.
Attribution. A correct-looking drawer_id is paired with content that drawer
does not hold. Anything reporting provenance — which file, which date, which line
range a statement came from — is wrong while looking right.
Relevance. Closet-matched hits consume result slots with drawers that do not
answer the query at all (case 3: five of ten). There is no way to opt out —
closet_boost is not an exposed parameter, only max_distance can be tuned. A
duplicate result wastes a slot on something true. This wastes it on something
false and supplies a citation for it.
For agent callers following the documented protocol ("BEFORE RESPONDING about any person, project, or past event: call mempalace_search FIRST"), every search is a potential exposure.
Environment
mempalace 3.7.1
chromadb 1.5.6
backend chroma
palace C:\Users\drobe\.mempalace\palace
sqlite integrity ok, 0 errors (checked at time of report)
Python 3.14, 64-bit
OS Windows x64 <- build number: run `winver`
MCP transport stdio, launched via a .bat shim rather than python.exe
directly, because the MSIX-packaged client does not pass
user environment variables to the server process it spawns
MCP client Claude Desktop 1.37937.3 (Electron 42.10.0, Node 24.18.1)Palace scale, in case it is relevant to how closets are built:
12,515 drawers across 18 wings, all in one room ("general")
largest wing 3,162 drawers; the wing in case 3 holds 1,110
largest single source file 1,502 chunksUpgrade history. This palace went 2.0.0 -> 3.7.1 in August 2026, so the closet layer arrived with that upgrade and nothing mined before it is affected.
If it is useful to know whether this regressed, the pre-upgrade palaces are
still on disk at ~/.mempalace_2-0-0 and ~/.mempalace8-23 and can be queried
with --palace.
Still reproducing on 3.7.1. Adding an observation that may be more useful than another repro.
#2399 (merged) added a SQLite expression index for closet enrichment. Its own comment names the lookup:
That is the mismatch path. Enrichment resolves a hit's text by
source_file, which returns every chunk in that file rather than the one chunkdrawer_idnames. Whatever picks from that set is what pairs one drawer's document with another drawer's id. It also explains an observation in the original report: every mismatched sibling carried the samedrawer_indexandtotal_drawers.So the query that produces the wrong row is now indexed rather than re-keyed. #2399 is a legitimate speedup — I am only noting that it touched this path without changing what it selects on.
I cannot confirm the behavior on 3.9.0. I am on 3.7.1 and have not upgraded. Nothing in #2399 or the 3.9.0 changelog looks like it changes the keying, but anyone on develop can settle it in a minute:
mempalace_searchon any wing that has closetsmatched_viaisdrawer+closetmempalace_get_draweron that result's exactdrawer_idtextOne thing worth flagging about this class of bug: it fails quietly. No error, no empty result — a real drawer id paired with well-formed text that drawer does not hold. It will not generate repeat reports the way a crash would, so report volume is a poor signal of how often it happens.
#2381 is still open, with CI red on three platforms while develop is green.