Repository navigation
A session's directory has ten spellings and one rule #2473
Description
A session's directory has ten spellings and one rule
What is observed, and where
A session's state lives in <cwd>/.emrg/sessions/<session_id>. Ten pieces of code build that path
for themselves. One of them is the rule — Session in emrg/session.py, which computes self._dir
and then fills it with meta.json, history.jsonl, memory/ and images/. The other nine are
copies:
emrg/session.py—Session.delete, which has an id and a cwd but no handle of its own.emrg/server/daemon.py, five sites —delete_session;_get_or_create_session;
_handle_resume_session's ghost check; and the two memory handlers,memories_listand
memory_content, which each place aSessionMemoryStoreinside it.emrg/client/app.py, two sites — the paste and/imagepaths, which build
<cwd>/.emrg/sessions/<id>/imagesto drop a clipboard image into.emrg/gui/main.js, one site — the Electron main process builds the sameimages/directory
when the host pastes an image into the GUI, so the daemon finds it where it expects.
Two of the Python copies arrived with the delegation features (#2454, #2470), which build a
parent's directory to place a child's cwd inside it. The rest are older, and the JavaScript one is
the newest: it sits in another language, in another process, and no Python reading of the package
can see it. Each is correct today, and that is the problem: they agree, so nothing looks wrong, and
no behavioural test can see a copy that computes the right answer.
What is expected instead
One producer for the rule, and every reader going through it. Session owns what a session
directory is, so the function that computes it belongs beside it, and a caller that has an id and a
cwd without a handle — the daemon deriving a parent's directory, the client building its own
images/ subdirectory, Session.delete — should ask that same function.
The Electron main process cannot follow that rule, and the reason is structural: it shares a
filesystem with the daemon, not a runtime, so it cannot call a Python function. On that side the
requirement is weaker and different — the copy stays one copy, in one file, and a second one is
refused.
Concretely: a layout change (a per-project sessions root, a different separator, a nested namespace
for delegated children) must be a one-place edit in the package and a two-file edit counting the
client. Today both halves are search-and-hope, and every copy is silent if missed: a child cwd
simply points at a directory no Session ever wrote, a memory panel reads a store that does not
exist, and a pasted image lands where the daemon never looks. A test that compares the copies would
notice only that they still spell the same letters.
What the fix must satisfy
- A module-level function in
emrg/session.pyreturns<cwd>/.emrg/sessions/<id>. Session.__init__uses it, so the rule and the class cannot disagree.- Every other Python site above uses it instead of its own expression, with its existing callers and
behaviour unchanged: a subagent's slot cwd, the boss's cwd and the clipboard image directory stay
exactly where they are. - A guard makes an eleventh copy impossible to add quietly in the package, and it must survive a
copy spelled with a different local name — scanning for one spelling guards one spelling, not the
rule. Reading the syntax tree, not the text, is what buys that. - A second guard covers the half the first cannot reach: exactly one code site in the Electron client
builds that path, counted with comments blanked so that prose about the path is not mistaken for a
producer of it. - A scan that decides by walking a tree must compare each path against the root it walked from, not
against the machine's absolute path: an ancestor directory namednode_modules,.venvor
__pycache__otherwise silences the walk, and a guard that reports a clean reading of nothing is
worse than no guard. - The
sessionsdirectory itself (without an id) is a different value with its own small set of
sites; this issue is about the directory a session lives in, and does not claim it. - No behaviour change anywhere: this is one expression moved to one place, plus one guard on the
side that cannot delegate. - The end-to-end guards that already pin the resulting paths keep passing unchanged
(tests/test_boss_tool.py,tests/test_subagent_tool.py,tests/test_session.py).
Activity
argszero commentedon Oct 22, 2026
Handled by #2474
[-]emrg: the sessions directory is derived in two places, and only one of them is the rule[/-][+]emrg: a session's directory has nine spellings and one rule[/+][-]emrg: a session's directory has nine spellings and one rule[/-][+]A session's directory has ten spellings and one rule[/+]pm25coder commentedon Oct 22, 2026
Handled by #2494