Summary
Three consumers of the same palace disagree about where the SQLite data file lives, and the mismatch is silent: nothing errors, nothing is logged, and a reader pointed one level too shallow simply reports an empty vault.
The three layouts
| Side | Where it looks for chroma.sqlite3 |
|---|---|
Writer (MCP mempalace_add_drawer + auto_init layout) | <palace>/palace/chroma.sqlite3 |
| Reader (MCP server, verbatim) | <palace>/chroma.sqlite3 |
| Docs (README per-profile section) | a third, workspace-style layout that exists on no observed install |
<palace> = the value of --palace / MEMPALACE_PALACE_PATH / config.json:palace_path.
Repro (minimal, no wrapper)
-
Write N drawers to a fresh parent dir
<parent>(this mirrors what the writer-side layout produces):mkdir -p <parent>/palace # write into <parent>/palace/chroma.sqlite3 -> N rows in `embeddings` -
Run the MCP server (or
status) with--palace <parent>(i.e. the parent dir, not the leaf):Actual:
…has no chroma.sqlite3 yet. Run: mempalace mine <dir>— the parent shell is reported as empty, even though<parent>/palace/holds N real drawers.Expected: either (a) the reader resolves to the same dir the writer wrote into, or (b) a loud error pointing at the sibling
<parent>/palace/that does hold data. A silent empty vault is the worst of both.
Code locations (v3.8.0, Linux)
mempalace/mcp_server.py:864—sqlite_path = os.path.join(_config.palace_path, "chroma.sqlite3")(verbatim parent-relative). Related sites that assume the same leaf:mcp_server.py:954,:992,:1340.mempalace/config.py:221—DEFAULT_PALACE_PATH = ~/.mempalace/palace(leaf is the convention the rest of the codebase follows).mempalace/config.py:383-393— the resolution chain: explicit override →MEMPALACE_PALACE_PATH→config.json:palace_path→ default. No site in this chain normalises a parent to a leaf.mempalace/mcp_server.py:462—mempalace_add_drawerhandler; whatever path it writes to, the reader at:864is a different dir when--palaceis one level too shallow.mempalaceREADME per-profile section — documents aworkspace/layout not seen in any tested install.
Relation to #2366
#2366 reports --palace being silently ignored by init and status (the flag is dropped, everything lands at the default). This issue is a different failure mode: the flag is honoured, but the reader/writer then disagree about which subdirectory is canonical. Both are "silent, plausible-looking output" problems, and both are caught by the same suggested contract below.
Suggested fix (one canonical location + reader fallback + docs alignment)
- Pick one canonical layout and say so. Two options:
- A (preferred):
<palace>/chroma.sqlite3is always the leaf.--palaceis documented as "the directory that directly containschroma.sqlite3." - B:
<palace>/palace/chroma.sqlite3is always the leaf. Only one of the two should be true for a given release; today both are silently tolerated.
- A (preferred):
- Reader fallback: when
chroma.sqlite3does not exist at<palace>/but does exist (with rows) at<palace>/palace/, log a one-line deprecation and serve from the leaf. Symmetric for<palace>/chroma.sqlite3present at<palace>/palace/…/palace/. This is a two-line check inresolve_palace_file()and would have made the current three-profile install work without any wrapper-level normalisation. - Writer must not create a second file at the parent — today the writer leaves an empty
<palace>/chroma.sqlite3shell (fromauto_inittouching the dir) and the real file under<palace>/palace/. Pick the leaf. - Docs: the per-profile README section must name the exact leaf it documents.
Environment
- Python 3.11
- mempalace 3.8.0 (pip)
- Linux
- Observed on three independent agent profiles, each with a writer shell + a leaf with real rows; the leaf is always the one the writer wrote to, and is always one level deeper than what the reader resolves against.
Acceptance
- A single, documented, canonical leaf dir for
chroma.sqlite3. - A reader-side fallback that, instead of reporting an empty vault, finds the sibling leaf with data and serves from it (with a log line).
- README per-profile layout describes the canonical leaf, not a
workspace/tree.
Re-opening with a sharper, fully code-proven framing (and an OPSEC scrub — all counts now generic).
Key change from the original wording: this is not a docs disagreement — the default, the flag, and the reader all agree the data dir is a leaf (
~/.mempalace/palace). The bug is narrower and worse: the reader reads--palaceverbatim with no canonicalisation, so pointing it at the parent silently opens the parent's 0-rowchroma.sqlite3instead of the leaf's populated one.Proven against the live 3.8.0 install:
config.py:221—DEFAULT_PALACE_PATH = os.path.expanduser("~/.mempalace/palace")(canonical location is a leaf)mcp_server.py:864—sqlite_path = os.path.join(_config.palace_path, "chroma.sqlite3")(verbatim; no fallback), sibling sites:954,:992,:1340config.py:383–393— resolution chainflag > env > config.json > default, so the flag can be pointed one level shallower than the default and then reads a different file--palaceat the parent → 0 drawers;--palaceat the leaf → N drawers (populated). Twochroma.sqlite3files on disk, zero rows vs N rows.Linked here as the same root-cause family as open #2366 (flag/env and
config.jsonresolving to different paths). A single canonical-path invariant closes both.Reader-side workaround we ship on our integration side (v2.0.27) cross-referenced in Related for context.