Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
d739617
docs: add git-native KB ADR context
CREDO23 Jul 24, 2026
ac2537a
docs: record git-native KB decision
CREDO23 Jul 24, 2026
ebab1f4
docs: cite sources in git-native KB ADR
CREDO23 Jul 24, 2026
30aaa55
docs: add v1 scope and consequences to KB ADR
CREDO23 Jul 24, 2026
88b285a
plans: add git-native KB umbrella positioning
CREDO23 Jul 24, 2026
4a9c374
plans: lock decisions in KB umbrella
CREDO23 Jul 24, 2026
b6961d9
plans: define KB backend phases
CREDO23 Jul 24, 2026
954d310
plans: sequence phases and log decisions
CREDO23 Jul 24, 2026
0215a75
plans: add git-native KB flow diagrams
CREDO23 Jul 24, 2026
b1a14ee
plans: add phase 1 git storage core
CREDO23 Jul 24, 2026
be55b52
plans: add phase 2 git working-tree backend
CREDO23 Jul 24, 2026
e09091f
plans: add phase 3 commit write path
CREDO23 Jul 24, 2026
e25b309
plans: add phase 4 derived index
CREDO23 Jul 24, 2026
7c7504d
plans: add phase 5 migration
CREDO23 Jul 24, 2026
dc76423
plans: add phase 6 zero projection
CREDO23 Jul 24, 2026
6280a9f
plans: add phase 0 shared contract
CREDO23 Jul 24, 2026
0feccfe
plans: pin redis lock and repo model in phase 1
CREDO23 Jul 24, 2026
ea5fbec
plans: fix phase 2 read contract to chunk-rendered
CREDO23 Jul 24, 2026
ea4d03b
plans: pin phase 3 write path to commit_staged_filesystem_state
CREDO23 Jul 24, 2026
2e43356
plans: correct phase 4 blob-sha and content_hash reality
CREDO23 Jul 24, 2026
e1a2289
plans: resolve phase 5-6 layout and dual-channel projection
CREDO23 Jul 24, 2026
85ba1f2
plans: fold phase 0 into umbrella and resolve open items
CREDO23 Jul 24, 2026
818752e
deps: add dulwich for git-native storage
CREDO23 Jul 27, 2026
5a461a4
config: add knowledge store flag and root
CREDO23 Jul 27, 2026
5fcc675
knowledge_store: add module settings
CREDO23 Jul 27, 2026
16a1dfd
knowledge_store: add workspace store locations
CREDO23 Jul 27, 2026
5e92557
knowledge_store: add versioned content store contract
CREDO23 Jul 27, 2026
300e782
knowledge_store: add dulwich git engine
CREDO23 Jul 27, 2026
543d931
knowledge_store: add per-workspace redis write lock
CREDO23 Jul 27, 2026
92d8e65
knowledge_store: add revise facade with intent verbs
CREDO23 Jul 27, 2026
2769e80
knowledge_store: expose public surface
CREDO23 Jul 27, 2026
15cca30
knowledge_store: test engine, lock, and facade
CREDO23 Jul 27, 2026
e6f300d
plans: mark phase 1 implemented
CREDO23 Jul 27, 2026
20960f9
plans: realign umbrella to knowledge_store naming
CREDO23 Jul 27, 2026
e6b76cc
plans: correct shared contract repo path and lock key
CREDO23 Jul 27, 2026
836251d
plans: repoint phase 2-3 to knowledge store api
CREDO23 Jul 27, 2026
fe5bb97
plans: rename kb_git to knowledge_store in phase 4-5
CREDO23 Jul 27, 2026
bfd7ae0
knowledge_store: split revision draft into its own file
CREDO23 Jul 27, 2026
502da73
knowledge_store: rename service and locations modules
CREDO23 Jul 27, 2026
585aeb0
knowledge_store: rename head accessor to current_revision
CREDO23 Jul 27, 2026
875da5e
knowledge_store: trim lock and config docstrings
CREDO23 Jul 27, 2026
0789a3c
knowledge_store: update tests and plans for renamed surface
CREDO23 Jul 27, 2026
599ef32
plans: adopt ports & adapters shape (adr 0002)
CREDO23 Jul 28, 2026
1ccd7d3
knowledge_store: adopt transaction vocabulary
CREDO23 Jul 28, 2026
33ecc23
knowledge_store: add list_changes and list_paths for the sync consumer
CREDO23 Jul 28, 2026
c2a5aee
plan: lock per-turn worktree isolation model
CREDO23 Jul 28, 2026
44c20ec
plan: lock raw-read citation model (path@revision + evidence lines)
CREDO23 Jul 28, 2026
a207f84
knowledge_store: engine port with working copies and self-bootstrap
CREDO23 Jul 28, 2026
2f5e6e6
knowledge_store: git engine implements working-copy lifecycle
CREDO23 Jul 28, 2026
5f46da0
knowledge_store: working-copies location in the store layout
CREDO23 Jul 28, 2026
14d40d2
knowledge_store: facade working-copy capabilities, engine vocabulary
CREDO23 Jul 28, 2026
94b8c19
knowledge_store: export WorkingCopy
CREDO23 Jul 28, 2026
cc12067
middleware: GitTreeBackend serves the turn's private working copy
CREDO23 Jul 28, 2026
6c9224a
middleware: mkdir capability on local folder backends
CREDO23 Jul 28, 2026
156b5bd
middleware: resolve GitTreeBackend when the knowledge store is enabled
CREDO23 Jul 28, 2026
a5c9bc5
middleware: route git-tree ops directly, bypassing state staging
CREDO23 Jul 28, 2026
0b9b98d
tests: unit suites for engine, transaction, and git-tree adapter
CREDO23 Jul 28, 2026
1c69ce3
tests: integration suites for write lock and facade against real redis
CREDO23 Jul 28, 2026
af40ba2
plans: system diagrams, engine vocabulary, and phase 2 status
CREDO23 Jul 28, 2026
d399870
plan: lock phase 3 commit path decisions
CREDO23 Jul 28, 2026
aa27c2f
plan: align phase 4 with transaction vocabulary and cut-time deletion
CREDO23 Jul 28, 2026
cca2b94
knowledge_store: committer parameter on record and transaction
CREDO23 Jul 28, 2026
7d9e475
knowledge_store: authorship conventions for recorded revisions
CREDO23 Jul 28, 2026
0c86303
middleware: single thread-to-working-copy-id convention
CREDO23 Jul 28, 2026
e4c0bd0
middleware: model-generated commit subjects with deterministic fallback
CREDO23 Jul 28, 2026
64cafcc
middleware: end-of-turn commit body records one revision per turn
CREDO23 Jul 28, 2026
1dac877
middleware: knowledge store persistence hook behind the flag
CREDO23 Jul 28, 2026
9f6cf82
middleware: wire knowledge store persistence into the main-agent stack
CREDO23 Jul 28, 2026
7050a6e
streaming: git-native safety-net commit on client disconnect
CREDO23 Jul 28, 2026
6db282a
documents: editor saves and upload markdown record store revisions
CREDO23 Jul 28, 2026
058c339
knowledge_store: daily janitor prunes abandoned working copies
CREDO23 Jul 28, 2026
4c70728
plan: sync phases 2-4 status with shipped phase 3
CREDO23 Jul 29, 2026
b5ce9ae
documents: generalize revision recorder to batched markdown writes
CREDO23 Jul 29, 2026
fb11c10
indexing: record connector syncs at the prepare_for_indexing choke point
CREDO23 Jul 29, 2026
7cb8201
plan: phase 3 complete - connector sync shipped via pipeline choke point
CREDO23 Jul 29, 2026
dda2560
plan: migration adopts the index, spans converge lazily (C2/C5/C7)
CREDO23 Jul 29, 2026
7b90099
knowledge_store: batch index staging, per-file adds were O(n^2)
CREDO23 Jul 29, 2026
ed15cf4
knowledge_store: expired write-lock hold fails loudly
CREDO23 Jul 29, 2026
3ad609a
migration: phase 5 seeder - convergent seed, byte parity, contained f…
CREDO23 Jul 29, 2026
3b9f00f
plan: C1 layout keeps the documents/ root, as shipped
CREDO23 Jul 29, 2026
3cdf93f
plan: documents/ root is deliberate - reserves top level for sibling …
CREDO23 Jul 29, 2026
05a4846
migration: fleet runner - dry-run-first, resumable, JSONL reports
CREDO23 Jul 29, 2026
7f33015
path_resolver: add to_store_path function to convert virtual paths to…
AnishSarkar22 Jul 29, 2026
7c100c5
refactor: use to_store_path function for storing document revisions
AnishSarkar22 Jul 29, 2026
ffb2bf7
test: add unit tests for to_store_path function in path_resolver
AnishSarkar22 Jul 29, 2026
58d5d59
knowledge_store: per-workspace flip flag behind the global kill switch
CREDO23 Jul 29, 2026
d8d88d0
agents: guard sites take the per-workspace verdict, resolved once per…
CREDO23 Jul 29, 2026
4bee824
migration: fleet runner flips workspaces, only ever on passing parity
CREDO23 Jul 29, 2026
d4a55a4
test: update document store path in integration tests for consistency
AnishSarkar22 Jul 29, 2026
3614092
refactor: rename _ensure_folder_hierarchy to ensure_folder_hierarchy …
AnishSarkar22 Jul 29, 2026
89d0234
refactor: consolidate indexing functions and enhance document identit…
AnishSarkar22 Jul 29, 2026
843cc60
refactor: update path resolution for flagged workspaces and unify doc…
AnishSarkar22 Jul 29, 2026
05f41da
feat(db): add derived-index columns for drift marker and chunk spans
AnishSarkar22 Jul 29, 2026
e83f68c
feat(indexing_pipeline): thread chunk line spans through the cache
AnishSarkar22 Jul 29, 2026
fb67769
test(indexing_pipeline): pin chunk line spans and the note identity seam
AnishSarkar22 Jul 29, 2026
f4a519b
feat(knowledge_store): converge the postgres index from a store revision
AnishSarkar22 Jul 29, 2026
501775e
test(knowledge_store): cover convergence, rebuild determinism, and pa…
AnishSarkar22 Jul 29, 2026
d89d2e6
feat(tasks): index store revisions and sweep drifted workspaces
AnishSarkar22 Jul 29, 2026
6d19049
feat(documents): enqueue indexing from both store writers
AnishSarkar22 Jul 29, 2026
3f541d7
fix(documents): stop legacy writers from fighting the store indexer
AnishSarkar22 Jul 29, 2026
80cb76a
docs(plans): record phase 4 as built
AnishSarkar22 Jul 29, 2026
487aac0
Merge PR #1647: derive the postgres chunk index from git
CREDO23 Jul 30, 2026
9fecf54
docs(plans): sync phase statuses with what shipped
CREDO23 Jul 30, 2026
28a5eaf
test(tasks): cover the drift sweep's candidates and queue routing
CREDO23 Jul 30, 2026
884b05a
test(knowledge_store): pin enqueue_index's swallow and its happy path
CREDO23 Jul 30, 2026
49f7c89
test(tasks): cover the drift monitor's verdict and per-workspace isol…
CREDO23 Jul 30, 2026
58126c5
refactor(knowledge_store): group the derived index into its own package
CREDO23 Jul 30, 2026
56fdda6
refactor(knowledge_store): name the two index entry points for their …
CREDO23 Jul 30, 2026
baca014
feat(knowledge_store): repair Postgres-side drift instead of alarming…
CREDO23 Jul 30, 2026
f04cfb3
docs(celery): the drift schedule repairs now, not just alarms
CREDO23 Jul 30, 2026
a2025f8
fix(agents): stop read_file promising a citation envelope two modes n…
CREDO23 Jul 30, 2026
23d0b1d
Cover the editor and sync recorders, not just the batch primitive
CREDO23 Jul 30, 2026
fa2d85e
Pin the receipt a turn emits for every kind of change, not just adds
CREDO23 Jul 30, 2026
fc9c7b4
Assert restore survives the global flag for an unflipped workspace
CREDO23 Jul 30, 2026
da0e5c6
Stamp the index revision when flipping, so the seed's saving survives
CREDO23 Jul 30, 2026
a336d37
Document the knowledge-store env, which nothing advertised
CREDO23 Jul 30, 2026
f88b80a
Share one working copy per turn, so delegated writes aren't dropped
CREDO23 Jul 30, 2026
5a39276
Stop write_file and edit_file staging what the git backend already wrote
CREDO23 Jul 30, 2026
521220c
Record the two defects the canary's first turn exposed
CREDO23 Jul 30, 2026
8c27559
Cut the narration out of the comments the last two commits added
CREDO23 Jul 30, 2026
d8468bf
Bound the commit subject's generation, so a stalled model can't hold …
CREDO23 Jul 30, 2026
d263fc7
Read a document's path off the row, rather than guess it from the title
CREDO23 Jul 30, 2026
1fe8e14
Open a redis client per lock, so a second celery task can still index
CREDO23 Jul 30, 2026
9d775bb
Move a document's file only when its title was chosen, not inferred
CREDO23 Jul 30, 2026
f66448b
Keep a rename from deleting the row its own upsert just wrote
CREDO23 Jul 30, 2026
6739d27
Leave the working copy alone while a turn waits for approval
CREDO23 Jul 30, 2026
6ebd7ca
Let a document's row follow its file when the file moves
CREDO23 Jul 30, 2026
10b8d4c
docs(plans): add the production seed and flip runbook
CREDO23 Jul 30, 2026
11ae903
Merge remote-tracking branch 'upstream/main' into kb_git_mvp
CREDO23 Jul 30, 2026
586bca8
style: apply ruff format to four knowledge store files
CREDO23 Jul 30, 2026
bdabc2e
docs(plans): record the measured test baseline in the runbook
CREDO23 Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions docs/adr/0001-git-native-knowledge-base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# ADR 0001: Git-native Knowledge Base (Git as source of truth, Postgres as derived index)

