aboutsummaryrefslogtreecommitdiff
path: root/docs/agent-knowledge-base-spec.org
blob: 78ff9bd53eb0fc94becf23215990f582b2237933 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#+TITLE: Agent Knowledge Base on Org-roam — Spec
#+AUTHOR: Craig Jennings & Claude
#+DATE: 2026-06-10

* Metadata
| Status   | implemented — v1 (Phases 0-4) shipped 2026-06-10 on Craig's go; manual validation + other-machine clones outstanding (todo.org) |
| Owner    | Craig Jennings                                                      |
| Reviewer | Craig Jennings; Codex (2026-06-10)                                  |
| Related  | [[file:../todo.org][todo.org — "Check that memories are sync'd across machines via git"]] |

This spec supersedes the 2026-06-05 draft (formerly docs/design/2026-06-05-org-roam-knowledge-base-spec.org, removed; content in git history), folding in Craig's 2026-06-10 ratification answers and restructuring to the spec-create format.

* Summary

Agents adopt Craig's existing org-roam knowledge base (~490 org files, curated since 2023) as the shared, cross-project store for durable knowledge. The KB moves from Syncthing to git (D8): it relocates out of the =~/sync/org= share into its own repo with a cjennings.net remote, synced the way every other repo is. Per-project harness memory stays as a fast capture layer; durable facts get promoted into the KB. This replaces two abandoned designs (a dedicated memory repo, a two-tier rules split) with the substrate that already exists — now with git history and revertability on every write.

* Problem / Context

Per-project agent memory lives at =~/.claude/projects/<encoded-cwd>/memory/=, a harness-owned path that is unmanaged and unsynced, so it doesn't survive a new-machine setup or dotfiles restore. Anything durable an agent learns is at-risk by default.

