docs: the convos miner does check mtime — finish a correction that stopped half-way

ARCHITECTURE.md and README.md still carried the claim from 954c3f2 (initial
commit) that the convos miner "keys on source_file path alone (convos miner
doesn't check mtime)", and told the operator to delete the staging dir to force a
re-mine. 29e660e corrected exactly that claim in AGENTS.md and SKILL.md — and
missed these two files, so the repo has been documenting both behaviours at once
ever since. Two files said mtime is checked, two said it is not.

Ground truth, read off the deployed mempalace 3.7.1 rather than inferred:
convo_miner.py:657 calls file_already_mined(..., check_mtime=True) inside
mine_lock(source_file), and palace.py:1430 re-mines when no drawers exist for the
source_file, when the stored normalize_version predates the current schema, or
when the mtime differs. On a mismatch the file's existing drawers are purged
(_source_file_delete_ids -> collection.delete) before refiling, so a changed
transcript is replaced rather than doubled. The docstring names the case outright:
transcripts are not assumed immutable, since a session keeps appending to its own
file while active and /compact or /clear can rewrite one in place.

The stale advice was not merely out of date, it was expensive. "Delete the staging
dir to force a re-mine" is the one gesture that re-keys dedup: the staged path IS
the key, so a stage that is wiped or recreated elsewhere makes the palace refile a
whole wing as duplicates instead of replacing it. The docs now say so, name `touch`
as the non-destructive way to force a single session, and record why staged copies
must carry the source's mtime — with the corollary that an old mtime in a stage or
a remote inbox says nothing about when the file was shipped, so a ship is judged by
the feeder's log instead.

Sample output blocks quoting "(dedup by source_file)" are deliberately left
verbatim: that is what bin/mempalace-session:426 and bin/mempalace-pi-session:857
actually print. Tightening the wrappers' own wording is a separate change, because
the samples have to move with it.
This commit is contained in:
2026-08-18 09:42:40 +02:00
parent 947604b25d
commit fd8b15f570
4 changed files with 25 additions and 5 deletions
+6 -2
View File
@@ -91,7 +91,7 @@ Neither wrapper reimplements the mempalace miner. They each:
1. Curate input (filter / transform / rename).
2. Write it to a deterministic path under `~/.cache/…/<wing>/` with `mtime` preserved (via `cp -p` or explicit `os.utime`).
3. Delegate actual embedding + filing to `mempalace mine`, which already dedups on `source_file` path.
3. Delegate actual embedding + filing to `mempalace mine`, which already dedups on `source_file` path + `mtime`.
This keeps the wrappers thin. A third wrapper following the same idiom would justify factoring a shared helper library — two does not.
@@ -264,7 +264,11 @@ best, and lets a scoped `mempalace sync` prune every drawer mined from it at wor
Both wrappers dedup via `mempalace mine`'s built-in key:
- `mempalace-docs`: keys on `source_file` path + `mtime` → edit a doc, it re-mines; unchanged files are skipped.
- `mempalace-session`: keys on `source_file` path alone (convos miner doesn't check mtime) → a session's JSONL filename is `<slug>_<id>.jsonl`, stable per session, so re-runs skip already-filed sessions. To force re-mining, delete the staging dir.
- `mempalace-session` / `mempalace-pi-session`: the convos miner keys on `source_file` path + `mtime` as well (`file_already_mined(..., check_mtime=True)` in upstream `convo_miner.py`) → a session's JSONL filename is `<slug>_<id>.jsonl`, stable per session, so a re-run on unchanged content is a no-op, while a session that has grown or been rewritten in place is **purged and refiled** rather than skipped. Transcripts are not immutable — an agent appends to its own transcript while the session is live, and `/compact` or `/clear` rewrites one — so this is the common case, not an edge case.
Because the key includes `mtime`, a staged copy must carry the *source's* mtime, not the copy's: `mempalace-docs` stages with `cp -p`, and the session feeders stamp the source transcript's mtime onto the staged file. Side effect worth knowing when inspecting a stage or a remote inbox: staged files show the original session's timestamp, so an old mtime there is expected and says nothing about when the file was shipped. Judge a ship by the feeder's log, never by inbox timestamps.
**Do not delete a staging dir to force a re-mine.** Changed content re-mines by itself, and the staged *path* is the dedup key: if a stage is wiped or recreated somewhere else, the palace no longer recognises the old drawers as belonging to those sources, so the wing lands a second time instead of being replaced — and a scoped `mempalace sync` will prune drawers whose staged source has vanished. The non-destructive way to force one session is to `touch` its staged JSONL, which takes the purge-and-refile path without moving the key.
**Verified:** a second full `mempalace-session` run immediately after the first produces 0 new drawers. The only cost is the post-mine `repair` step (index rebuild — ~5 min on 5k drawers).