- **Status:** Proposed (brainstorm outcome — for team review)
- **Date:** 2026-07-24
- **Origin:** Rohan Verma's meeting proposal to pivot from the custom-built KB "file system" to a Git-based system due to persistent maintenance issues; Thierry Bakera to investigate.

---

## Context

### What we have today

SurfSense does **not** actually have a file system. It has a **virtual filesystem façade mapped onto Postgres rows**, used by the chat agent. The moving parts:

- Virtual `/documents/` namespace computed from DB rows — `surfsense_backend/app/agents/chat/runtime/path_resolver.py`
- Read-side backend faking `ls`/`read`/`glob`/`grep` over Postgres — `.../filesystem/backends/kb_postgres.py`
- Write-side "commit at end of turn" layer — `.../main_agent/middleware/kb_persistence/middleware.py`
- **Three separate hand-rolled versioning/audit systems:**
- `DocumentVersion` (user history) — `app/utils/document_versioning.py`
- `DocumentRevision` / `FolderRevision` (agent revert snapshots) — `app/services/revert_service.py`
- `AgentActionLog` (tool-call audit)
- Supporting machinery: fractional indexing for ordering, move tracking, `content_hash` change detection, chunk reconciliation.

### The problem

The team has been hand-implementing — on top of a relational DB never designed for it — the exact primitives Git provides natively. That re-implementation is the source of the "persistent maintenance issues."

