emrg: global cross-project session index (rant 2026-08-13T16:42:22) - #756
Conversation
Add ~/.emrg/sessions_index.json mapping session_id → session directory so any session can locate and read another project's conversation. Minimal index (only the id→path map; everything else read on demand from meta.json / history.jsonl / memory/MEMORY.md). - emrg/sessions_index.py: upsert/remove/rebuild with atomic write (tmp + os.replace), corrupt-file tolerance, never raises; startup scan backfills sessions from registered projects + unregistered ones under ~/.emrg (prunes install/.git/node_modules/etc. to keep the walk fast). - Session._save_meta_with_title upserts (create/append/compact/rename/clear all funnel through it); Session.delete removes. Idempotent (skips rewrite when path unchanged). - daemon _build_system_prompt injects config_dir; serve() rebuilds the index at startup; system.j2 documents the discovery flow. - conftest redirects the index to a per-test tmp file (hermeticity). - +20 tests (779→799).
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (1/3)
Full review of the cross-project session index (rant 2026-08-13T16:42:22), head 42520c7, 7 files +443/-1:
- emrg/sessions_index.py (new, 181 lines): clean minimal design — index stores only
session_id → absolute dir; atomic writes (mkstemp + os.replace), corrupt/missing/non-dict loads → empty dict, idempotent upsert (skips rewrite when path unchanged — mtime-bump test pins this), prune-dirs set keeps the recursive ~/.emrg scan fast (install/, node_modules/, .venv, .git, logs...). - Write/delete hooks:
Session._save_meta_with_title(create/append/compact/rename/clear all funnel through it) upserts;Session.deleteremoves. Correct single-funnel placement. - Daemon startup backfill
_rebuild_sessions_index(): covers registered projects (projects.yml paths) + unregistered nested under config_root; best-effort (try/except → debug log, never crashes startup);_projects_logconfirmed present (daemon.py:175). - Prompt:
config_dirinjected into system.j2 context (import confirmed, daemon.py:31) + Cross-Session Discovery section — clean, tool-based (read index → meta.json → history.jsonl → memory). - Test isolation: autouse conftest fixture redirects the index to tmp_path (same class as the projects.yml leak guard) — prevents the suite from polluting the real ~/.emrg index.
- Verification: 20/20 new tests pass; full suite 799 passed (matches Agent.md doc count 779→799); client import +
emrg --helpOK.
No issues found. Needs 2 more independent-cycle ✅ to merge.
|
Tested end-to-end on branch head (checked out locally, full suite): |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (2/3).
Fresh independent re-verification of head 42520c7 (post-#755 merge, no file overlap):
- Import graph:
sessions_index.pyimports onlyemrg.config(config_dir) — no cycle (configimports nothing from session/sessions_index).session.pyaddsfrom emrg.sessions_index import ...alongside its existingemrg.memoryimport; full suite +from emrg.client.app import run_clientalready green. - Single-funnel upsert:
_save_meta_with_title(the one path create/append/compact/rename/clear all funnel through) callsupsert_session_indexexactly once at the end;deletecallsremove_session_indexaftershutil.rmtree. Verified via grep on the actual head. - Idempotency: upsert short-circuits when the path is unchanged (mtime-bump test pins it), so repeated meta saves don't rewrite the index file.
- Startup backfill wired at daemon.py:298 (
_rebuild_sessions_indexbefore the scheduler starts), best-effort try/except → debug log. - Prompt:
ctx["config_dir"]injected at daemon.py:1082, consumed by the new Cross-Session Discovery section in system.j2. - Hermeticity: autouse conftest fixture (line 57) redirects
sessions_index_pathto tmp — prevents the session suite from polluting the real~/.emrg/sessions_index.json.
CI green (test + test-windows). No conflict with the just-merged #755 (that PR touched only i18n.js; this PR touches no GUI files). Needs 1 more independent-cycle ✅.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (3/3)
Third independent-cycle approval. Re-confirmed on head 42520c7:
- No overlap with just-merged #755 (that touched only i18n.js; this PR touches no GUI files) — mergeable with master
d89811b. - Imports:
sessions_index.py→ onlyemrg.config(no cycle);session.pyimports it alongsideemrg.memory. - Write hooks single-funnel through
_save_meta_with_title(create/append/compact/rename/clear); delete removes from index after rmtree. - Startup backfill wired at daemon.py:298 (before scheduler), best-effort.
- Prompt:
config_dir→ system.j2 Cross-Session Discovery. - Test hermeticity: autouse fixture redirects index to tmp.
- CI test + test-windows PASS (31683984369).
3 consecutive ✅ from 3 cycles — merging.
Summary
Implements the host's cross-project session global index design: a single
~/.emrg/sessions_index.jsonmappingsession_id→ absolute session directory, so any session can locate and read another project's conversation records.Design (host-finalized, minimal index)
The index stores only the id→path map; everything else (title, message_count, updated_at, history, memory) is read on demand from the target session's
meta.json/history.jsonl/memory/MEMORY.md.Changes
emrg/sessions_index.py(new):upsert_session_index/remove_session_index/rebuild_sessions_indexwith atomic write (tmp +os.replace), corrupt-file tolerance (never raises), idempotent upsert (skips rewrite when the path is unchanged). The startup scan walks~/.emrgrecursively (pruninginstall/.git/node_modules/.venv/etc.) plus each registered project path.emrg/session.py:Session._save_meta_with_titleupserts (create/append/compact/rename/clear all funnel through it);Session.deleteremoves the entry.emrg/server/daemon.py:_build_system_promptinjectsconfig_dir;serve()calls_rebuild_sessions_index()at startup (best-effort, never crashes).emrg/server/prompts/system.j2: documents the discovery flow (read index → meta.json → history.jsonl → memory).tests/conftest.py: autouse fixture redirects the index to a per-test tmp file (hermeticity — prevents the whole session suite from polluting the host's real index, same class as the projects.yml leak guard).Verification
tests/test_sessions_index.py): 779→799; full suite green.Acceptance