Two fixes were built or designed and dropped. A dedicated =claude-memory.git= repo was built then reversed because it pooled work-confidential and personal memory into one store. A two-tier split (general lessons to rules, project memory to each project's =.ai/=) left public-remote code projects' memory at-risk by design.

The simplification: the knowledge base already exists and already syncs across machines. The task stops being "build a memory-sync system" and becomes "point agents at the existing KB." The 2026-06-10 transport revision (D8) moves that sync from Syncthing to git — history, revertability, and explicit per-machine replication, with the KB syncing like every other repo instead of through a second mechanism.

* Goals and Non-Goals

** Goals
- Durable, cross-machine agent knowledge synced the same way as every other repo (git, cjennings.net remote).
- Agents query the KB before relying on remembered project facts, prior decisions, or reference material.
- Agent-written notes are distinguishable from Craig's, and index cleanly in his org-roam.
- The work/personal confidentiality boundary is explicit and enforced on the write side.

** Non-Goals
- The rules layer (=claude-rules/=, =CLAUDE.md=) is untouched. The KB replaces the memory tier, not the rules tier.
- No Emacs/org-roam package integration; agents never touch the SQLite cache.
- No autonomy expansion. Free agent writes apply to the KB only — email, Linear comments, PRs, and every other public or external channel still require Craig's review and consent (D6).

** Scope tiers
- v1: the transport migration (Phase 0), the pointer rule, the write schema, the boundary, the guided memory sweep (Phase 1.5), one verified seed node, the promotion cadence with usage instrumentation, and the monthly hygiene pass (Phase 4).
- Out of scope: wholesale import of historical harness memory (the sweep is guided and approved, not bulk); auto-promotion.
- vNext: a =/promote= command if the wrap-up prompt proves insufficient; an =:agent:inbox:= staging tag if free writes prove too noisy.

* Design

The KB is a directory of plain org files with =#+title=, =#+filetags=, =:ID:= property drawers, and =[[id:UUID]]= links. That is the entire interface.

For a *reader* (any agent): query with ripgrep over content and tags; follow a link by grepping for its target =:ID:=. The backlink graph and database are Craig's Emacs conveniences — the files are the agent's interface. Before relying on a remembered project fact, a prior decision, or reference material, search the KB first.

Conflict-file exclusion is part of the command contract, not a prose reminder — the KB carries dozens of Syncthing =*.sync-conflict-*= files (63 at last count) whose contents are stale or duplicated. The canonical commands:

#+begin_src sh
# content/tag search
rg --glob '*.org' --glob '!*sync-conflict*' '<query>' <kb-path>/
# follow an [[id:UUID]] link to its node
rg --glob '*.org' --glob '!*sync-conflict*' ':ID:[[:space:]]+<uuid>' <kb-path>/
#+end_src

For a *writer* (personal-project agents only, per D5): create one node per fact, following roam conventions so the next =org-roam-db-sync= indexes it:

#+begin_example
:PROPERTIES:
:ID:       <generated uuid>
:END:
#+title: <concise title>
#+filetags: :agent:<scope>:

<the fact, with [[id:...]] links to related nodes>
#+end_example

Filename follows roam's timestamp-prefix convention (=YYYYMMDDHHMMSS-slug.org=), under the =agents/= subdirectory (see Transport and layout below). The =:agent:= filetag makes =rg '#\+filetags:.*:agent:'= a clean inventory of everything agents wrote, so Craig can review or prune at will.

** Transport and layout (D8)

The KB is a git repo with a cjennings.net remote, not a Syncthing subtree. It relocates out of the =~/sync/org= share (that share's =.stfolder= root sits at =~/sync/org=) to a standalone path — proposed =~/org/roam=, final path set at migration — and =roam-dir= in Craig's =org-roam-config.el= follows it.

- Craig's edits sync via a systemd user timer (the git-sync pattern: =pull --rebase=, commit, push every 15-30 minutes), preserving his zero-touch flow.
- Agents need no new plumbing: pull before query, commit + push after write — the same session discipline as every other repo. Agent commits are attributable in history.
- Per-machine replication is opt-in by clone. The work machine simply doesn't clone, which retires D5's replication exposure.
- The phone needs no roam leg: Craig's on-demand pattern (an agent drops a doc into the separate =~/sync/phone= share, Syncthing pushes it to the phone) replaces syncing all of roam to mobile.
- Agent writes land under the =agents/= subdirectory. org-roam scans recursively, so it's one database and one search surface with id-links intact, while the agent corpus stays physically corralled — hideable later with one =org-roam-file-exclude-regexp= line, and a future split is a =mv=. The =:agent:= filetag stays on every node regardless; the subdirectory is bulk management, the tag is identity.
- Once migrated, the Syncthing =*.sync-conflict-*= file class disappears (git merges or conflicts loudly; it never forks silent copies). The query commands keep their exclusion globs through the transition; they're harmless afterward.

** Inclusion criteria — what goes in, what stays out

=knowledge-base.md= carries these as rules, so every project applies the same bar.

In: durable facts with cross-project or cross-machine value — decisions and their why, environment and tooling gotchas, reference pointers (URLs, dashboards, key paths), lessons that transfer beyond the project that learned them.

Out: anything the repo already records (code structure, git history, CLAUDE.md content), session state, task state (todo.org owns that), high-churn facts that will be stale in a month, secrets and credentials, and anything work-confidential (the D5 boundary).

Existing knowledge migrates through a one-time guided sweep per project (Phase 1.5), not a wholesale import: the project's agent reads its harness-memory dir, classifies each fact against the criteria above (KB-worthy / stays local / stale-delete), and proposes the batch for Craig's approval. The memory frontmatter helps: =reference=-type and durable =feedback=-type memories are natural candidates; most =project=-type entries stay local.

** Project classification and write routing (v1)

D5's boundary needs an executable answer to "is this project allowed to write?" — inference from cwd names, remotes, or task content is too much discretion for a confidentiality boundary. The v1 source of truth is an explicit *work-root denylist* carried in =knowledge-base.md= (=~/projects/work= — confirmed complete by Craig, 2026-06-10; archangel is not work-scoped). Classification:

- *Work* — the project root is, or sits under, a denylisted work root. No KB write, ever. The agent records durable facts per that project's own conventions (work already keeps its knowledge in its project tree); v1 adds no new work-side store.
- *Personal* — the project root sits under a known project parent (=~/code/=, =~/projects/=, =~/.emacs.d=) and is not denylisted. KB writes allowed per D6.
- *Unknown* — anything else. No KB write. Refuse and report.

The refusal message contract (work and unknown alike): state the classification, name the durable fact in a one-line redacted summary, and say where it was or wasn't written — so Craig can re-route it deliberately instead of losing it silently.

** Harness memory: capture, then promote

Harness memory keeps its current role but is redefined as an ephemeral working set: fast, automatic, per-project, relevance-recalled, and allowed to be at-risk because nothing durable depends on it surviving. Durable or cross-machine-valuable facts get promoted into the KB as a deliberate step (wrap-up, a task audit, or an explicit prompt) — the same capture-on-landing / promote-on-review cadence the pattern catalog uses.

A new =claude-rules/knowledge-base.md= rule (auto-installs via the Makefile RULES glob, like =patterns.md= — no Makefile change expected) is the bridge: it carries the KB path, the query commands, the write schema, the classification denylist, and the D5/D6 boundary with its refusal contract.

* Alternatives Considered

** Dedicated private git repo (=claude-memory.git=)
- Good, because git gives history, review, and a deliberate sync step.
- Bad, because it pooled work-confidential and personal memory into one all-machines store — the reason it was built and then reversed (2026-05-23/24).
- Bad, because it added a new clone + symlink mechanism every machine must maintain.
- Neutral, because the pooling objection is solved by D5's write boundary, not by avoiding git — D8 (2026-06-10) adopts git as the transport for the existing KB, keeping this alternative's history and revert benefits without its new-store cost.

** Two-tier split (rules file + per-project =.ai/memory/=)
- Good, because general lessons would load natively into every session via the rules layer.
- Bad, because project memory in gitignored-=.ai/= projects stays at-risk by design — it solved sync only where =.ai/= was tracked.
- Neutral, because the promote-general-lessons instinct survives in this design as KB promotion.

** Org-roam KB (chosen)
- Good, because the substrate already exists and already syncs — no new store to build.
- Good, because the KB is already Craig's curated knowledge home; agent knowledge lands where he actually looks.
- Bad as originally specced on Syncthing — no review gate, no history, silent conflict forks. D8 moves the transport to git, which restores history and revert and softens the D6 risk to "revertable after push."
- Neutral, because agents read it as plain files; no org-roam tooling required or used.

* Decisions

** D1 — The KB is a queried substrate, accessed as files
- State: accepted (Craig, 2026-06-10)
- Context: an agent is a harness process, not an Emacs session; it cannot call org-roam's Elisp API or read its SQLite cache.
- Decision: We will treat the KB directory (pre-migration =~/sync/org/roam/=; post-Phase-0 the D8 git checkout) as a directory of plain org files — ripgrep for search, grep-for-=:ID:= to follow links.
- Consequences: easier — works in every runtime, no Emacs dependency; harder — no backlink graph or db-backed queries for agents (acceptable: ~490 tagged, linked text files grep well).

** D2 — Capture in harness memory, promote into the KB
- State: accepted (Craig, 2026-06-10)
- Context: harness memory is fast and auto-recalled but unsynced; the KB is durable but query-only.
- Decision: We will keep both with distinct roles — harness memory captures, the KB holds what's promoted. Promotion is deliberate (wrap-up, task audit, or explicit prompt), never automatic.
- Consequences: easier — the at-risk problem dissolves (what stays unsynced is by definition the regenerable hot set); harder — promotion is a discipline that has to actually happen, or value silts up in the capture layer. D7 (resolved: keep) confirms the capture layer stays, so this decision stands as written.

** D3 — Surfacing via a pointer rule
- State: accepted (Craig, 2026-06-10)
- Context: agents need to know the KB exists, where it lives, and how to use it — in every project, on every machine.
- Decision: We will ship =claude-rules/knowledge-base.md= carrying path, query method, write schema, and boundary. It auto-installs via the existing Makefile RULES glob.
- Consequences: easier — one rule, machine-wide, same mechanism as =patterns.md=; harder — nothing material.

** D4 — Write schema: roam-valid, =:agent:=-tagged, one node per fact
- State: accepted (Craig, 2026-06-10: "per fact")
- Context: agent writes must index cleanly in Craig's org-roam and stay distinguishable from his hand-authored notes. Granularity was open: per-fact nodes vs a per-project appended notes file.
- Decision: We will write one roam-valid node per fact (=:ID:= drawer, =#+title=, =#+filetags: :agent:<scope>:=, timestamp-prefixed filename), linking related nodes by =[[id:]]=.
- Consequences: easier — roam-native, linkable, =rg :agent:= inventories everything agents wrote; harder — more files (accepted; that's what roam is).

** D5 — Write boundary: read-shared, write-scoped (option C)
- State: accepted (Craig, 2026-06-10: "Your recommendation C is the right one.")
- Context: the KB is personal and replicates to every Syncthing machine — Craig confirmed that includes a work machine. The leak risk is asymmetric and lives on the write side: a work agent writing confidential facts would pool them into the personal store.
- Decision: We will let any project read the shared KB; only personal projects write to it. Work agents write to work's own project tree, never the shared KB. Craig confirmed C handles the work-machine replication acceptably.
- Consequences: easier — reading value lands everywhere, confidential work data stays physically out of the KB; harder — work knowledge has no shared home (status quo, unchanged), and the inverse risk (personal facts surfacing in work artifacts) remains governed by the existing content-scope rules in =commits.md=. Under D8, machine replication is opt-in by clone, so the work machine holds no KB copy at all unless deliberately cloned.

** D6 — Agent writes land freely in the KB, and only there
- State: accepted (Craig, 2026-06-10)
- Context: Syncthing has no git-style review gate; the alternative was a staging tag (=:agent:inbox:=) Craig promotes from.
- Decision: We will let agent writes land freely in the KB without a review gate. This autonomy is scoped to the KB alone — it is not permission to send email, comment on Linear tickets, or post to any public or external channel; those still require Craig's review and consent.
- Consequences: easier — no promotion queue to tend, knowledge lands immediately; harder — a bad write lands without pre-review (mitigated by the =:agent:= inventory, Craig's normal roam curation, and — under D8 — git history making every write attributable and revertable).

** D7 — Harness memory stays as the capture layer
- State: accepted (Craig, 2026-06-10: "keep")
- Context: with the KB live, harness memory (=~/.claude/projects/<enc>/memory/= — the per-project store the harness auto-loads into context at session start) could either stay or retire. *Keep* preserves automatic relevance recall at zero query cost, at the price of two stores plus the promotion habit. *Retire* would mean one store and no promotion step, but recall stops being automatic and session start gets heavier.
- Decision: We will keep harness memory as the ephemeral capture layer. D2 stands as written, and Phase 3's promotion cadence is required, not optional — it's what keeps the capture layer from silting up.
- Consequences: easier — automatic recall keeps working, no harness behavior changes; harder — two stores and a promotion discipline (mitigated by Phase 3's mechanical wrap-up trigger).

** D8 — Transport: git on cjennings.net, not Syncthing
- State: accepted (Craig, 2026-06-10: "shape a looks good")
- Context: the KB lived inside the =~/sync/org= Syncthing share — no history, no review gate, silent =*.sync-conflict-*= forks (63 at count), and share-level replication that pushed roam to a work machine whether wanted or not. The phone constraint dissolved on inspection: Craig's mobile pattern is on-demand (an agent drops a doc into the =~/sync/phone= share), not whole-roam sync, and he prefers it that way. The earlier dedicated-memory-repo design was rejected for pooling work and personal facts, which D5 now solves independent of transport.
- Decision: We will move the KB out of the Syncthing share into its own git repo (proposed =~/org/roam=, cjennings.net remote). Craig's edits auto-sync via a systemd user timer; agents pull before query and commit + push after write; machines replicate by cloning, and the work machine doesn't clone.
- Consequences: easier — per-fact history and blame, revertable and attributable agent writes, the conflict-file class disappears, explicit per-machine replication, one sync model everywhere; harder — a one-time migration (conflict-file cleanup, the move, the Emacs =roam-dir= update, a link sweep for the old path, timer setup) and staleness windows between timer runs where Syncthing was near-instant (mitigated by pull-at-session-start on both Craig's and agents' sides). D4's one-node-per-fact files make merge collisions rare by construction.

* Implementation phases

All five phases shipped 2026-06-10 (Craig's go, no-approvals batch). Completion details per phase live under the parent task in todo.org; what remains is the manual-testing child and the other personal machines' one-time clone + timer setup.

** Phase 0 — Transport migration (D8)
Resolve or delete the 63 =*.sync-conflict-*= files, move the directory out of the =~/sync/org= share to the new path, =git init= + =.gitignore= + initial commit + push to the cjennings.net remote, clone on the other personal machines (not the work machine), and set up the auto-sync timer. Update =roam-dir= in =org-roam-config.el= (handoff to the =.emacs.d= project) and sweep references to the old path (the =protocols.org= task-list pointer at =~/sync/org/roam/inbox.org=, among others) per the keep-links-current rule; a transition symlink at the old location covers stragglers. Verify: Syncthing no longer tracks the tree, org-roam indexes the new location, and an edit round-trips between two machines via the timer.

** Phase 1 — Pointer rule
The work-root denylist is confirmed (=~/projects/work= only, Craig 2026-06-10). Write =claude-rules/knowledge-base.md=: the KB path (post-migration), the canonical query commands, the D4 schema with the =agents/= subdirectory, the pull-before-query / commit-and-push-after-write discipline, the inclusion criteria, the classification + write-routing rules, the refusal contract, and the D5/D6 boundary. =make install= links it machine-wide via the existing RULES glob — no Makefile change. Tree stays working throughout (pure addition).

** Phase 1.5 — Guided memory migration (one-time, per project)
A small =migrate-memories= pass each project runs once: read the project's harness-memory dir, classify each fact against the inclusion criteria (KB-worthy / stays local / stale-delete), propose the batch, and write approved facts as =agents/= nodes. Craig approves per project; no wholesale import.

** Phase 2 — Seed node + index verification
The seed node is the KB's own user-facing documentation: a "How the agent knowledge base works" node — what agents write, the =:agent:= tag, the inventory command, what Craig can prune. A genuine durable fact, it doubles as Craig's doc and validates the schema end-to-end: Craig confirms it indexes and displays (=org-roam-db-autosync-mode= is on, so no manual sync step). Rollback if the schema fails: delete or revert that one node.

** Phase 3 — Promotion cadence + usage instrumentation
Wire the promotion prompt into the wrap-up workflow (a "anything worth promoting to the KB?" check), and have wrap-up record one line in the session summary: "KB: promoted N / consulted yes-no." That single line makes usage measurable by grepping session archives — the input to the Success metrics checkpoint. Note the cadence in =knowledge-base.md=. Resolves D2's discipline risk with a mechanical trigger.

** Phase 4 — Maintenance automation
A monthly agent-run hygiene pass: the =:agent:= inventory, orphan and duplicate detection, node-count trend — report dropped in the rulesets inbox, deletions proposed for approval (auto-cleanup allowed only for =:agent:=-tagged nodes). Extends the filed hygiene-reports task; under D8 there is no conflict-file purge left to schedule. Craig's recurring duty collapses to approving one short report a month.

* Acceptance criteria

- [ ] =claude-rules/knowledge-base.md= exists with path, query method, write schema, and the ratified D5/D6 boundary.
- [ ] An agent in a personal project can find a relevant prior note by querying the KB.
- [ ] An agent-written node indexes cleanly in Craig's org-roam on the next =org-roam-db-sync= and is identifiable via the =:agent:= filetag.
- [ ] The capture/promote split and its trigger are documented.
- [ ] A work-project agent, asked to store a durable fact, writes it to work's own tree, not the KB.
- [ ] An unknown-classification project, asked to store a durable fact, refuses the KB write and reports the redacted fact per the refusal contract rather than guessing.
- [ ] The documented query commands find a known note and exclude =*.sync-conflict-*= files.
- [ ] The KB is a git repo with a cjennings.net remote; Syncthing no longer tracks it; the auto-sync timer round-trips Craig's edits between machines.
- [ ] Agent writes land under =agents/= as attributable commits.
- [ ] =knowledge-base.md= states the inclusion criteria and the pull / commit-push discipline.
- [ ] The 30-day Success-metrics checkpoint is scheduled with its thresholds recorded.

* Success metrics

The acceptance criteria prove the mechanism; these prove the design. Measured from Phase 3's wrap-up instrumentation line and the =:agent:= inventory:

- Usage: node count grows steadily, and promotions appear in wrap-up lines (most multi-session weeks promote at least one fact).
- Recall wins: a KB query answers something that would otherwise be re-derived or re-asked. The decisive form is cross-project — a fact written in project A used in project B. Target: at least one cross-project win inside 30 days.
- Boundary integrity: zero work-classified content in the KB, ever. Auditable via the inventory and the git log.
- Hygiene: orphan and duplicate rate among =:agent:= nodes stays low enough that the monthly report is boring.

30-day checkpoint: review the four together. Writes-but-no-recall-wins means agents aren't querying — strengthen the query trigger in =knowledge-base.md= rather than writing more. No writes at all means the promotion prompt isn't firing or the inclusion bar is set wrong. Either failure revises the rule, not the substrate.

* Readiness dimensions

- Data model & ownership: KB nodes are user-curated (Craig) or agent-authored (=:agent:= tag); agents never edit Craig's hand-authored nodes, only link to them. Harness memory stays agent-owned and ephemeral.
- Errors, empty states & failure: a missing KB checkout (machine without the clone) means the rule's query step finds nothing — agents proceed without the KB and say so rather than fabricate recall. No write occurs to a nonexistent path.
- Security & privacy: D5/D6 are the whole story — work never writes; KB writes never extend to public channels. No credentials live in the KB.
- Observability: =rg '#\+filetags:.*:agent:'= inventories all agent writes; roam's UI shows them tagged in Craig's normal browsing; under D8 the git log is the per-write audit trail.
- Performance & scale: 484 files today; ripgrep over a few thousand org files is milliseconds. N/A as a concern.
- Reuse & lost opportunities: maximal — the entire design is reusing an existing synced, curated store instead of building one.
- Architecture fit & weak points: mirrors the patterns.md pointer-rule shape; the existing Makefile RULES glob installs the new rule with no Makefile change. The legacy weak point (Syncthing conflict files, 63 today) is retired by D8 at migration; the query commands keep their exclusion globs through the transition.
- Config surface: one path constant in =knowledge-base.md=. No knobs.
- Documentation plan: =knowledge-base.md= is the documentation; the spec records the why.
- Dev tooling: N/A because the interface is ripgrep and Write — no build, no tests beyond Phase 2's manual index check.
- Rollout, compatibility & rollback: pure addition past Phase 0; rollback is deleting the rule file and (optionally) the =:agent:=-tagged nodes — a one-command sweep by tag, or a revert by commit under D8. Phase 0 itself is reversible until the Syncthing copy is retired.
- External APIs & deps: git + the cjennings.net remote, the same dependency every other repo carries; otherwise plain files. Verified 2026-06-05/10: ~490 org files plus 63 conflict files at the pre-migration location inside the =~/sync/org= share; =org-roam-db-autosync-mode= is on in Craig's config.

* Risks, Rabbit Holes, and Drawbacks

- Un-reviewed writes land without a gate (D6 accepted this). Dodge: under D8 every write is an attributable, revertable commit, and the =:agent:= inventory keeps cleanup cheap.
- Promotion discipline may not stick (D2). Dodge: Phase 3 makes it a mechanical wrap-up step rather than a memory burden, and its instrumentation line makes a lapse visible at the 30-day checkpoint.
- Staleness between machines in the gap between auto-sync timer runs (Syncthing was near-instant). Dodge: pull at session start on both Craig's and agents' sides; D4's per-fact files make collisions rare when concurrent edits do happen.
- The Phase 0 move breaks references to the old path (the Emacs =roam-dir=, the =protocols.org= task-list pointer). Dodge: the migration includes a link sweep plus a transition symlink at the old location.
- An incomplete work-root denylist would let a work project classify as personal. Dodge: Craig confirmed the denylist (=~/projects/work= only, 2026-06-10), and the classification's safe default (unknown → refuse) covers anything outside the known parents.

* Testing / Verification

From the 2026-06-10 review, the verification surface for v1:

- =make install= links =knowledge-base.md= into =~/.claude/rules/=.
- In a personal repo, the documented =rg= command finds a known note.
- In a work repo, a durable-storage request produces no write in the KB and the refusal report names the fact.
- In an unknown project, the agent refuses or asks rather than guessing.
- One approved seed node indexes via =org-roam-db-sync= and appears in the =rg '#\+filetags:.*:agent:'= inventory.
- A =*.sync-conflict-*= file containing a unique token is excluded by the documented query.

- After Phase 0: an edit made on one machine appears on another within the timer interval, no new =*.sync-conflict-*= files appear, and the work machine has no clone.

The first, second, and last checks are agent-runnable; the org-roam display check, the work/unknown behavioral checks, and the cross-machine round-trip are Craig's manual validation (tracked in todo.org).

* Review dispositions

Modified recommendations from the 2026-06-10 Codex review, with reasons. Everything else was accepted as written.

- *Drop-in =[#B]= implementation tasks as standalone top-level TODOs* — modified: the phase tasks hang as children under the existing parent task ("Check that memories are sync'd across machines via git"), per the one-parent-owns-the-effort convention in the response workflow. Content carried over intact.
- *Update the fact count to the exact recursive number* — modified: the spec now says ~490 (Codex's own alternative), so routine KB growth doesn't churn the spec.
- *Define the exact work-side write destination* — modified within the review's own options: v1 adds no new work-side store. Work projects keep their existing project-tree conventions, and the KB rule's only work-side behavior is the refusal + report.

* Review and iteration history

** 2026-06-05 Fri @ 05:57:35 -0500 — Claude (rulesets session) — author
- What: initial one-page draft (five decisions, mechanics recommended), after Craig redirected the memory-sync task onto the existing org-roam KB.
- Why: the dedicated-repo and two-tier designs both failed on the work/personal boundary or left memory at-risk; the KB already syncs.
- Artifacts: original draft at docs/design/2026-06-05-org-roam-knowledge-base-spec.org (superseded by this file; content in git history).

** 2026-06-10 Wed @ 14:29:20 -0500 — Craig Jennings (cj annotations) + Claude — author revision
- What: folded in Craig's ratification answers (D5 = option C; Syncthing does replicate to a work machine and C stands; per-fact node granularity; free KB-only writes with the explicit no-public-channels boundary) and rewrote into the spec-create format. D7 (harness memory's fate) held open with the fuller explanation Craig requested.
- Why: all but one decision ratified; the 2026-06-05 draft predated the spec-create template.
- Artifacts: this file; implementation explicitly deferred pending Craig's go-ahead.

** 2026-06-10 Wed @ 14:35:40 -0500 — Codex — reviewer
- What changed or was recommended: reviewed implementation readiness and wrote a blocking review. The main blockers are unresolved D7 and the missing executable personal/work/unknown write-boundary classifier; medium notes cover concrete =rg= commands for conflict-file exclusion and seed-node approval/rollback mechanics.
- Why: implementation would otherwise force the agent to invent memory architecture and confidentiality-boundary behavior at write time.
- Artifacts: docs/agent-knowledge-base-spec-review.org (deleted on disposition completion per the response workflow; content summarized here and in Review dispositions).

** 2026-06-10 Wed @ 14:39:41 -0500 — Claude Code (rulesets) — responder
- What: processed the Codex review with Craig's D7 ratification ("keep") as a pre-agreed input. Both blockers cleared: D7 accepted (harness memory stays the capture layer, Phase 3 mandatory) and a new "Project classification and write routing" design subsection (work-root denylist as source of truth, unknown → refuse, refusal message contract, no new work-side store). Mediums accepted: canonical =rg= commands with conflict-file exclusion baked in, Phase 2 approval/rollback mechanics, Makefile no-change note, ~490 fact count, Testing/Verification section. Three recommendations modified (see Review dispositions); none rejected.
- Why: converge to implementation-ready. Rubric: ready with caveats — the one caveat is confirming the work-root denylist contents with Craig before Phase 1 ships the rule.
- Artifacts: this file; implementation-task breakdown under the parent task in todo.org; review file deleted.

** 2026-06-10 Wed @ 17:29:37 -0500 — Craig Jennings — caveat resolved
- What: confirmed the work-root denylist is complete at =~/projects/work= alone; archangel is not work-scoped.
- Why: this was the single "ready with caveats" caveat. The spec is now ready. Implementation still awaits Craig's explicit go.
- Artifacts: this file (status flipped to ready); the denylist VERIFY in todo.org resolved to a dated entry.

** 2026-06-10 Wed @ 17:57:08 -0500 — Craig Jennings + Claude — amendment: D8 git transport + five design folds
- What: Craig's five design questions answered and ratified. New D8: the KB moves out of the =~/sync/org= Syncthing share into its own git repo on cjennings.net (Shape A — whole roam), with an =agents/= subdirectory for agent writes, a systemd auto-sync timer for Craig's edits, opt-in-by-clone replication (work machine doesn't clone), and the phone staying on the on-demand =~/sync/phone= pattern. Folded in: inclusion criteria + a Phase 1.5 guided memory migration, a Success metrics section with a 30-day checkpoint, the seed node redefined as the KB's own user-facing documentation, and Phase 4 maintenance automation. Phases renumbered 0-4.
- Why: the original verification proved mechanism but not success; migration and maintenance were unspecified; and Syncthing's no-history / no-gate / conflict-fork costs were the design's weakest accepted risks — git removes them for the price of a one-time migration. The phone constraint that might have blocked Shape A dissolved: Craig's mobile pattern is on-demand doc drops, not whole-roam sync.
- Artifacts: this file; todo.org phase tasks updated (Phase 0, 1.5, and 4 added). Implementation remains held pending Craig's go.

** 2026-06-10 Wed @ 18:21:33 -0500 — Claude (rulesets, no-approvals batch) — v1 implemented
- What: all five phases shipped on Craig's go. Phase 0: roam migrated to =~/org/roam= as a git repo (roam.git on cjennings.net), 63 conflict files resolved by deletion (tarball backup kept), transition symlink at the old path, Emacs constants updated live, roam-sync timer running, old-path references swept. Phase 1: =knowledge-base.md= written and linked machine-wide. Phase 1.5: rulesets memories swept (3 promoted, 2 rule-encoded kept local, 1 de-staled); sweep handoff broadcast to the 10 other memory-bearing personal projects. Phase 2: the seed/doc node written and verified indexed (420 nodes). Phase 3: wrap-up promotion check + the =KB:= receipt line. Phase 4: =kb-hygiene.sh= + monthly timer, live-run verified.
- Why: spec status was ready with all decisions ratified; Craig authorized the full batch.
- Artifacts: rulesets commits fcf554a, d071f1f, 242b95e, b014095 (+ todo.org/spec bookkeeping); roam.git initial history; handoffs to .emacs.d, archsetup, and the 10 sweep targets. Outstanding: the manual-testing checklist and other-machine clones.

** 2026-06-10 Wed @ 17:31:10 -0500 — Codex — reviewer
- What changed or was recommended: re-ran the spec-review workflow after the caveat resolution. Rubric: ready. No new blocking or medium-priority findings; no review file written. Confirmed the implementation phases and test-surface tasks are already represented under the existing parent task in todo.org.
- Why: the prior blockers are dispositioned, the work-root denylist is confirmed, the pointer-rule install path matches the current Makefile RULES glob, and v1's manual/agent-runnable verification surface is explicit.
- Artifacts: this file; [[file:../todo.org][todo.org]] parent task "Check that memories are sync'd across machines via git".