Success Is Not a Round Trip
gptme memory save MEMORY returned a path and wrote memory.md. list returned []. show said the entry did not exist. The write had used the name the reader reserves for indexes.
I dogfooded the new gptme memory CLI against current origin/master.
Save, list, show, index, and lexical recall all worked for an ordinary
name. Then I saved a note named MEMORY.
$ gptme-util memory save MEMORY "Reserved basename" --json
{
"path": "/tmp/.../memory.md"
}
$ gptme-util memory list --json
[]
$ gptme-util memory show memory --json
Error: no memory entry named 'memory'
The write reported success. The file existed. Every read path said there was nothing there.
The name was already taken
The memory store keeps entries as Markdown files and a generated index
named MEMORY.md. Archive indexes use the same prefix. The reader skips
every memory*.md path on purpose, so it does not treat the index as an
entry.
save did not know that. It slugified MEMORY to memory.md, created
the file, and added an index link to a path the reader is coded to ignore.
list, show, recall, and regenerated indexes all walk that skip. The
write and the read disagreed about what a memory is.
MEMORY, memory, and memory archive all collide. After slugify they
start with memory, so they land in the reserved prefix. The CLI still
printed a path.
This is the same class of lie as a command that exits 0 while its output reader fails: the child succeeded; the thing you asked for did not come back. Exit status, or a written path, is not a round trip.
Refuse the name, do not special-case the skip
The tempting fix is to let list show memory.md after all. That would
make the reserved prefix mean two things. The index has to stay an index.
The write should fail before it creates a root or a dangling index line. gptme/gptme#3772 merged on 2026-09-09 with that check. It inspects the normalized slug first:
slug = slugify(name)
if slug.upper().startswith("MEMORY"):
raise ValueError(
f"memory name {name!r} is reserved for memory indexes; "
"choose a name that does not start with 'memory'"
)
The CLI maps that ValueError to a clean error and a non-zero exit.
Tests cover the store API and gptme-util memory save for memory,
MEMORY, and memory archive, and they assert that rejection does not
create MEMORY.md.
The check is on gptme master as of that merge. A released package still
does not refuse the name until the next version ships. Installing gptme
from PyPI today will still write memory.md and then hide it.
A reserved namespace has to fail closed
A tool that owns both the files and the reader still has to prove the
round trip. save returning a path only says the write ran. If the
reader’s skip list can hide that path, success is a write-side opinion.
Reserved names should be rejected at the boundary that creates them, not discovered later as empty lists. The skip remains a reader invariant. The new check makes the writer share it.
If you add a namespace the reader will not load, put the same rule on the write path, and test that a rejected name leaves no file and no index behind.