Mesh memory¶
Status: first CLI slice shipped; MCP, hook injection, edit/rm commands, and migration helpers remain design proposal. Builds on the orchestrator memory layer described in orchestrator.md and the persona/SOUL surface described in personas.md.
Repowire today only ships one curated memory surface: the orchestrator
workspace at ~/.repowire/orchestrator/memory/. Every other peer either keeps
its memory inside a host-agent specific location (Claude Code's per-project
memory/, Codex notes, vault folders) or writes nothing durable at all.
Mesh memory generalises the orchestrator's curated-procedure pattern to the whole mesh: project peers, personas, users, and global mesh conventions all get the same file-based memory shape, addressable from the same CLI/MCP surfaces, without changing the orchestrator's existing role.
Goals¶
- One memory shape across the mesh — same frontmatter, same
MEMORY.mdindex pattern, same write/read semantics — so an operator can move between orchestrator, project peers, and personas without re-learning a system. - Cleanly namespaced storage that survives backups, sync, and re-installs.
- Deliberate writes only. Hooks and prompts may suggest a memory write but must never silently mutate memory state.
- A small CLI/MCP read+write surface that any peer can use without depending on the host agent's own memory implementation. Today this is CLI-only.
- Composable with the existing orchestrator memory and persona SOUL layers rather than replacing them.
Non-goals¶
- Replacing host-agent memory systems (Claude Code's
MEMORY.md, Cursor notepads, Codex memory) — those remain authoritative for their own runtime. - Becoming a long-term recall or full session history. Detailed recall belongs to session-native storage and SQLite-backed search.
- Implementing a vector store, embeddings, or any retrieval beyond grep/keyword. Mesh memory is curated text, not a knowledge base.
- Auto-writing or summarising on the agent's behalf. See "No auto-writes" below.
Namespace¶
All mesh memory lives under ~/.repowire/memory/:
~/.repowire/memory/
├── MEMORY.md # top-level pointer index (optional)
├── global/ # cross-mesh conventions and operator rules
│ ├── MEMORY.md
│ └── <slug>.md
├── user/ # the operator behind the mesh
│ ├── MEMORY.md
│ └── <slug>.md
├── projects/
│ └── <project-name>/
│ ├── MEMORY.md
│ └── <slug>.md
├── personas/
│ └── <persona-name>/
│ ├── MEMORY.md
│ └── <slug>.md
└── orchestrator/ # symlink to ~/.repowire/orchestrator/memory/
Each scope has the same internal shape: a MEMORY.md index plus one file per
memory, identical frontmatter to the orchestrator template
(repowire/orchestrator/template/memory/MEMORY.md).
Scope semantics¶
| Scope | Owner | Read by | Example |
|---|---|---|---|
global/ |
mesh operator | every peer in the mesh | "Default to short commit messages." |
user/ |
mesh operator | every peer, but framed as preferences about the human | "Operator is a senior backend engineer, prefers Go analogies." |
projects/<name>/ |
project peers on <name> |
peers whose project matches |
"Auth middleware rewrite is compliance-driven, not tech debt." |
personas/<name>/ |
a persona | sessions running that persona | "Anya answers concisely, refuses to predict timelines." |
orchestrator/ |
the orchestrator role | the active orchestrator session | unchanged from today |
The orchestrator entry is a symlink, not a moved directory, so the existing
workspace at ~/.repowire/orchestrator/memory/ keeps working untouched. This
preserves the orchestrator's "workspace owns its own memory" property while
making it discoverable from ~/.repowire/memory/orchestrator.
Project resolution¶
A peer's project name comes from its existing registry entry (project column
in list_peers). The CLI/MCP resolves the scope path as
~/.repowire/memory/projects/<peer.project>/. If the directory does not exist,
read returns empty and write creates it on first use.
Persona resolution¶
A persona's name comes from the active persona marker described in
personas.md. The CLI/MCP resolves it from
~/.repowire/orchestrator/personas/ACTIVE_PERSONA, then falls back to the
--persona flag. When no persona is active, persona-scoped reads return empty
and writes require an explicit --persona <name>.
Approval and write path¶
Mesh memory is a deliberate-action store. The contract is:
- No hook, scheduler, or background task writes memory files.
- Stop hooks, prompts, and session events MAY emit a proposed memory
notification (e.g. "consider remembering: X") but the agent or operator must
invoke an explicit
repowire memory write(or future MCPmemory_write) call to persist it. - Memory writes always go through the public CLI/MCP surface, never via direct daemon side effects. This keeps an audit trail and a single chokepoint.
- The daemon does not own memory state. Storage is plain filesystem under
~/.repowire/memory/; the daemon only mediates reads/writes for peers that prefer the HTTP/MCP route.
This is the same rule the orchestrator memory layer already implies, lifted to the whole mesh.
The approval lifecycle is intentionally simple in the first product slice:
- Propose. A peer may present a proposed memory with target scope, slug, type, description, body, and provenance. Provenance should point at the ask, session, job, or human instruction that made the memory useful.
- Diff. Before any write, the operator sees the exact file-level change. New memories show the full file. Appends and overwrites show a unified diff against the existing file. Silent "learned" facts are not valid input.
- Decide. The operator can approve, reject, or edit. Rejecting closes the proposal without changing disk. Editing creates a new diff and requires the same explicit approval.
- Write. Only after approval does Repowire call
repowire memory write(or the future MCP equivalent). Existing files still require--appendor--force, so approval does not bypass collision protection. - Record. The write refreshes the scope
MEMORY.mdindex. A future approval broker or jobs integration may also emit timeline/job events, but those records are evidence of a decision, not an alternate memory store.
The first shipped CLI slice covers the explicit write operation. It does not
yet provide a proposal queue, approval UI, or MCP approval tool. Until those
surfaces exist, the safe manual path is: draft the target Markdown, show the
diff in chat or terminal, get explicit user approval, then run
repowire memory write.
Proposal shape¶
Future proposal surfaces should preserve these fields:
scope: user | global | projects/<name> | personas/<name> | orchestrator
slug: short-kebab-case
type: feedback | project | user | reference | global
description: one-line summary
body: markdown body to write
provenance:
source: ask | session | job | human | import
ref: stable id or human-readable pointer
The proposal object is not itself memory. It is a pending decision. Dropping or rejecting it must leave all memory files unchanged.
Surfaces¶
CLI¶
repowire memory list [--scope <scope>] [--project <name>] [--persona <name>]
repowire memory show <slug> [--scope ...]
repowire memory search <query> [--scope ...] [--all]
repowire memory write <slug> --body "..." [--scope ...] [--type ...] [--description ...]
repowire memory write <slug> --body "..." [--scope ...] --append
repowire memory edit <slug> [--scope ...] # proposed
repowire memory rm <slug> [--scope ...] # proposed
repowire memory path [--scope ...] # print resolved directory
Scope defaults:
- Inside an orchestrator workspace →
orchestrator. - Inside a registered project peer's CWD →
projects/<auto-detected-name>. - Otherwise →
user.
The first shipped slice writes one Markdown file at a time, protects existing
files unless --force or --append is passed, and refreshes the scope
MEMORY.md index. It does not implement MCP tools yet.
--all on search walks every scope and prefixes results with their scope path.
MCP¶
Proposed tools on the existing MCP server:
memory_list(scope?, project?, persona?) -> TSV(slug, type, description, updated_at)memory_read(slug, scope?, project?, persona?) -> str— returns the file bodymemory_write(slug, body, scope?, project?, persona?, type?, description?, append=False) -> strmemory_search(query, scope?, all=False) -> TSV(scope, slug, snippet)
All tools default scope from the caller's identity (same rules as the CLI). The
MCP layer never auto-summarises or auto-writes; memory_write is the only path
that mutates state.
Hooks (read-only)¶
The existing SessionStart context injection gains a small, optional
[Repowire Memory] block summarising which scopes have entries and how to read
them, mirroring how persona context is injected today. The block is purely
informational; it never embeds memory bodies inline to avoid context bloat.
Format¶
Identical to the orchestrator memory template:
---
name: <short-kebab-case-slug>
description: <one-line summary>
metadata:
type: feedback | project | user | reference | global
---
# <short title>
**Why:** <the incident or strong preference behind the rule>
**How to apply:** <when/where this kicks in>
MEMORY.md per scope is a one-line-per-entry index, capped at the same
~150-line soft budget the orchestrator template uses.
Relation to existing layers¶
- Orchestrator memory keeps its workspace path and template. The new
~/.repowire/memory/orchestratoris just a symlink for discovery. - User memory stores stable preferences about the human operator that should influence the whole mesh. It should stay compact and behavioral, not become a profile dump or private diary.
- Global memory stores cross-mesh operating rules that are not about the user personally, such as preferred commit style or escalation norms.
- Project memory stores durable project facts or decisions for one repo or workspace. Peers in other projects do not inherit it unless an orchestrator or human includes it in a brief.
- Persona SOUL.md stays as identity context, not memory. Personas may also
accumulate memory in
personas/<name>/, which is curated lessons distinct from the persona's voice/identity. Persona memory can tune how a persona works; it must not override user instructions, approval requirements, or permissions. - Host-agent memory (Claude Code's per-project
memory/, etc.) is unaffected. Operators who want a single source of truth can symlink their agent's memory dir into~/.repowire/memory/projects/<name>/themselves; the product does not enforce this. - Obsidian and vault notes remain the right place for rich personal knowledge, source material, and long-form notes. Mesh memory may link or summarize a vault-backed decision, but approved mesh memory should contain only the compact rule needed by future agents.
- Jobs may propose memory as part of completion or review, especially when a job produces a durable preference or project lesson. Jobs must not write memory directly; they feed the same proposal, diff, approval, and explicit write path.
- Session-native search (roadmap) covers detailed recall. Mesh memory remains the curated layer.
Open questions¶
- Should
global/anduser/collapse into one scope? Keeping them separate matches the auto-memory taxonomy and lets us tag user-profile facts distinctly from cross-mesh procedural rules. - Sync. The orchestrator workspace is single-machine today. If/when we add cross-machine sync, mesh memory inherits the same questions; out of scope for this design.
- Conflict resolution between scopes when a peer reads from multiple. Initial answer: the CLI lists all matches; the agent decides. No automatic merge.
- Whether
memory_propose(a read-only "I noticed something worth saving" stream) should be its own MCP tool, or whether proposals should live inside a general approval broker. Deferred until a real hook or job needs it.
Implementation phasing¶
First CLI-only filesystem slice has landed. Suggested follow-up beads:
- Diff-first approval helper for proposed writes. It should render the target
scope/path and unified diff, then require explicit approve/reject/edit before
calling
write_memory. - MCP
memory_read/memory_write/memory_list/memory_searchtools wrapping the same filesystem layer. - SessionStart context injection block (read-only summary).
repowire memory edit/repowire memory rmwith user-visible diffs or confirmations.- Migration helper:
repowire memory adoptto symlink the orchestrator dir under~/.repowire/memory/orchestrator.
Each phase is independently shippable; nothing depends on auto-write or daemon state.