mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
Add a local-first, cross-harness memory vault with CLI and MCP surfaces, bounded search and storage, harness-scoped visibility, setup guidance, and comprehensive tests.
223 lines
9.1 KiB
Markdown
223 lines
9.1 KiB
Markdown
# 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
|
|
<repo>/.ecc/memory/
|
|
├── project/
|
|
│ ├── contexts/
|
|
│ ├── decisions/
|
|
│ ├── facts/
|
|
│ ├── handoffs/
|
|
│ ├── lessons/
|
|
│ ├── notes/
|
|
│ ├── preferences/
|
|
│ └── runbooks/
|
|
└── team/
|
|
└── <same kind directories>
|
|
|
|
~/.ecc/memory/
|
|
└── <same kind directories>
|
|
```
|
|
|
|
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 <text> [--body-file <path>|--stdin] [metadata flags]
|
|
ecc memory handoff --from <harness> --target <harness> --title <text> ...
|
|
ecc memory search <query> [--scope ...] [--target-harness ...] [--json]
|
|
ecc memory read <id> [--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.
|