Version: mempalace 3.9.0 (PyPI), served via mempalace serve, backend chroma
Summary
The protocol string returned by mempalace_status is hardcoded and cannot be
extended. It instructs the reading agent to query the palace before answering
and never to guess. An operator who knows their corpus carries a systematic
bias has no way to tell the agent that is reading it.
Where
mcp_server.py:2278. No config.json key and no environment variable appends
to or overrides it — I checked for MEMPALACE_PROTOCOL, protocol_override,
custom_protocol, and environment reads around the string.
Worth noting the boundary falls in a slightly surprising place: topic_wings
and hall_keywords are operator-configurable via config.json, so the palace
already accepts operator input about how it classifies — just not about how its
output should be read.
Why it matters: two independent, measured biases
1. Extraction keeps rules and drops exceptions.
Scoped honestly: one model, at one size, under four schema shapes. Not a claim about extraction in general.
A passage whose entire thesis was a caution had its positive claim extracted on
every run (bm25 | is | a FOREIGN-VOCABULARY detector) while the contrast that
carried the warning — not a confabulation detector — was dropped in all four.
Attempts to require the missing content made it worse rather than better:
- a free-text
caveatsarray returned commentary about the note ("The note does not specify...") rather than the warning the note issued; - renaming it
warningswith an explicit description — imperatives the NOTE ITSELF issues, quote the note, NOT observations about what it omits — produced exactly one item, and it was the forbidden kind; - tightening to a
{is, is_not}object produced tautologies:is: "below a confabulation at 0.473",is_not: "above a confabulation at 0.473".
minItems: 1 forces presence, not correctness. Notably, the negative predicates
in this palace that are trustworthy — do_not_trust, must_not_be_used,
should_not_be_used_in, around 38 distinct ones — all appear because the model
volunteered them, not because a schema demanded them.
2. Retrieval is dominated by base rate in a skewed corpus.
One wing holds roughly 91% of ~3.4k drawers. total_before_filter reads 20
unscoped against 10 when scoped to a small wing, so the candidate pool is drawn
palace-wide and capped. A generic-vocabulary query whose best answer lived in a
10-drawer wing returned 5 of 5 results from the large wing. The relevant drawer
scored 0.441 against a top result of 0.486 — marginal on relevance, but rarely
in the candidate set to be ranked at all.
These are different mechanisms with different fixes. Both are facts the operator knows and the reading agent cannot.
Why a local patch is not a workaround
The image here is FROM python:3.12-slim + pip install --no-cache-dir mempalace==3.9.0, so editing mcp_server.py in site-packages is discarded by
the next image build, silently and with no warning.
More importantly, and this is the part that would rule it out even if it were easy: a protocol that sometimes carries the caveat and sometimes does not is worse than one that never does, because a reader cannot distinguish it from its own absence. The workaround would introduce the exact failure mode the caveat exists to prevent.
Request
A config key (e.g. protocol_append in config.json) or an environment
variable whose contents are appended to the protocol payload returned by
mempalace_status.
Append rather than replace, so the shipped guidance stays authoritative and the operator's note is strictly additive.
Current mitigation, and its limit
Per-machine context injected at session start — a CLAUDE.md on one machine,
memory notes on another. It is durable and it works, but it is per-machine: a
machine added later silently has no copy, and nothing reports the gap. That is
why the caveat wants to live with the corpus rather than with the reader.
Activity
0No activity yet.