Give ADR 0343 a home in design 36: a session's memory follows the operator (issue 367)
mesh/merge-gate head 213987b2cc94 merged into trunk 58a6415ff510 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 213987b2cc94 merged into trunk 58a6415ff510 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 213987b2cc94 merged into trunk 58a6415ff510 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 213987b2cc94 merged into trunk 58a6415ff510 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
This commit is contained in:
@@ -2,8 +2,9 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-catalog modules/claude-code]
|
||||
updated: 2026-10-10
|
||||
updated: 2026-10-13
|
||||
decisions:
|
||||
- "ADR 0343"
|
||||
- "ADR 0274"
|
||||
- "ADR 0273"
|
||||
- "ADR 0266"
|
||||
@@ -402,7 +403,7 @@ the helper of §9 where that is an agent account):
|
||||
"Completely empty", the operator's words, means: nothing in the home was put there by hand. What is not the
|
||||
module's rendering is the program's own working state, excluded by name in the module's documentation and
|
||||
never written or removed: the credentials file (§5), the history, the sessions and their environments, the
|
||||
projects and their memory, `todos/`, `plans/`, `statsig/`, the shell snapshots, the file history, the debug,
|
||||
projects and their memory (each project's memory folder is also carried between nodes since ADR 0343; see §12), `todos/`, `plans/`, `statsig/`, the shell snapshots, the file history, the debug,
|
||||
cache, paste and telemetry directories, the statistics, the daemon's files (`daemon-auth-cooldown` among
|
||||
them), the dot-markers (`.last-cleanup`), `skills/synced/`, and the backups, worktrees, jobs, feedback, state
|
||||
and IDE directories. **Two are the program's and watched**: `settings.local.json` (the permission answers a
|
||||
@@ -520,6 +521,33 @@ A node away for more than the 30 days the router keeps its record, or joining la
|
||||
approval and does not render it; the list says so. The module uses the `operator-channel` seat, which grants it the
|
||||
asker's subjects and its own records there.
|
||||
|
||||
## 12. A session's memory follows the operator
|
||||
|
||||
*Added 2026-10-13 by ADR 0343 (issue 367).*
|
||||
|
||||
A session started on another node starts with what the sessions on every other node learned. **The module
|
||||
carries each project's memory folder**, `~/.claude/projects/<project>/memory/`, and nothing else of the home.
|
||||
|
||||
| Step | What happens |
|
||||
|---|---|
|
||||
| pick up | the module's own long-running process on each node looks at the memory folders at least every ten seconds; a file whose text differs from the version last carried is put in the module's kept `memory` state, one key per home kind and file: the project, the path, the text, its digest, the digest of the version it replaced, the node and when it changed |
|
||||
| carry | every node watches the `memory` state (the whole set at start, then each change) and writes an incoming version where its own file is still the version it replaced, or absent; staged and renamed in, never through a symbolic link, the replaced version kept in `memory-copies/` in the module's state directory for 30 days |
|
||||
| conflict | a file changed on two nodes since the version last carried becomes one file holding both, the newer first, then a line naming each version's node and time, then the older; that file is carried, so every node ends on it, and the operator is told by a warning through the operator channel's `notify`, cleared on the file's next change without a conflict |
|
||||
| remove | a carried file removed from a memory folder that still exists is put in the state as a removal marker for 30 days; a node still holding the removed version moves it into `memory-copies/`, a node that changed it keeps and carries it again; a missing folder or an absent key removes nothing |
|
||||
| first carrying | a node with no record of what it carried takes an equal file as agreed and a different one as a conflict, so every version learned before is kept |
|
||||
| list | `claude_code_memory`: what this node carries, each file's fingerprint and last version, what was not carried and why, open conflicts and copies; never the text |
|
||||
|
||||
**What is not carried**: a symbolic link, a file over 128 KiB, and a file holding a key block, a signed
|
||||
token, a known token prefix or a password in an address. Each is listed with why.
|
||||
|
||||
**Homes of one kind only.** Memory moves among the homes where the agent runs as the operator's account,
|
||||
and separately among the homes of an agent account of its own (§9); never from one kind to the other, so a
|
||||
confined agent's text never reaches a session run as the operator (issue 340).
|
||||
|
||||
**No tool writes memory.** The `memory` state and the folders are written only by the module's own process,
|
||||
from what the module on another node carried; issue 340's boundary holds. A value put straight into the
|
||||
state by a holder of a tool runner's credential is not yet told apart (issue 638).
|
||||
|
||||
## How it is checked
|
||||
|
||||
| Check | Defends |
|
||||
@@ -547,6 +575,8 @@ asker's subjects and its own records there.
|
||||
| live: a proposal for every node shows on the operator's phone with Approve and Decline; after Approve a new session on another node reads it | ADR 0274 |
|
||||
| live: a skill registered at the mesh scope is offered as `nox-mesh:<name>` in a new session on each machine | ADR 0216 |
|
||||
| the module's test: the "instead of" table is generated from the controller's answers, ordered and cut; the guard refuses ssh to a mesh machine by name, domain, internal name, address, alias and jump, and the local work-arounds for a mesh name, naming the tool — and lets git to the forge, ssh beyond the mesh and a command merely mentioning ssh through; the override is honoured only from the session's start and only when recorded | ADR 0245 |
|
||||
| the module's tests over temporary homes and a state in memory, never the live folders: a memory file written on one node appears unchanged on another; only `projects/*/memory/` is carried, and a symbolic link, an oversized file and a key block are not; homes of the two kinds never take each other's memory; a file changed on both ends as one file holding both, newer first, with a warning; a removal moves the other node's unchanged file into `memory-copies/` and a changed one is kept and carried again; a missing folder and an absent key remove nothing; a first carrying loses nothing | ADR 0343 |
|
||||
| live: a file written in a project's memory folder on the laptop is listed by `claude_code_memory` on the workstation with the same fingerprint within a minute; a file changed on both is kept twice in one file, with a warning on the operator's channel | ADR 0343 |
|
||||
| the mesh MCP server's provision resolves by co-location; a machine without the mesh MCP server refuses the module by name | ADR 0027, ADR 0152 |
|
||||
| the account the agent runs as: the facts fall back to the operator account; an agent account of its own is judged unable to become root, or raises `agent-can-become-root`; its files are written only through the helper; a session as another account is refused with no override | ADR 0266 |
|
||||
| a new session on the assigned workstation lists the mesh MCP server's five tools under `mesh` (ADR 0195) and answers "which node am I" from the instruction file | the exit of the build |
|
||||
|
||||
Reference in New Issue
Block a user