fix(hooks): auto-mine transcripts in convos mode with stable wing - #1053
fix(hooks): auto-mine transcripts in convos mode with stable wing#1053kasparas-anusauskas wants to merge 1 commit into
Conversation
The Stop/PreCompact auto-mine invoked `mempalace mine <dir>` with no
mode flag, so Claude Code session transcripts were ingested in the
CLI's default `projects` mode and landed under an encoded-path wing
(e.g. `-Users-you-Projects-my_app`) with everything in `room:general`.
Detect the mine-dir source in `_get_mine_target` and build the command
via `_build_mine_cmd`:
- source=transcript -> `--mode convos --wing conversations`
- source=mempal_dir -> `--mode projects` (wing left to the miner;
existing behavior)
New optional env vars for explicit override: MEMPAL_MODE, MEMPAL_WING,
MEMPAL_EXTRACT. Unknown values fall back to auto-detect with a log
warning — never silent-drop, since silent-drop would let the CLI's
`projects` default reassert and reintroduce the original bug.
`MINE_MODES` and `MINE_EXTRACTS` now live in cli.py as the single
source of truth for both the argparse `choices=` lists and the hook
validator.
Completes the intent of MemPalace#633 (jphein, closed as superseded) on the
current `hooks_cli.py` architecture that MemPalace#840/MemPalace#863 established.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hi, thanks for the contribution. This PR has merge conflicts with Could you rebase onto If this change is no longer relevant, feel free to close the PR. (This message is part of a periodic backlog pass, sent to all open PRs that match this state.) |
|
Operator follow-up — we landed an architecturally adjacent change in our techempower-org/palace-daemon fork yesterday (2026-05-14) that supersedes the hook side of what this PR is fixing. Sharing in case the design point is useful. What we didSame problem class — hooks were producing low-quality drawers because of how they wrote — but we took it a step further by collapsing the two parallel write paths into one. Before:
Both paths emit drawers, with different wing/room/ID conventions, different metadata fields, different chunking behavior. After (our Phase 1D refactor):
So all your fixes here ( Wing derivation specificallyWe also tightened wing derivation. Our hook.py
Trade-offsThe "single write path" approach is more invasive than just adding Happy to share the full refactor diff if useful. Operator details in our changelog at familiar.realm.watch CHANGELOG.md. |
Problem
The plugin's Stop/PreCompact hooks auto-mine Claude Code (and Codex) session transcripts on every save trigger, but the invocation is hardcoded as
mempalace mine <dir>with no--modeflag. The CLI default isprojectsmode, which treats the JSONL as a source file.The primary defect is chunk quality. Projects mode runs the project-file chunker over the JSONL, producing 800-char sliding windows over raw
{"role": "…", "content": "…"}records. Drawers cut mid-JSON and mid-sentence:These drawers are BM25-findable but their embeddings represent structural boilerplate, and when they surface in search results the returned text is unreadable — users can't reconstruct what was actually said. That defeats the point of storing the transcript.
Convos mode chunks via
chunk_exchangesafter normalizing the JSONL into>-prefixed turn markers, emitting one semantically whole Q+A exchange per drawer. That's the chunk-level quality difference this PR delivers.Secondary cleanup also fixed:
mempalace.yamlin the transcript folder, projects mode falls back to the encoded-path directory name — producing wings like-Users-you-Projects-my_app. This PR defaults to the stableconversationswing.room:general. Convos mode'sdetect_convo_roompicks a topical room per session from{technical, architecture, planning, decisions, problems}. Note on room-per-session semantics:detect_convo_roomis called once per JSONL file (convo_miner.py:442-446), so all drawers from a single session share one room by design. Per-chunk room routing requires--extract general(exposed here via theMEMPAL_EXTRACTenv var).Evidence (from a user's
~/.mempalace/hook_state/hook.logon currentdevelop@ 32ec74d)Four independent signals confirm projects-mode fired: the
mempalace.yaml not foundwarning only fires in projects mode,Rooms: generalis the projects-mode single-room fallback, the source files are.jsonlsession transcripts, and the drawer count matches the orphan wing exactly.Prior art
PR #633 ("feat: two-layer hook capture, auto-mine transcripts", @jphein) proposed the same fix, but for the pre-migration standalone bash hooks (
hooks/mempal_save_hook.sh). It was closed on 2026-04-12 as "superseded by recently merged PRs to develop" — but the supersession was architectural only. PRs #840 and #863 moved hook logic from bash intomempalace/hooks_cli.pyand carried the infrastructure forward but not the correctness fixes:--mode convosflag on the auto-mine command anywhere in the plugin codepath ondevelop."conversations"in feat: two-layer hook capture, auto-mine transcripts, hook settings #633) was carried forward.grep "wing.*conversations\|chunk_exchanges\|PYMINE" hooks/mempal_save_hook.shondevelopreturns zero matches.This PR completes that migration — brings the correctness payload onto the
hooks_cli.pycodepath that the installed plugin actually uses.Changes
mempalace/hooks_cli.py_get_mine_target(transcript_path)returns(mine_dir, source)wheresourceis"mempal_dir","transcript", or"". Callers now know whether the mine dir came from a user-configuredMEMPAL_DIRor was derived from the harness's transcript path._get_mine_dir()kept as a thin wrapper over_get_mine_targetfor backward compatibility (tests and external callers unaffected)._build_mine_cmd(mine_dir, source)builds themempalace mineargv, driven by three new optional env vars:MEMPAL_MODEprojects/convos/autoautoconvosfor transcript-sourced mines,projectsforMEMPAL_DIR-sourced.MEMPAL_WINGconversations(transcript source only)MEMPAL_EXTRACTexchange/generalexchange)exchange→ one room per file viadetect_convo_room;general→ per-chunk rooms viageneral_extractor.memory_type.Auto-detect rule (default):
source=transcript→--mode convos --wing conversations(exchange-pair chunking, stable wing)source=mempal_dir→--mode projects(wing left to the miner, preserving existing behavior)Unknown values for
MEMPAL_MODE/MEMPAL_EXTRACTfall back to auto-detect / miner-default with a warning inhook.log. They are never silently dropped — silent-dropping--modewould let the CLI'sprojectsdefault reassert and reintroduce the original bug.Both auto-mine spawn sites (
_maybe_auto_ingest,_mine_sync) now use_get_mine_target+_build_mine_cmd.mempalace/cli.pyMINE_MODESandMINE_EXTRACTSadded at module level as the single source of truth.add_argumentnow useschoices=list(MINE_MODES)/list(MINE_EXTRACTS).hooks_cli._build_mine_cmdimports the same tuples. Adding a new mode to the CLI propagates automatically to the hook validator.tests/test_hooks_cli.py_get_mine_target(all four branches) and_build_mine_cmd(auto-detect for each source, every env-var override, invalid-value fallback + log-warning emission, extract-only-in-convos).test_precompact_mines_transcript_dir) updated: it assertedmine_dirwas the last argv entry, which no longer holds now that the command grows flags. It now locatesmine_dirby position after"mine".Compatibility
MEMPAL_DIRbehavior unchanged by default. Users who pointMEMPAL_DIRat a project directory keep projects-mode and existing wing-derivation rules._get_mine_dir()signature preserved, so PRs in flight that import it (e.g. fix: harden hooks, MCP server, and config (security audit) #893) continue to compile..jsonlwill delete-and-reinsert its drawers under the newconversationswing via the existingfile_already_mined+ mtime-rebuild path (convo_miner.py:320-333). Pre-existing drawers under ugly wings on static/old sessions remain in place — documented behavior of the purge-on-change pattern, not new to this PR.Relationship to other PRs
_ingest_transcriptPython-API path and does not modify_maybe_auto_ingest/_spawn_mine. This PR fixes the existing subprocess path; the two can land in either order without conflict._is_safe_transcript_pathand leaves the spawn sites alone.Verification
Primary benefit — chunk quality
Before (projects mode on JSONL, 800-char windows over structural noise):
After (convos mode with exchange-pair chunking):
Secondary — wing and room
Before (current
develop):After (this branch, default
--extract exchange):Test + lint
Tests:
pytest tests/ --ignore=tests/benchmarks -q→ 1047 passed.Lint:
ruff format --check+ruff check→ clean.Credit
Approach (convos mode + stable
conversationswing default) follows @jphein's design in #633. This PR brings it onto the currenthooks_cli.pyarchitecture that #840 / #863 established.