| Hand-rolled today | Git provides natively |
|---|---|
| `path_resolver` + folder tree | tree objects |
| end-of-turn staged commit | atomic commits |
| `DocumentVersion` snapshots | commit history |
| `DocumentRevision` + `revert_service` | `git revert` / `reset` |
| `AgentActionLog` | commit log / `blame` |
| `content_hash` dedup | content-addressed blobs (SHA) |
| fractional indexing / move tracking | tree diff / rename detection |

### Search stack (unchanged by this ADR)

Hybrid chunk search: pgvector (HNSW) + Postgres FTS + RRF, optional reranking. Chunking via Chonkie; incremental via `chunk_reconciler.py`. See `.../shared/retrieval/hybrid_search.py`.

---

## Decision

**Adopt Git as the single source of truth for all indexed KB content. Postgres becomes a derived, rebuildable index holding only chunks + embeddings.**

### Core model

```
agent notes ─┐
editor saves ─┤
uploads ──────┤→ Git commit (source of truth) → indexer → Postgres (chunks + embeddings)
Notion ───────┤
Drive ────────┘
(indexable connectors only)

Slack / Gmail (live connectors) ──→ queried at chat time, bypass storage entirely
```

- **Git = truth** for everything that gets stored/indexed (agent/editor notes, uploads, and **indexable** connectors like Notion, Drive, Obsidian — the `is_indexable` connectors).
- **Postgres = derived index only** (chunks + embeddings). It is a **cache**: it can be wiped and rebuilt from Git at any time via a single `reindex(workspace)` function.
- **Live connectors (Slack, Gmail)** are never stored or indexed — they are queried live at chat time and are entirely out of scope for this design.
- **Binary blobs** (original PDF/DOCX) stay in the existing local/Azure blob store (or Git-LFS later); Git holds the extracted markdown, not raw binaries.

