Observed on: 3.8.0, backend sqlite_exact, MCP over HTTP (docker image python:3.13-slim + pip install mempalace==3.8.0).
mempalace_update_drawer ignores a wing change that differs only by case, and reports success anyway. The response echoes the old wing, and the row is not touched — filed_at is unchanged. A caller that trusts success: true concludes the drawer moved when it did not.
Reproduction
Same drawer, same call, only the target string differs.
add_drawer(wing="ZZTestCaseRename", room="scratch", content="...")
-> {"success": true, "drawer_id": "drawer_ZZTestCaseRename_scratch_f627c5ad...", "wing": "ZZTestCaseRename"}
# 1. case-only change -> IGNORED, but reported as success
update_drawer(drawer_id="drawer_ZZTestCaseRename_scratch_f627c5ad...",
wing="zztestcaserename", room="scratch")
-> {"success": true, "wing": "ZZTestCaseRename"} # <-- the OLD value
# 2. control: same drawer, target differs by more than case -> works
update_drawer(drawer_id="drawer_ZZTestCaseRename_scratch_f627c5ad...",
wing="zz-test-case-rename", room="scratch")
-> {"success": true, "wing": "zz-test-case-rename"} # <-- changed
Confirmed against the store, not just the response: for a real case-only attempt on a
pre-existing drawer, select count(*) from documents where wing = 'AI-infrastructure' COLLATE BINARY
was unchanged afterwards, and the rows' filed_at still held their original timestamp.
Why it matters
Case-duplicate wings (AI-infrastructure vs ai-infrastructure, Projects vs projects) arise
naturally — different sessions capitalise differently — and they split wing-scoped retrieval, so a
list_drawers(wing=...) or wing-filtered search silently sees only part of the topic. Consolidating
them is the obvious fix, and it is exactly the operation that cannot be performed.
The failure is quiet in the way that matters: a bulk consolidation reports every call as successful.
In our run, 41 of 46 drawers moved (their wings differed by more than case) and 5 did not, with no
error on any of them. The distinction is invisible from the tool responses alone.
Note list_drawers(wing=...) is case-sensitive — AI-INFRASTRUCTURE returns 0 while
ai-infrastructure returns 10 — so listing does discriminate. It is only the update that does not.
Expected
Either the case-only rename is applied, or it fails loudly. Silently returning success: true with
the old value is the problematic part; a caller has no way to detect it short of re-reading the
store with a binary collation.
Workaround
Rewriting $.wing inside metadata_json directly, with the server stopped, does work — wing is a
generated column, so the index it backs recomputes. That is obviously not something a client should
have to do.
Observed on: 3.8.0, backend
sqlite_exact, MCP over HTTP (docker imagepython:3.13-slim+pip install mempalace==3.8.0).mempalace_update_drawerignores a wing change that differs only by case, and reports success anyway. The response echoes the old wing, and the row is not touched —filed_atis unchanged. A caller that trustssuccess: trueconcludes the drawer moved when it did not.Reproduction
Same drawer, same call, only the target string differs.
Confirmed against the store, not just the response: for a real case-only attempt on a
pre-existing drawer,
select count(*) from documents where wing = 'AI-infrastructure' COLLATE BINARYwas unchanged afterwards, and the rows'
filed_atstill held their original timestamp.Why it matters
Case-duplicate wings (
AI-infrastructurevsai-infrastructure,Projectsvsprojects) arisenaturally — different sessions capitalise differently — and they split wing-scoped retrieval, so a
list_drawers(wing=...)or wing-filtered search silently sees only part of the topic. Consolidatingthem is the obvious fix, and it is exactly the operation that cannot be performed.
The failure is quiet in the way that matters: a bulk consolidation reports every call as successful.
In our run, 41 of 46 drawers moved (their wings differed by more than case) and 5 did not, with no
error on any of them. The distinction is invisible from the tool responses alone.
Note
list_drawers(wing=...)is case-sensitive —AI-INFRASTRUCTUREreturns 0 whileai-infrastructurereturns 10 — so listing does discriminate. It is only the update that does not.Expected
Either the case-only rename is applied, or it fails loudly. Silently returning
success: truewiththe old value is the problematic part; a caller has no way to detect it short of re-reading the
store with a binary collation.
Workaround
Rewriting
$.winginsidemetadata_jsondirectly, with the server stopped, does work —wingis agenerated column, so the index it backs recomputes. That is obviously not something a client should
have to do.