Architecture¶
The whole system rests on three decisions, in order of how much they define the product:
- Git is the substrate — not a database with git export.
- Concurrency is designed out, not resolved — atomic files + derived indexes.
- The "auto" bridge is the product — capture → curate → commit → propagate.
1. Git is the substrate¶
The brain is a git repository of markdown. No proprietary store is the source of truth — that is the ownership and portability guarantee: your knowledge stays in files you own, not an opaque index or a block database you can't leave.
- One repo per project brain.
acme-brain,internal-brain, etc. A team may also have anorg-brainfor cross-project knowledge. (Monorepo-of-brains is a later option; per-project repos keep permissions and blast radius simple.) - Remote = any git host. GitHub by default (fits our OSS + Projects workflow), but the design must not assume GitHub-only. Self-host / BYO-remote is first-class.
- A local working copy lives on each member's machine (e.g.
~/.commonwealth/<brain>), kept in sync by the daemon (below). The agent reads/writes the local copy; the daemon moves commits to/from the remote. - A derived index (SQLite + embeddings) is built from the markdown for fast
search. It is disposable and
.gitignored — rebuildable from files at any time (basic-memory's model, and the right one).
acme-brain/ # a git repo = one project's brain
├── memory/ # atomic notes (append-heavy, rarely collide)
│ ├── 2026-07-01-auth-choice-a1b2.md
│ └── 2026-06-30-client-billing-quirk-9f3d.md
├── decisions/ # ADR-style, one file per decision
├── work-state/ # current status per workstream
├── people/ # people-threads: one file per person/relationship
├── index/ # DERIVED, gitignored (SQLite + vectors)
├── .commonwealth/ # config: schema version, curation rules, remotes
└── COMMONWEALTH.md # human+agent entry point (the router / TOC)
2. Concurrency: design it out¶
Concurrency is where most shared-memory tools are weakest — mtime-wins overwrites, whole-file rewrites that conflict, or simply being single-player. We don't want to win merge conflicts — we want to not have them.
Three mechanisms, in priority order:
a) Atomic, append-only notes — one fact per file¶
Following the pattern that already works in personal setups (Claude's own
one-fact-per-file memory): each note is a small, self-contained markdown file with a
collision-proof name (<date>-<slug>-<shortid>, shortid = content/uuid hash). Two
teammates writing "at the same time" create two different files. Git merges them as a
union — no conflict, ever. This is the single most important concurrency decision.
b) Derived, never-hand-merged indexes¶
COMMONWEALTH.md, per-folder tables of contents, backlink graphs, roll-ups — all
regenerated from the note files, not hand-edited. So the high-contention "index"
file is never the subject of a manual merge. Two strategies, combined:
- Regenerate on the daemon after every pull (idempotent from the file set).
- Register a git merge driver (
merge=unionvia.gitattributes) for the append- only index files as a backstop, so even a raw merge unions cleanly.
c) A serialization queue for the rare true edit¶
Editing an existing note (a correction, a decision superseded) can still collide. For those:
- Section-scoped edits, not whole-file rewrites. Prefer
append/insert-sectionops over rewriting the file (Letta'smemory_insertis safe;rethinkis lossy). - A lightweight write queue in the daemon serializes commits to the same file: acquire → pull/rebase → apply → push, with retry. This is the "queueing mechanism" from the brief, scoped down to only where it's actually needed (same-file edits), not every write.
- On genuine conflict, never silently overwrite. Write both versions as sibling notes
and file a
conflict:curation task for review.
Net: ~all writes are new atomic files (conflict-free unions); indexes are derived (no manual merges); only same-file edits touch the queue, and even those degrade to a reviewable task rather than data loss.
3. The "auto" bridge — capture → curate → commit → propagate¶
Storage is solved; the unsolved problem (per the research) is the auto pipeline: turning session learnings into shared, curated, propagated knowledge. This is the product. It runs in four stages, wired into Claude Code lifecycle hooks + MCP.
session ──▶ CAPTURE ──▶ CURATE ──▶ COMMIT ──▶ PROPAGATE ──▶ next session
(learnings) (draft (dedupe, (atomic (push + (pull +
notes) verify, file + open PR / inject relevant
gate) queue) review) context)
Capture (Stop / SessionEnd hook + MCP write tools)¶
- The agent proposes candidate memories from the session (decisions made, gotchas
learned, work-state changes). Two paths: explicit MCP
remembercalls during the session, and an end-of-session sweep that drafts notes. - Candidates land in a staging area (
staging/), not straight into canon.
Curate (curation gate, runs on staging + nightly)¶
- Dedupe against existing notes — lexical, plus optional embeddings (
semanticDedup). - Verify where possible — check memory against reality (e.g. a claim about code vs. the
actual code). Mark
verified:/stale:. - Contradiction check — flag notes that conflict with canon; open a review task.
- Relevance gate — score whether a candidate is worth committing/sharing at all (avoid junk accumulation). Low-value → drop; high-value → promote.
Commit (atomic file + queue, per §2)¶
- Promoted notes become atomic files, committed with a structured message.
- Curation-as-review: promotion opens a PR (or a review queue) so a human — or a higher-trust agent — approves before it becomes canon. Junk never auto-lands.
Propagate (SessionStart hook + relevance-gated fetch)¶
- Push on commit (daemon).
- Fetch/merge into every teammate's local copy on session start (daemon
pull). - Relevance-gated injection — the genuinely novel bit: don't dump the whole brain into context; surface the notes relevant to what this teammate is doing right now (current project/files/task). This is the "auto push/fetch where it sees fit" from the brief, made concrete.
Components¶
| Component | Role |
|---|---|
| Brain repo(s) | Git repo(s) of markdown — the substrate & source of truth |
| Sync daemon | Per-machine: pull on session start, commit+push on write, run the write queue, rebuild the index. Long-lived, one per user. |
| MCP server | Exposes search / read / remember / list-workstate / whoami-style tools to Claude Code (and any MCP client). Reads local copy + index. |
| Curation agent | Runs dedupe/verify/contradiction/relevance on staging + nightly (cron), opens review PRs. Can reuse Claude via the Agent SDK. |
| Claude Code plugin | Bundles MCP server config + lifecycle hooks (SessionStart pull+inject, Stop capture) + the brain registry. The auto-provisioning unit (see distribution). |
| Brain registry | Maps a working directory / project → its brain repo(s), so the plugin mounts the right brain automatically. |
Decisions of record¶
The concrete choices behind the above — embeddings (local-first, opt-in), the review-gate default, secret scanning, clone-on-demand access, and more — live as Architecture Decision Records. Cross-brain graduation (project → org brain) is the main open thread; see the roadmap.
See docs/02-data-model.md for the note schema and
docs/03-distribution.md for how the plugin auto-provisions.