### What changes for the agent

The agent's **tools are unchanged** (`ls`, `read`, `write`, `edit`, `mv`, `rm`). Only the backend behind them changes:

| Agent action | Backed by |
|---|---|
| File ops (`ls`/`read`/`write`/`edit`/`mv`/`rm`) | **Git** working tree (real files) |
| Semantic search | **Postgres** (derived chunk/embedding index) |

- **Before:** `KBPostgresBackend` fakes files over Postgres rows.
- **After:** a Git-working-tree backend operates on **real files**. `path_resolver` largely disappears (paths are real).
- **Write flow:** agent edits → git working tree → **one commit at end of turn** (replaces the `kb_persistence` commit-to-Postgres step) → indexer refreshes Postgres chunks.

### What we delete

- The virtual-FS façade (`path_resolver`, `kb_postgres` staging).
- `DocumentVersion`, `DocumentRevision` / `FolderRevision`, `revert_service` → replaced by git history + `git revert`.
- Fractional indexing / bespoke move tracking → git tree operations.

This pivot is **mostly deletion**, which is the point.

---

## We are borrowing from authoritative sources, not inventing

Every decision traces to a proven, battle-tested reference. The only SurfSense-specific work is the *adaptation glue*.

| Decision | Borrowed from |
|---|---|
| Git = truth, Postgres = rebuildable cache | **Fossil SCM** — canonical artifacts + SQL tables as a pure cache, recomputed via `fossil rebuild` (production since 2007). https://fossil-scm.org/home/doc/trunk/www/theory1.wiki |
| Content in git, metadata/index in a DB | **Gollum** (GitHub/GitLab wikis): "storage abstraction layer... only some data in the DB". https://github.com/gollum/gollum · https://docs.gitlab.com/17.5/development/wikis/ |
| Silent commit-per-save, hide git from users | **kherad**. https://github.com/mohammadmaso/kherad |
| Embeddings keyed by blob SHA, incremental (not rebuild) | **Coregit LLM Wiki** (https://coregit.dev/blog/llm-wiki-launch) + vector-index-as-cache best practices (LangChain RecordManager, LlamaIndex docstore). Cache key = `(model_version, content_hash)`. |
| Python git engine | **dulwich** — pure Python, deploy-friendly, real wire protocol. https://github.com/jelmer/dulwich |
| "Don't put a firehose in git" / git limits | "Git is not a database" critiques — degrades past ~500k–1M files, no query engine, no concurrency control. |
| Future content model (`raw/`+`wiki/`, lint, contradiction-flagging) | **Karpathy's LLM Wiki**. https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f |
| Future real-time collab | **Yjs** (Notion, Linear). https://github.com/yjs/yjs |
| Future fact-level temporal memory | **Zep / Graphiti** (bi-temporal, "invalidate don't delete"). https://arxiv.org/html/2501.13956 |

### The counter-example we explicitly reject

**Wiki.js** git module = DB is truth, git is a two-way mirror. This creates two sources of truth and a reconciliation cursor; it has a known class of silent-sync bugs (see requarks/wiki discussion #7860). We use **one-way derivation** (git → Postgres), never two-way sync.

---

## Scope: v1 (keep it simple)

Ship the smallest thing that removes the maintenance pain:

1. **Git repo per workspace** (dulwich) holding indexed content as markdown.
2. **One commit per agent turn / editor save.** No branches, no merge, no review workflow.
3. **Delete** the three versioning systems; history/undo = git log + `git revert`.
4. **pgvector stays**, rebuilt from git, keyed by blob SHA (point existing `chunk_reconciler` at blob SHA so unchanged files skip re-embedding).
5. **Per-workspace lock/queue** around commits (git is single-writer; this is a data-integrity boundary, not a feature — non-negotiable).
6. **One `reindex(workspace)` function** that wipes and rebuilds Postgres chunks from the git repo (the Fossil `rebuild` discipline — makes the system safe to ship early).

### Deferred (v2+, explicitly out of v1)

- Connect-your-own-remote (GitHub/GitLab) — free later because the repo is real git.
- CRDT / Yjs real-time collaboration.
- Review / merge workflows (kherad's reviewer layer).
- Graphiti / bi-temporal fact graph.
- Karpathy `raw/` + `wiki/` content model, contradiction-flagging, lint.

---

## Consequences

### Positive

- Large net **deletion** of bespoke code (the maintenance win).
- Storage and search **decouple** → the team can improve semantic search independently (Rohan's stated goal).
- Postgres becomes disposable/rebuildable → simpler recovery, fewer consistency bugs.
- Unlocks future "user owns their KB as a real git repo" differentiator.

### Negative / risks

- **Concurrency:** git is single-writer per repo → requires the per-workspace lock (mitigated in v1).
- **Repo hygiene:** many small commits → periodic `git gc`/repack (operational, manageable).
- **Migration:** existing Postgres KBs must be exported into git repos once, preserving `unique_identifier_hash` mapping.
- **Real-time UI (Zero):** currently driven by Postgres logical replication; still needs a git → Postgres projection to keep the web client live. This is *new* code that partially offsets deletions.

---

## Open questions (for team discussion)

1. **Zero / real-time UI:** confirm the git → Postgres projection path and whether Zero stays as-is.
2. **Binaries:** keep blob store vs. adopt Git-LFS.
3. **Migration cutover:** big-bang vs. per-workspace feature flag (recommend feature-flag rollout).
4. **Merge UX later:** CRDT (Yjs) vs. review-gate (kherad) when multi-writer becomes a requirement.

---

## Appendix: key file index (current implementation)

| Topic | Path |
|---|---|
| Virtual path resolver | `surfsense_backend/app/agents/chat/runtime/path_resolver.py` |
| Virtual FS read backend | `.../filesystem/backends/kb_postgres.py` |
| Virtual FS write commit | `.../main_agent/middleware/kb_persistence/middleware.py` |
| Hybrid search | `.../shared/retrieval/hybrid_search.py` |
| Chunk reconciliation | `surfsense_backend/app/indexing_pipeline/chunk_reconciler.py` |
| User version history | `surfsense_backend/app/utils/document_versioning.py` |
| Agent revert | `surfsense_backend/app/services/revert_service.py` |
| ORM models | `surfsense_backend/app/db.py` |
64 changes: 64 additions & 0 deletions docs/adr/0002-knowledge-core-ports-and-adapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ADR 0002: Knowledge core as Ports & Adapters (deepagents is an adapter, not the core)

- **Status:** Proposed (brainstorm outcome — for team review)
- **Date:** 2026-07-28
- **Relates to:** [ADR 0001](0001-git-native-knowledge-base.md) decides *what* the store is (git as source of truth, Postgres derived). This ADR decides the *shape* of the code around it.

---

## Context

Should the git-backed store be built **as a deepagents backend**, or as a **standalone core** that deepagents consumes?

The same knowledge already has **multiple real consumers**:

- the chat agent (deepagents filesystem tools),
- a **Knowledge Base REST API** (Rohan's mandate to standardize artifact generation and drop redundant UI dialogues),
- the **vector-store sync** (the derived chunk/embedding index),
- and, later, an **MCP server** and **connect-your-own-remote** (GitHub/GitLab).

If the core were a deepagents backend, every other consumer would have to route through an agent framework to touch the KB. That is the wrong dependency direction.

## Decision

**Structure the KB as Hexagonal / Ports & Adapters.** A framework-agnostic **knowledge core** exposes capabilities through **ports**; every consumer is an **adapter** at the edge; the core imports no consumer framework.

- **Core (inside the hexagon):** `KnowledgeStore` — versioned knowledge (content + history). Already framework-free (imports no deepagents).
- **Driven port** (core → infrastructure): `VersionedContentEngine` → `GitContentEngine` (dulwich). Swappable, mirroring libgit2's pluggable backends.
- **Driven consumer** (downstream of commits): the **vector-store sync / derived index** — subscribes to commits, one-way (git → Postgres). The core does not know it exists.
- **Driving adapters** (world → core): the **deepagents backend** (build now), the **KB REST API** (later), **MCP / remote git** (future). The core is oblivious to all of them.
- **Capabilities live once in the core; each adapter selects the subset it needs.** `ls`/`grep`/`glob` are offered for filesystem-shaped consumers (agent, MCP); document/version/diff verbs serve the REST API; commit-diff serves the sync. We never force one consumer's verbs on another, and never reimplement a capability per consumer.

## Borrowing, not inventing

| Element | Borrowed from |
|---|---|
| Framework-agnostic core, many adapters per port | **Cockburn — Ports & Adapters (Hexagonal)**: "there will typically be multiple adapters for any one port." https://alistair.cockburn.us/hexagonal-architecture |
| Agnostic core + thin consumer layer | **Git's own plumbing/porcelain split** — low-level toolkit as building blocks, user commands on top. https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain |
| Linkable core + pluggable storage backends for long-running services | **libgit2** (built precisely because forking the git binary is wrong for services) — same reasoning that chose **dulwich** behind `VersionedContentEngine`. |

## Scope — YAGNI (v1)

**Build now:** the **core** + the **deepagents adapter** + the **vector-store-sync driven consumer**. Nothing else.

- Design the ports so the **REST API** and **MCP/remote-git** adapters slot in later — but **do not build them now**.
- **Grow the port surface on demand:** add a capability when an adapter needs it. No speculative methods.
- The deepagents adapter should **reuse deepagents' own `FilesystemBackend`/git plumbing** for read-only structure ops (`ls`/`glob`/`grep`) rather than reimplementing them; writes + commit route through the core so the write lock, commit policy, and citation control have one home.

## Consequences

### Positive

- Consumers are decoupled: the KB REST API and MCP become **thin adapters**, not parallel rewrites of the KB.
- The core is testable with no agent framework in the loop; deepagents itself becomes swappable.
- Matches ADR 0001's "separate file management from the vector store/search" — the sync is just one driven consumer.

### Negative / cost

- One indirection (an adapter) over using `FilesystemBackend` directly — accepted because the consumers are real, not hypothetical (the YAGNI test for hexagonal).

## Open (deferred to their phases)

- **Citation model** for `read_file`: raw file vs chunk-rendered `[n]`. Rohan flagged current citations as poor — redesign candidate.
- **Commit granularity**: per-turn (Aider-style) vs per-mutation.
- **Turn isolation**: shared working tree + per-workspace lock vs per-turn worktree.
Loading
Loading