# ECC Memory Vault ## Capability An operator can save, inspect, search, and hand off durable context through one human-readable vault that Claude Code, Codex, Hermes, OpenCode, and other harnesses can share. Project and team memories live under `.ecc/memory/`; user memories live under `~/.ecc/memory/`. The same `ecc.memory.v1` documents are available through the `ecc memory` CLI and an opt-in local stdio MCP server, so knowledge transfer does not depend on email, one vendor's transcript format, or one harness's hook support. ## Constraints - Markdown files are the source of truth. SQLite context graphs, embeddings, and hosted systems are indexes or adapters, never the only copy. - A memory is context, not an instruction. Every first-release vault entry is `trust: "unreviewed"` and cannot silently become rules, skills, or policy. - Reviewed project standards still belong in the repository's canonical rules, decision records, runbooks, or other governed documentation. The vault may link to those artifacts; it does not replace them. - The core is local-first, inspectable, and usable without a model, network, database server, or embedding provider. - Writes are create-only. The tool never overwrites an existing memory ID. Supersession is represented by a new document with explicit links. - Known credential shapes and private keys are rejected before a tool writes a file. This scan is a best-effort backstop, not a complete secret classifier. Memory readers do not follow symbolic links. - Search is bounded lexical retrieval in the first release. Optional semantic adapters may rerank results later without changing the document contract. - Harness adapters stay thin. Shared behavior belongs in `scripts/`, `skills/`, and the MCP server rather than separate Claude/Codex/Hermes stores. - Procedural memory remains in rules and instincts, subject to their existing promotion and validation gates. ### Threat boundary The first-release runtime defends against hostile vault documents, stable symlink/path escapes, accidental project-memory commits, cross-harness MCP identity spoofing, known secret shapes, terminal control data, and bounded resource exhaustion. Vault roots must remain writable only by the operator. It is not a security boundary between concurrent processes running as the same OS user: Node.js does not expose the directory-file-descriptor-relative `openat2` guarantees needed to eliminate every parent-directory swap race. Operators who need protection from a malicious local process must use separate OS accounts, containers, or equivalent filesystem isolation. ## Implementation Contract ### Actors - **Operator:** owns the vault, reviews files, commits team memories, and decides when recalled context becomes governed project truth. - **Harness agent:** writes unreviewed facts, notes, lessons, and handoffs; reads active memories targeted to itself or all harnesses. - **ECC CLI:** deterministic local create/read/search/doctor interface. - **ECC Memory MCP:** stdio adapter exposing the same create/read/search/doctor operations. It has no review or promotion tool. - **ECC2 context graph:** optional projection populated from the Markdown directory connector for richer relationship and session views. ### Surfaces ```text /.ecc/memory/ ├── project/ │ ├── contexts/ │ ├── decisions/ │ ├── facts/ │ ├── handoffs/ │ ├── lessons/ │ ├── notes/ │ ├── preferences/ │ └── runbooks/ └── team/ └── ~/.ecc/memory/ └── ``` The project scope is repo-local operator context and receives its own fail-closed `.gitignore`: initialization and writes stop if the protection file exists with unexpected content. The team scope is intended to be inspected by a human before it is committed, but committed vault entries remain unreviewed context. The user scope follows the operator across repos and is recalled only when explicitly requested. `ECC_MEMORY_PROJECT_ROOT` and `ECC_MEMORY_USER_ROOT` may override the two vault locations explicitly. ### Document contract Each memory is a Markdown file with strict JSON-valued YAML frontmatter: ```markdown --- schema: "ecc.memory.v1" id: "mem_20260726_01k123example" title: "Authentication migration handoff" kind: "handoff" scope: "project" trust: "unreviewed" status: "active" source_harness: "codex" target_harnesses: ["claude"] tags: ["auth", "migration"] links: ["mem_20260725_01kolder"] created_at: "2026-07-26T20:00:00.000Z" updated_at: "2026-07-26T20:00:00.000Z" --- The token rotation tests pass. The remaining task is ... ``` Required fields are schema, ID, title, kind, scope, trust, status, source harness, targets, tags, links, and timestamps. IDs, kinds, tags, and harness names use a bounded lowercase slug grammar. Bodies are bounded Markdown text. Backlinks are derived from other documents' `links` fields. ### States and transitions ```text tool save ──> active + unreviewed │ ├── human verifies evidence │ └──> governed rule, decision record, runbook, or doc │ └── new memory links with supersedes relation └──> old item may be marked superseded manually ``` The initial runtime creates active, unreviewed memories only, and normal search recall returns active entries only. A direct ID read may still retrieve a non-active entry for inspection. Human review does not change a vault entry's `trust` field; accepted knowledge is promoted into a governed repository artifact. The runtime exposes no automated promotion transition. This is intentional: a shell-capable agent cannot be treated as an independent human approval boundary. ### Interfaces CLI: ```text ecc memory init [--scope project|team|user] ecc memory save --title [--body-file |--stdin] [metadata flags] ecc memory handoff --from --target --title ... ecc memory search [--scope ...] [--target-harness ...] [--json] ecc memory read [--scope ...] [--json] ecc memory doctor [--json] ``` MCP tools: ```text memory_save memory_search memory_read memory_doctor ``` The CLI searches active `project` and `team` memories by default. `user` recall requires an explicit `--scope user`. Its `--target-harness` option is a caller-selected routing filter, not an authorization boundary. The MCP server requires a lowercase `ECC_MEMORY_HARNESS` identity at launch. That server-side identity supplies `source_harness` for writes and constrains search/read to memories targeted to that harness or `all`; clients cannot override it in tool arguments. MCP access to `user` scope is disabled unless the operator also sets `ECC_MEMORY_ALLOW_USER_SCOPE=1`, after which the client must still request that scope explicitly. MCP writes always produce unreviewed documents. Structured errors omit stack traces and secret values. ### Failure and recovery - Invalid metadata, oversized input, duplicate IDs, suspected secrets, and path escapes fail before writing. - A malformed file is reported by `doctor` and excluded from search; it is never deleted or rewritten automatically. - Duplicate IDs and broken links are reported explicitly. - Symlinks are skipped and reported. - Missing vault directories are equivalent to an empty vault. - A failed MCP request returns a bounded error and leaves existing files unchanged. ### Observability The first release reports operation results only. Write acknowledgements omit the raw body and use a scope-relative vault path; only an explicit read returns the full body. A later event-sourced ECC2 projection may record content hashes and operation metadata, but it must not log raw memory bodies or credentials. ## Non-goals - Building a vector database, hosted sync service, email transport, or new agent framework. - Importing raw Claude/Codex/Hermes transcripts automatically. - Treating recalled memory as trusted system instructions. - Auto-promoting memory into skills, rules, instincts, or policy. - Replacing ECC2 sessions, the context graph, GitHub/Linear work items, or governed project documentation. - Solving cross-machine conflict-free replication in the first release. ## Open Questions - Whether the team scope should gain a signed promotion manifest that points to governed artifacts after the ECC2 append-only event substrate lands. - Which semantic adapter should be the first optional reranker, and what offline evaluation must beat lexical search before it becomes recommended. - Whether SessionStart should inject links to governed project references or keep all recall explicitly task-scoped. The first release keeps recall explicit. - How `.context/` worktree handoffs should materialize from vault handoffs once the conductor fork lifecycle is stable. ## Handoff The local file/CLI/MCP slice is implemented behind explicit CLI or MCP activation and covered by core, schema, CLI, protocol, packaging, and cross-harness tests. ECC2 graph sync, automatic session capture, semantic adapters, governed-reference recall, and event-log promotion belong in follow-up lanes after real-world retrieval evaluation.