@cairnkeep/cli 2.3.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +232 -6
  3. package/bin/cairn +43 -2
  4. package/claude/capability-contract/hooks/capability-command-finish.sh +81 -0
  5. package/claude/capability-contract/hooks/capability-command-start.sh +76 -0
  6. package/claude/hooks/compaction-capture.sh +34 -0
  7. package/claude/hooks/memory-capture.sh +47 -5
  8. package/claude/hooks/memory-wakeup.sh +55 -2
  9. package/docs/building-an-overlay.md +4 -3
  10. package/docs/design/meta-agent.md +439 -0
  11. package/docs/harness-compatibility.md +117 -0
  12. package/docs/learning/CURRICULUM-MAP.md +91 -0
  13. package/docs/learning/FEATURE-GUIDE.md +86 -0
  14. package/docs/learning/PRODUCTION-PLAN.md +45 -14
  15. package/docs/learning/README.md +20 -1
  16. package/docs/learning/instructor-guide.md +6 -0
  17. package/docs/learning/lessons/L00-why-cairnkeep.md +20 -8
  18. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  19. package/docs/learning/lessons/L02-installation.md +23 -5
  20. package/docs/learning/lessons/L03-first-project.md +7 -2
  21. package/docs/learning/lessons/L04-memory-fundamentals.md +4 -0
  22. package/docs/learning/lessons/L05-daily-workflow.md +4 -0
  23. package/docs/learning/lessons/L07-privacy-and-storage.md +3 -0
  24. package/docs/learning/lessons/L10-context-exploration.md +4 -2
  25. package/docs/learning/lessons/L12-managed-overlays.md +2 -0
  26. package/docs/learning/lessons/L13-session-evidence.md +71 -0
  27. package/docs/learning/lessons/L14-typed-memory.md +51 -0
  28. package/docs/learning/lessons/L15-capability-governance.md +56 -0
  29. package/docs/learning/lessons/L16-evaluation.md +67 -0
  30. package/docs/learning/lessons/L17-agent-boundary.md +55 -0
  31. package/docs/learning/templates/lesson-template.md +4 -1
  32. package/docs/learning/tracks/evidence-and-evaluation.md +20 -0
  33. package/docs/learning/tracks/operator.md +8 -2
  34. package/docs/learning/tracks/practitioner.md +6 -1
  35. package/docs/learning/tracks/quickstart.md +3 -0
  36. package/docs/learning/video-scripts/L00-why-cairnkeep.md +21 -17
  37. package/docs/learning/video-scripts/L02-installation.md +9 -2
  38. package/docs/learning/video-scripts/L03-first-project.md +3 -0
  39. package/docs/operating.md +567 -7
  40. package/docs/privacy-and-data-flow.md +417 -0
  41. package/docs/storage.md +252 -0
  42. package/examples/eval/README.md +42 -0
  43. package/examples/eval/adapter.json +12 -0
  44. package/examples/eval/bundled-fake.json +1 -0
  45. package/examples/eval/task-set.json +1 -0
  46. package/mcp-memory-server/dist/artifact-cli.js +184 -0
  47. package/mcp-memory-server/dist/artifact-schema.js +218 -0
  48. package/mcp-memory-server/dist/artifact-store.js +812 -0
  49. package/mcp-memory-server/dist/capability-adapter.js +286 -0
  50. package/mcp-memory-server/dist/capability-cli.js +361 -0
  51. package/mcp-memory-server/dist/capability-config.js +310 -0
  52. package/mcp-memory-server/dist/capability-harness.js +496 -0
  53. package/mcp-memory-server/dist/capability-registry.js +123 -0
  54. package/mcp-memory-server/dist/capability-schema.js +65 -0
  55. package/mcp-memory-server/dist/capability-store.js +431 -0
  56. package/mcp-memory-server/dist/compaction-normalize.js +267 -0
  57. package/mcp-memory-server/dist/eval-cli.js +523 -0
  58. package/mcp-memory-server/dist/eval-plan.js +346 -0
  59. package/mcp-memory-server/dist/eval-process.js +273 -0
  60. package/mcp-memory-server/dist/eval-report.js +609 -0
  61. package/mcp-memory-server/dist/eval-runner.js +827 -0
  62. package/mcp-memory-server/dist/eval-schema.js +368 -0
  63. package/mcp-memory-server/dist/eval-statistics.js +161 -0
  64. package/mcp-memory-server/dist/eval-workspace.js +313 -0
  65. package/mcp-memory-server/dist/failure-signature.js +141 -0
  66. package/mcp-memory-server/dist/index.js +600 -55
  67. package/mcp-memory-server/dist/node-cli.js +84 -0
  68. package/mcp-memory-server/dist/node-schema.js +170 -0
  69. package/mcp-memory-server/dist/node-store.js +455 -0
  70. package/mcp-memory-server/dist/note-cli.js +235 -0
  71. package/mcp-memory-server/dist/note-distiller.js +177 -0
  72. package/mcp-memory-server/dist/note-enrichment.js +93 -0
  73. package/mcp-memory-server/dist/note-schema.js +4 -0
  74. package/mcp-memory-server/dist/note-store.js +985 -0
  75. package/mcp-memory-server/dist/trajectory-cli.js +141 -0
  76. package/mcp-memory-server/dist/trajectory-normalize.js +448 -0
  77. package/mcp-memory-server/dist/trajectory-redaction.js +155 -0
  78. package/mcp-memory-server/dist/trajectory-schema.js +73 -0
  79. package/mcp-memory-server/dist/trajectory-store.js +339 -0
  80. package/mcp-memory-server/package.json +24 -2
  81. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json +9 -0
  82. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json +9 -0
  83. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json +6 -0
  84. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json +119 -0
  85. package/mcp-memory-server/scripts/fixtures/compaction-unknown-version.json +17 -0
  86. package/opencode/capability-contract/plugins/capability-command.ts +319 -0
  87. package/opencode/command/graphify.md +25 -14
  88. package/opencode/plugins/memory-capture.ts +76 -10
  89. package/opencode/plugins/memory-wakeup.ts +58 -6
  90. package/package.json +8 -4
  91. package/pi/extensions/cairnkeep-trajectory.ts +55 -0
  92. package/schemas/artifact.schema.json +133 -0
  93. package/schemas/capability-callback.schema.json +68 -0
  94. package/schemas/capability-contract.schema.json +126 -0
  95. package/schemas/eval-adapter.schema.json +43 -0
  96. package/schemas/eval-protocol.schema.json +114 -0
  97. package/schemas/eval-report.schema.json +423 -0
  98. package/schemas/eval-task-set.schema.json +114 -0
  99. package/schemas/memory-node.schema.json +90 -0
  100. package/schemas/note.schema.json +112 -0
  101. package/schemas/trajectory-redaction.schema.json +25 -0
  102. package/schemas/trajectory.schema.json +82 -0
  103. package/scripts/bootstrap.sh +6 -0
  104. package/scripts/completion.sh +119 -5
  105. package/scripts/doctor.sh +284 -0
  106. package/scripts/fake-eval-adapter.mjs +120 -0
  107. package/scripts/fixtures/capability-harness-contracts.json +408 -0
  108. package/scripts/install-audit-timer.sh +3 -1
  109. package/scripts/lib/capability-opencode-plugin-harness.mjs +291 -0
  110. package/scripts/lib/trajectory-opencode-plugin-harness.mjs +25 -0
  111. package/scripts/lib/trajectory-pi-extension-harness.mjs +42 -0
  112. package/scripts/memory-wiki-audit.sh +49 -0
  113. package/scripts/smoke-bash32.sh +9 -0
  114. package/scripts/sync-claude-assets.sh +99 -6
  115. package/scripts/sync-opencode-graphify-assets.sh +24 -3
  116. package/scripts/sync-opencode-plugin-assets.sh +31 -5
  117. package/scripts/sync-opencode-security-assets.sh +24 -3
  118. package/scripts/sync-opencode-wiki-assets.sh +24 -3
  119. package/scripts/sync-pi-assets.sh +72 -0
  120. package/scripts/test-audit-timer.sh +98 -0
  121. package/scripts/test-bootstrap-untracked.sh +49 -0
  122. package/scripts/test-cli-dispatch.sh +41 -2
  123. package/scripts/test-compaction-hooks.sh +495 -0
  124. package/scripts/test-completion.sh +77 -0
  125. package/scripts/test-doctor.sh +348 -1
  126. package/scripts/test-eval-cli.sh +175 -0
  127. package/scripts/test-launcher-hooks.sh +630 -3
  128. package/scripts/test-learning-docs.sh +20 -4
  129. package/scripts/test-notes-cli.sh +114 -0
  130. package/scripts/test-package-install.sh +254 -0
  131. package/scripts/test-phase16-node-lifecycle.sh +50 -0
  132. package/scripts/test-phase17-artifact-lifecycle.sh +370 -0
  133. package/scripts/test-phase18-capability-lifecycle.sh +504 -0
  134. package/scripts/test-phase18-harness-boundary.sh +1060 -0
  135. package/scripts/test-phase19-eval-lifecycle.sh +1015 -0
  136. package/scripts/test-trajectory-cli.sh +87 -0
  137. package/scripts/test-trajectory-hooks.sh +142 -0
  138. package/scripts/test-uninstall.sh +231 -5
  139. package/scripts/uninstall.sh +38 -5
  140. package/scripts/verify-dependency-security.mjs +121 -0
  141. package/scripts/verify-docs-parity.sh +527 -1
  142. package/scripts/verify-phase18-runtime-evidence.mjs +895 -0
  143. package/scripts/verify-phase19-runtime-evidence.mjs +947 -0
  144. package/templates/capabilities.json.template +7 -0
  145. package/templates/env.example.template +51 -0
  146. package/templates/memory-wiki-audit.service.template +1 -1
  147. package/templates/start-claude.sh.template +36 -0
  148. package/templates/start-kimi.sh.template +39 -0
  149. package/templates/start-opencode.sh.template +47 -5
  150. package/templates/start-pi.sh.template +30 -0
  151. package/templates/trajectory-redaction.json.template +4 -0
package/docs/operating.md CHANGED
@@ -16,8 +16,9 @@ installs. This guide covers all three in order.
16
16
 
17
17
  ## Prerequisites
18
18
 
19
- - Node.js 22 or newer (for the memory server) and a supported harness: Claude
20
- Code or OpenCode.
19
+ - Node.js 22 or newer (for the memory server) and a supported harness. Claude
20
+ Code and OpenCode receive the full operating layer. Kimi Code receives the
21
+ memory MCP and launcher. Pi receives its native trajectory adapter.
21
22
  - Optional: the `sqlite3` CLI for `cairn memory export`. Runtime memory and
22
23
  `cairn memory import` do not require it.
23
24
  - Optional: an OpenAI-compatible LLM endpoint for memory extraction and
@@ -93,11 +94,15 @@ Step 3 installs into `~/.claude` (override with `CLAUDE_CONFIG_DIR` or
93
94
  `repo-review`, `graphify`, `context-explore`
94
95
  - **7 agents** → `agents/`: `code-reviewer`, the three `security-*` agents, and
95
96
  the three `wiki-*` agents
96
- - **3 hooks** → `hooks/`, registered in `settings.json`:
97
+ - **5 hooks** → `hooks/`, registered in `settings.json`:
97
98
  - `memory-wakeup.sh` on **SessionStart** — surfaces AgentFS memory + wiki index
98
99
  - `memory-capture.sh` on **SessionEnd** — extracts memory candidates to staging
99
100
  - `memory-recall.sh` on **PreToolUse** (Edit/Write/MultiEdit) — injects
100
101
  file-specific memory before an edit
102
+ - `context-explore-pretask.sh` on **UserPromptSubmit** — inert unless the
103
+ separate context-exploration opt-in is enabled
104
+ - `compaction-capture.sh` on **PostCompact** — inert unless local compaction
105
+ continuity is enabled
101
106
  - **scaffold templates** → `templates/`, used by `/security-audit` and `/wiki-*`
102
107
 
103
108
  Re-running `sync-claude-assets.sh --apply` is idempotent; use `--check` to see
@@ -121,7 +126,7 @@ OpenCode is a secondary path. Steps 1, 4, and 5 are identical (use
121
126
  The operating-layer assets are installed by topic-specific scripts:
122
127
 
123
128
  ```bash
124
- scripts/sync-opencode-plugin-assets.sh --apply # memory-wakeup plugin
129
+ scripts/sync-opencode-plugin-assets.sh --apply # memory wakeup/capture/recall plugins
125
130
  scripts/sync-opencode-memory-assets.sh --apply # memory-sync/review + code-review
126
131
  scripts/sync-opencode-wiki-assets.sh --apply # wiki commands/agents/workflows
127
132
  scripts/sync-opencode-security-assets.sh --apply # security-audit chain
@@ -153,6 +158,64 @@ Model selection stays operator-env-driven through the existing
153
158
  variables (see "Configuration" below) — the harness commits no default model
154
159
  and carries no known-good allowlist.
155
160
 
161
+ ## Setup order (Kimi Code)
162
+
163
+ Kimi Code can use the Cairnkeep memory MCP and the generated project launcher.
164
+ It does not yet receive Cairnkeep-specific skills, commands, or hooks.
165
+
166
+ For local stdio memory, create `.kimi-code/mcp.json` in the project:
167
+
168
+ ```json
169
+ {
170
+ "mcpServers": {
171
+ "cairn-memory": {
172
+ "command": "cairn",
173
+ "args": ["memory-server"]
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ Then launch from the project root with `./.ai/start-kimi.sh`. The launcher
180
+ loads `.ai/.env`, runs the optional pre-launch and post-exit hooks, and forwards
181
+ all arguments to `kimi`.
182
+
183
+ Remote HTTP configuration needs one extra precaution: Kimi validates `url` as
184
+ a literal URL and does not expand `${CAIRN_MEMORY_REMOTE_URL}` there. Put the
185
+ resolved URL in a private `.kimi-code/mcp.json` and use
186
+ `bearerTokenEnvVar: "CAIRN_MEMORY_HTTP_TOKEN"` so the token value remains in
187
+ the environment. A repository-root `.mcp.json` must also be valid because Kimi
188
+ parses it before applying its private override. See
189
+ [Harness compatibility](harness-compatibility.md#kimi-code) for the complete
190
+ configuration and trust-boundary guidance.
191
+
192
+ ## Setup order (Pi)
193
+
194
+ Pi trajectory capture uses the same project scaffold and store, but its source
195
+ is a native TypeScript extension installed into Pi's agent root:
196
+
197
+ ```bash
198
+ npm install -g @cairnkeep/cli
199
+ cairn sync-pi --apply # default: ~/.pi/agent
200
+ cairn bootstrap /path/to/project
201
+ cp /path/to/project/.ai/env.example /path/to/project/.ai/.env
202
+ cd /path/to/project && cairn doctor && ./.ai/start-pi.sh
203
+ ```
204
+
205
+ This command does not install an MCP bridge. Cairnkeep does not own or select a
206
+ Pi bridge; if you want `cairn memory-server` tools inside Pi as well, configure
207
+ a user-chosen Pi extension/bridge separately.
208
+
209
+ Use `cairn sync-pi --check` to report drift without writing, or
210
+ `--live-root DIR` to target an isolated Pi agent root. The command owns exactly
211
+ `extensions/cairnkeep-trajectory.ts`; `cairn uninstall --pi-live-root DIR`
212
+ removes that file backup-first and leaves every other Pi asset untouched.
213
+
214
+ The extension listens for Pi's native `session_shutdown` event and reads only
215
+ the active branch from the read-only session manager. It returns before doing
216
+ that work unless `CAIRN_TRAJECTORY_CAPTURE` is explicitly enabled. Capture is
217
+ local, fail-open, and capped at three seconds so it cannot hold Pi open.
218
+
156
219
  Before burning a multi-run soak, a preflight probe drives one real tool call
157
220
  and fails fast with a trait-named message if the configured model is not
158
221
  tool-call-reliable. The harness keeps a three-tier speed structure:
@@ -175,6 +238,38 @@ vendor or host.
175
238
  | `CAIRN_MEMORY_EMBEDDING_MODEL` | Embedding model name (required for semantic search) |
176
239
  | `CAIRN_MEMORY_EMBEDDING_TIMEOUT_MS` | Embedding request timeout before substring fallback (default `15000`) |
177
240
  | `CAIRN_AGENTFS_BASE_DIR` | Server-side base dir for named/global memory scopes (default `~/.cairnkeep`); it does not affect `project` scope |
241
+ | `CAIRN_TRAJECTORY_CAPTURE` | Opt in to local structured session capture (`1`, `true`, `yes`, or `on`; unset/default → no capture work) |
242
+ | `CAIRN_TRAJECTORY_SESSION_MAX_BYTES` | Maximum serialized bytes per session (default `5242880`, 5 MiB; minimum 1024) |
243
+ | `CAIRN_TRAJECTORY_STORE_MAX_BYTES` | Logical local trajectory budget (default `268435456`, 256 MiB; must be at least the session maximum) |
244
+ | `CAIRN_TRAJECTORY_RETENTION_DAYS` | Age retention applied on capture/prune (default `30`; `0` removes sessions once they are older than the current instant) |
245
+ | `CAIRN_TRAJECTORY_REDACTION_FILE` | Optional redaction JSON path contained by the project (default `.ai/trajectory-redaction.json` when that file exists) |
246
+ | `CAIRN_REDACTION_FILE` | Optional project-contained redaction JSON shared by trajectories and artifacts; takes precedence over `CAIRN_TRAJECTORY_REDACTION_FILE` |
247
+ | `CAIRN_COMPACTION_CAPTURE` | Opt in to supported local compaction capture and automatic structured recovery (off by default; unset means zero new work) |
248
+ | `CAIRN_ARTIFACT_STORE` | Expose the four local stdio artifact tools (off by default; independent of compaction capture) |
249
+ | `CAIRN_ARTIFACT_HTTP` | Separately expose artifact tools over authenticated HTTP (off by default; also requires `CAIRN_ARTIFACT_STORE`) |
250
+ | `CAIRN_ARTIFACT_MAX_BYTES` | Per-artifact stored-byte cap (default `1048576`, minimum `1024`) |
251
+ | `CAIRN_ARTIFACT_SESSION_MAX_BYTES` | Per-session logical-byte cap (default `16777216`; at least the artifact cap) |
252
+ | `CAIRN_ARTIFACT_STORE_MAX_BYTES` | Total logical-byte cap (default `268435456`; at least the session cap) |
253
+ | `CAIRN_ARTIFACT_RETENTION_DAYS` | Age retention in days (default `30`, minimum `0`) |
254
+ | `CAIRN_COMPACTION_MAX_REVISIONS` | Immutable compaction revisions retained per session (default `8`, minimum `1`) |
255
+ | `CAIRN_ARTIFACT_GENERATED_FILE_SNAPSHOT_MAX_BYTES` | Generated-file inline snapshot cap (default `262144`; effective cap is the lower of this and the artifact cap) |
256
+ | `CAIRN_NOTE_DISTILLATION` | Master opt-in for local one-shot/scheduled hindsight distillation and exact lookup (unset/default → disabled before I/O) |
257
+ | `CAIRN_NOTE_ENRICHMENT` | Separate opt-in for provider prose enrichment; credentials alone never enable it |
258
+ | `CAIRN_NOTE_ENRICHMENT_MODEL` | Explicit OpenAI-compatible chat model for note enrichment (no default) |
259
+ | `CAIRN_NOTE_ENRICHMENT_TIMEOUT_MS` | Enrichment timeout in milliseconds (default `15000`, allowed `100`–`120000`) |
260
+ | `CAIRN_TYPED_MEMORY_NODES` | Add typed metadata, filters, logical note address spaces, and `memory_import` (unset/default off; restart after changing) |
261
+ | `CAIRN_CAPABILITY_CONTRACT` | Enable the managed eight-capability contract (unset/default off) |
262
+ | `CAIRN_CAPABILITY_LOGGING` | Strict per-process callback-logging override (`1`/`0`; compatibility default off) |
263
+ | `CAIRN_CAPABILITY_MEMORY_WRITE` | Strict per-process override for `memory.write` |
264
+ | `CAIRN_CAPABILITY_MEMORY_SEARCH` | Strict per-process override for `memory.search` |
265
+ | `CAIRN_CAPABILITY_NOTES_DISTILL` | Strict per-process override for `notes.distill` |
266
+ | `CAIRN_CAPABILITY_WIKI` | Strict per-process override for `wiki` |
267
+ | `CAIRN_CAPABILITY_GRAPH` | Strict per-process override for `graph` |
268
+ | `CAIRN_CAPABILITY_SECURITY_AUDIT` | Strict per-process override for `security.audit` |
269
+ | `CAIRN_CAPABILITY_ROUTE_CHECK` | Strict per-process override for `route.check` |
270
+ | `CAIRN_CAPABILITY_CONTEXT_EXPLORE` | Strict per-process override for `context.explore` |
271
+ | `CAIRN_EVAL` | Opt in to the local evaluation coordinator (`1`, `true`, `yes`, or `on`; unset/default → stable disabled result before input reads, writes, or adapter execution) |
272
+ | `CAIRN_HARNESS_STATE_DIR` | Optional absolute local root for recoverable native capability leases (default `${XDG_STATE_HOME:-~/.local/state}/cairn/harness`) |
178
273
  | `CAIRN_GIT_PROVIDER` | Git host for collaboration commands: `github`\|`gitlab`\|`codeberg`\|`forgejo`\|`none`. See [git-providers.md](git-providers.md) |
179
274
  | `CAIRN_ROUTE_ENDPOINT` | Base URL of an already-running token-miser routing/tiering proxy (unset → the `route_check` tool is inert) |
180
275
  | `CAIRN_EXPLORE_BINARY` | Absolute path to the `token_miser` binary used by `context_explore` (unset → the tool throws at call time) |
@@ -187,6 +282,208 @@ vendor or host.
187
282
  | `CAIRN_ANYTHINGLLM_PROJECTS_FILE` | Override path to the bundled sync script's project configuration |
188
283
  | `CAIRN_ANYTHINGLLM_STATE_FILE` | Override path to the bundled sync script's incremental state |
189
284
 
285
+ ### Managed capability contract (opt-in)
286
+
287
+ The managed contract is a narrow state and measurement boundary around eight
288
+ existing owners. It adds no runtime dependency and does not move business logic
289
+ from the MCP server, note CLI/timer, installed workflows, or the external
290
+ token-miser delegates.
291
+
292
+ The canonical registry is exactly:
293
+
294
+ | ID | Kind | Owner | Compatibility default | Environment override | Restart required |
295
+ |---|---|---|---:|---|---:|
296
+ | `memory.write` | `mcp-tool` | `mcp-memory-server:index#memory_write` | on | `CAIRN_CAPABILITY_MEMORY_WRITE` | yes |
297
+ | `memory.search` | `mcp-tool` | `mcp-memory-server:index#memory_search` | on | `CAIRN_CAPABILITY_MEMORY_SEARCH` | yes |
298
+ | `notes.distill` | `offline-job` | `mcp-memory-server:note-cli#distill` | `CAIRN_NOTE_DISTILLATION`, otherwise off | `CAIRN_CAPABILITY_NOTES_DISTILL` | no |
299
+ | `wiki` | `operating-workflow` | `operating-layer:wiki` | on | `CAIRN_CAPABILITY_WIKI` | no |
300
+ | `graph` | `operating-workflow` | `operating-layer:graph` | `.planning/config.json` `graphify.enabled`, otherwise off | `CAIRN_CAPABILITY_GRAPH` | no |
301
+ | `security.audit` | `operating-workflow` | `operating-layer:security-audit` | on | `CAIRN_CAPABILITY_SECURITY_AUDIT` | no |
302
+ | `route.check` | `mcp-tool` | `mcp-memory-server:index#route_check` | on | `CAIRN_CAPABILITY_ROUTE_CHECK` | yes |
303
+ | `context.explore` | `mcp-tool` | `mcp-memory-server:index#context_explore` | on | `CAIRN_CAPABILITY_CONTEXT_EXPLORE` | yes |
304
+
305
+ `CAIRN_CAPABILITY_CONTRACT` is the default-off rollout gate. Master off is exact
306
+ legacy behavior: the launchers and sync commands preserve the legacy installed
307
+ assets byte-for-byte, install and invoke no capability hook or plugin, introduce
308
+ no capability block, and create no capability measurement state. Master on is
309
+ not enough to modify the normal harness root. The launcher must also select the
310
+ explicit `capability-overlay` mode; only that two-factor path renders the
311
+ isolated `.ai/capability-contract/` root and installs its native hooks/plugins.
312
+ Normal installed assets remain in place, and toggling never deletes
313
+ configuration, retained data, or installed files.
314
+
315
+ Bootstrap creates a mode-`0600` `.ai/capabilities.json` only if it is absent:
316
+
317
+ ```json
318
+ {
319
+ "schema_version": 1,
320
+ "capabilities": {},
321
+ "logging": { "callbacks": false }
322
+ }
323
+ ```
324
+
325
+ Use the managed CLI; do not hand-edit the file:
326
+
327
+ ```bash
328
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities list
329
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities status
330
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities status --json
331
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities enable wiki
332
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities disable memory.search
333
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities reset memory.search
334
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities reset --all
335
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities logging enable
336
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities logging disable
337
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities logging reset
338
+ ```
339
+
340
+ Precedence is strict: the derived per-capability environment value (or
341
+ `CAIRN_CAPABILITY_LOGGING`) wins over the project value, which wins over the
342
+ compatibility resolver. Every derived key uses the `CAIRN_CAPABILITY_` prefix
343
+ followed by the uppercased ID with dots replaced by underscores. Accepted
344
+ values are `1|true|yes|on` and
345
+ `0|false|no|off`, case-insensitively. An invalid top-level value is reported
346
+ with a fixed value-free issue and falls directly back to compatibility; it does
347
+ not reveal the raw value or consult a lower project override. Unknown IDs and
348
+ invalid project rows are likewise reported without their values. Each bad row
349
+ falls back independently, so it cannot disable another capability or the
350
+ server.
351
+
352
+ `reset` removes the explicit override; `reset --all` clears every capability
353
+ and logging override. The returned schema-v1 status orders all eight rows and
354
+ reports `enabled`, `source` (`environment`, `project`, or `compatibility`),
355
+ `restart_required`, contract/logging state, fixed issue codes, and the SHA-256
356
+ `configuration_digest` of the canonical effective snapshot. The digest is
357
+ evidence, not a signature or secret. MCP state is resolved once before server
358
+ registration, so changes to `memory.write`, `memory.search`, `route.check`, or
359
+ `context.explore` require a new server start; a disabled tool is omitted from
360
+ `tools/list`. Notes and operating workflows resolve current state for the next
361
+ invocation. Disabled notes retain the existing structured `enabled: false`
362
+ offline result.
363
+
364
+ Wiki covers ingest, query, and lint; graph covers its command family; security
365
+ audit covers its command and workflow family. There are no subcommand-level
366
+ switches. The four enabled MCP tools retain their exact names, schemas,
367
+ responses, timeout/error behavior, and owners. In particular, `route_check`
368
+ and `context_explore` remain thin token-miser delegates; Cairnkeep does not gain
369
+ endpoint, model, tier, sandbox, or inference-loop ownership.
370
+
371
+ The five operating command surfaces are still `wiki-ingest`, `wiki-query`,
372
+ `wiki-lint`, `graphify`, and `security-audit`. Their installed commands, agents,
373
+ and workflows remain the owners described by D-10, D-12, and D-16; the native
374
+ boundary admits or blocks an invocation and settles evidence, but does not move
375
+ or duplicate owner logic.
376
+
377
+ #### Native operating lifecycle
378
+
379
+ Claude Code uses two native hooks. `capability-command-start.sh` runs on
380
+ `UserPromptExpansion`, before command or MCP-prompt expansion can enter owner
381
+ I/O. It validates the exact target-command event and binds the canonical
382
+ project root from the event to the process root. The coordinator then creates
383
+ an issued recoverable lease for an eligible measured invocation. That validated
384
+ project identity and lease are immutable for the invocation: terminal events
385
+ carry neither a replacement project path nor a caller-selected handle.
386
+ `capability-command-finish.sh` maps `Stop` to success and maps `StopFailure`
387
+ immediately to an error terminal; error settlement is not deferred to
388
+ `SessionEnd`. `SessionEnd` performs abandonment cleanup only for unfinished
389
+ leases, while `CwdChanged` updates lifecycle state without rebinding the
390
+ start-time project.
391
+
392
+ OpenCode uses the pinned native plugin for OpenCode 1.17.20.
393
+ `command.execute.before` validates the target command, session, output shape,
394
+ and the one canonical project identity supplied at plugin initialization before
395
+ expansion. `session.idle` and `session.status:idle` settle success,
396
+ `session.error` settles error, and `session.deleted` performs abandonment
397
+ cleanup only for an unfinished invocation. Plugin disposal likewise abandons
398
+ only unfinished leases. Both harnesses settle the issued lease atomically and
399
+ idempotently: one terminal can win, replay cannot add another final, and
400
+ crash-recovery cleanup cannot rewrite a settled outcome.
401
+
402
+ The three operating states are exact. With master off, no native capability
403
+ owner is installed or invoked and no measurement state exists. With master on
404
+ and a target disabled, the native boundary returns the fixed block before owner
405
+ I/O regardless of measurement consent. Only when all three consents are on may
406
+ that block settle exactly one D-25/D-26 value-free `disabled` final through an
407
+ issued lease; if either measurement consent is off, it blocks with no state.
408
+ With master on and a target enabled, either measurement consent being off
409
+ preserves owner execution unchanged and creates no measurement state.
410
+
411
+ Deterministic native-boundary tests prove pinned event shapes, admission before
412
+ owner delegation, terminal ordering, and overlay installation. They are not
413
+ exhaustive live real-owner evidence. Phase 18 also requires the complete live
414
+ eight-by-seven matrix: eight capabilities across seven target-disabled cells,
415
+ 56 genuine owner executions in total. Any missing, failed, or unavailable cell
416
+ keeps Phase 18 incomplete.
417
+
418
+ Callback logging is separately default-off. When all three consents are on
419
+ (`CAIRN_CAPABILITY_CONTRACT`, managed callback logging, and
420
+ `CAIRN_TRAJECTORY_CAPTURE`), the native boundary measures only the capability-owned
421
+ invocation. State is resolved first; the timer starts immediately before the
422
+ owned body and stops after its terminal outcome but before final presentation.
423
+ Discovery, configuration resolution, guard work, and unrelated harness overhead
424
+ are excluded. Logging is payload-free, local-only, final-only, and fail-open;
425
+ HTTP callbacks are never persisted. See the privacy and storage guides for the
426
+ exact record and retention contract.
427
+
428
+ Phase 19 must explicitly enable all eight capabilities to establish its
429
+ baseline before disabling one at a time. This contract records status/digest
430
+ and callback evidence only: it does not implement tasks, a runner, turns/tokens,
431
+ statistics, confidence intervals, or a quality/efficiency claim.
432
+
433
+ ### Typed memory nodes and note address spaces (opt-in)
434
+
435
+ Set `CAIRN_TYPED_MEMORY_NODES=1` in the memory-server process and restart it.
436
+ Tool schemas are selected when `createMemoryServer` constructs a stdio server
437
+ or authenticated HTTP session. With the flag unset, Cairnkeep retains the
438
+ existing 14-tool list, required inputs, structured response roots, raw KV
439
+ values, files, processes, network behavior, and output. Enabled mode adds
440
+ optional fields to the existing lifecycle tools and exactly `memory_import`;
441
+ it does not add `memory_edit`, `memory_update`, `memory_remove`, or duplicate
442
+ read/delete tools. Use `memory_supersede` for both content and metadata edits.
443
+
444
+ Types are `memory`, `knowledge`, `hindsight`, `shared`, `provenance`, or a
445
+ lowercase namespaced extension such as `team:runbook`. Tags are trimmed, ASCII
446
+ lowercased, normalize whitespace/underscore runs to one hyphen, collapse
447
+ hyphens, deduplicate, and sort. `memory_list` and `memory_search` accept
448
+ non-empty `node_types`, `tags_all`, and `tags_any`; these are hard eligibility
449
+ filters applied before `top_k`, cache lookup, or embedding. Case-insensitive
450
+ key matches and exact canonical tag/type matches sort before semantic results.
451
+ Endpoint failure or absent embedding configuration falls back to stable local
452
+ substring matching over key, value, type, and tags.
453
+
454
+ Enabled `memory_write` without any Phase 16 field preserves collision-safe
455
+ legacy overwrite behavior. Supplying `address_space`, `node_type`, `tags`, or
456
+ `note` makes it create-only on a differing live node and returns `CONFLICT`;
457
+ use `memory_supersede` to preserve the complete prior value/type/tags snapshot.
458
+ Delete also records the complete final snapshot, and recreation is allowed.
459
+
460
+ `memory_import` takes `schema_version: 1`, one concrete `scope`, optional
461
+ `address_space` (default `memory`), 1–256 `nodes`, optional `import_id`,
462
+ `dry_run`, and `conflict_policy` (default `reject`, or explicit `supersede`).
463
+ Each unique logical key is a contained relative slash path; values are limited
464
+ to 256 KiB UTF-8 each and 5 MiB per batch. The content digest excludes retry
465
+ controls, while `import_id` durably binds to it. Dry-run performs no create,
466
+ lock, cache, journal, or mutation and returns only the digest, sorted actions,
467
+ and counts. Committed and replay results likewise omit values. Stable failures
468
+ include `INVALID_SCHEMA`, `INVALID_SCOPE`, `INVALID_PATH`, `CONFLICT`,
469
+ `IMPORT_ID_REUSE`, and `UNSUPPORTED_TARGET`.
470
+
471
+ `project-notes` and `shared-notes` both require `scope: project`; no named
472
+ memory scope is reserved. Keys such as `knowledge/build-cache` are logical,
473
+ not client filesystem paths. Rich hindsight/shared/provenance imports require
474
+ the complete nested note record; knowledge values must still losslessly encode
475
+ the validated record. Canonical Markdown and its manifest remain authoritative.
476
+ Unmanaged collisions are refused, and bytes after the managed marker are
477
+ preserved exactly. Local stdio derives project identity from its working
478
+ directory. Authenticated HTTP project notes require validated
479
+ `X-Cairn-Project`; the bearer token still defines the trust domain.
480
+
481
+ An interrupted note mutation blocks later note writes with `RECOVERY_REQUIRED`.
482
+ `cairn doctor --repair` rolls prepared/committing journals back to verified
483
+ pre-images, but verifies and finalizes committed-before-cleanup journals without
484
+ undoing the completed operation. Unverifiable committed state remains failed
485
+ and untouched for backup-guided manual recovery.
486
+
190
487
  ### Domain knowledge (RAG via AnythingLLM, opt-in)
191
488
 
192
489
  `domain_knowledge_query` / `domain_knowledge_sync` bridge to an optional
@@ -293,6 +590,7 @@ is guarded and **fails closed**:
293
590
  | `CAIRN_MEMORY_HTTP_TOKEN` | **Required** in HTTP mode — clients send `Authorization: Bearer <token>`; the server refuses to start without it |
294
591
  | `CAIRN_MEMORY_HTTP_ALLOWED_ORIGINS` | Comma-separated browser origins allowed via CORS (default: none — no cross-origin access) |
295
592
  | `CAIRN_MEMORY_HTTP_ALLOWED_HOSTS` | Comma-separated allowed `Host` headers for DNS-rebinding protection (default: the bind host + `localhost` on the chosen port) |
593
+ | `CAIRN_ARTIFACT_HTTP` | Additional consent for artifact tools (default off; requires `CAIRN_ARTIFACT_STORE`, a valid `X-Cairn-Project`, bearer auth, and the normal Host/CORS checks) |
296
594
 
297
595
  Requests without a valid bearer token get `401`; requests with an unexpected
298
596
  `Host` header get `403`. Keep HTTP mode bound to `127.0.0.1` unless you have a
@@ -364,13 +662,14 @@ maintain a running install — without forking the core. All are opt-in.
364
662
 
365
663
  ### Launcher seams
366
664
 
367
- The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`) run three
368
- optional hooks around the harness, each a no-op when absent:
665
+ The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`,
666
+ `.ai/start-kimi.sh`, and `.ai/start-pi.sh`) run optional hooks around the
667
+ harness, each a no-op when absent:
369
668
 
370
669
  | Seam | When | Purpose |
371
670
  |---|---|---|
372
671
  | `.ai/pre-launch.sh` | sourced after `.env`, before launch | export env (e.g. a provider base URL / auth), refresh credentials, or abort by returning non-zero |
373
- | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered on the harness (`--settings` / `--config`); process env still wins over it |
672
+ | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered on Claude Code (`--settings`) or OpenCode (`OPENCODE_CONFIG`); Kimi and Pi leave the variable available to hooks but do not interpret it |
374
673
  | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
375
674
 
376
675
  A wrapper that needs a non-default provider drops a `pre-launch.sh` that renders
@@ -384,10 +683,265 @@ harness's remote HTTP registration. Unconfigured optional dependencies are
384
683
  skipped; it exits non-zero when the local server probe fails or a configured
385
684
  dependency (LLM/embedding endpoint, writable store) is unreachable.
386
685
 
686
+ It also checks an existing project-local trajectory database for SQLite
687
+ integrity, schema compatibility and index consistency. Because capture is
688
+ opt-in, a missing database is skipped. Metadata or indexes can be rebuilt from
689
+ valid full records only through the explicit repair operation:
690
+
387
691
  ```bash
388
692
  cd /path/to/project && cairn doctor
693
+ cd /path/to/project && cairn doctor --repair
694
+ ```
695
+
696
+ The same command checks `.agentfs/artifacts.db`. Absence is a healthy skip
697
+ because both producers are opt-in. A valid schema, full-record digest, index,
698
+ dedupe row, compaction pointer, and retention state passes. `--repair` may
699
+ rebuild only derived indexes/dedupe/pointers from valid authoritative records.
700
+ Unsupported schema, SQLite failure, invalid full records, or digest corruption
701
+ fails untouched with guidance to preserve the database before manual recovery.
702
+
703
+ ### Evaluation harness (opt-in)
704
+
705
+ `cairn eval` is a local, serial experiment coordinator. It is disabled unless
706
+ `CAIRN_EVAL` is explicitly truthy. The disabled path returns before reading a
707
+ task set or adapter configuration, creating a workspace or report, opening a
708
+ database, starting a subprocess, or making a network request. Enabling the
709
+ flag does not choose a harness, model, endpoint, or credential: inference
710
+ remains owned by the operator-configured adapter process.
711
+
712
+ Task sets and adapters are strict schema-v1 JSON. Each task names an immutable
713
+ source commit, fixed input, shell-free preparation and verification commands,
714
+ and limits. An adapter configuration is one program-plus-arguments array and
715
+ an exact turn-semantics ID plus definition. Cairnkeep sends one bounded JSON
716
+ request on stdin and accepts exactly one bounded, strict JSON observation on
717
+ stdout. The adapter may report terminal status, turns under its declared
718
+ semantics, independently optional token components and total, optional
719
+ currency-bearing cost, component identities, capability digest, and local
720
+ trajectory/artifact references. The independent task verifier—not the
721
+ adapter—assigns pass, fail, or unknown. Arbitrary adapter stderr remains live
722
+ operator diagnostics and is not copied into the report.
723
+
724
+ The request's `workspace_path` is the task's Cairnkeep project root for all
725
+ trajectory-producing harness work. An adapter that returns `trajectory_ref`
726
+ must run that work against the supplied root so the closed trajectory is in
727
+ that task's local store. The reference is a bounded session identifier, not a
728
+ filesystem locator. A task manifest with `workspace.path: "."` remains the
729
+ compatible repository-root case; there is no second trajectory-root field.
730
+
731
+ Validate before spending or mutating:
732
+
733
+ ```bash
734
+ export CAIRN_EVAL=1
735
+ cairn eval validate --task-set tasks.json --adapter adapter.json \
736
+ --output .agentfs/eval/experiments --json
737
+
738
+ # Two serial observations per task and repetition: Run 1, then fresh Run 2.
739
+ cairn eval run --task-set tasks.json --adapter adapter.json \
740
+ --output .agentfs/eval/experiments --repetitions 1 --seed trial-1 --yes --json
741
+
742
+ # Four serial observations per task and repetition: all-on and one-off,
743
+ # with Run 1 and Run 2 in each arm. Only one capability may be disabled.
744
+ cairn eval ablate --disable memory.search --task-set tasks.json \
745
+ --adapter adapter.json --output .agentfs/eval/experiments --seed trial-1
746
+ # Inspect the printed invocation estimate and both configuration digests, then
747
+ # repeat with --yes to confirm execution.
748
+
749
+ cairn eval report --experiment EXPERIMENT-ID --json
750
+ cairn eval prune --older-than-days 30 --dry-run --json
751
+ cairn eval prune --older-than-days 30 --json
752
+ cairn eval delete --experiment EXPERIMENT-ID --dry-run --json
753
+ cairn eval delete --experiment EXPERIMENT-ID --json
754
+ ```
755
+
756
+ `validate` resolves task/adapter bytes and digests, immutable revisions,
757
+ commands, limits, output containment, seeds, and the complete deterministic
758
+ schedule without invoking the adapter. `run` requires `--yes` and prints its
759
+ serial invocation estimate. `ablate` first prints the estimate, the explicit
760
+ all-eight-enabled baseline, the exactly-one-disabled treatment, and their
761
+ configuration digests; execution likewise requires `--yes`. There are no
762
+ automatic retries: repetitions and paired seeds are explicit experiment
763
+ inputs.
764
+
765
+ The packaged fake workflow uses `examples/eval/task-set.json` and
766
+ `examples/eval/adapter.json` with the same `validate`, `run`, `report`,
767
+ `prune`, and `delete` commands. Its manifest must match the package-owned ID,
768
+ version, canonical bytes, and digest. It is deterministic and network-free,
769
+ but proves framework behavior only; it is not live performance evidence.
770
+
771
+ Run 1 starts every task from a fresh workspace without evaluation notes. After
772
+ a closed trajectory, the existing offline note distiller may create an
773
+ experiment-owned immutable snapshot. Run 2 uses another fresh workspace and
774
+ can see only its own task's snapshot. Tasks with `no_notes`, failed, or skipped
775
+ distillation still run and remain visible. Reports show the full committed
776
+ population and the note-eligible subset, plus executed, verified, paired, and
777
+ missing populations and reason-coded missingness.
778
+
779
+ Turns are paired or aggregated only when exact turn-semantics IDs match.
780
+ Token fields remain independent and totals are never inferred. Verified pass
781
+ rates exclude unknown verifier outcomes while retaining them in denominators
782
+ and missingness tables. Task-clustered paired bootstrap intervals use recorded
783
+ seeds and streams; fewer than two valid pairs has no interval and fewer than
784
+ 20 emits a small-sample warning. Results are estimates and may be
785
+ inconclusive—reports do not make causal, significance, quality, or efficiency
786
+ claims.
787
+
788
+ SIGINT or SIGTERM stops schedule admission, terminates the active adapter,
789
+ checkpoints a `cancelled` observation, waits for bounded cleanup, and retains
790
+ an inspectable partial report. On POSIX systems the runner targets the child
791
+ process group with TERM then bounded KILL escalation. Windows descendant-tree
792
+ termination is not guaranteed and is not a supported evaluation claim.
793
+
794
+ See [storage](storage.md#evaluation-report-and-note-snapshot-storage) for local
795
+ retention/removal and [privacy](privacy-and-data-flow.md#evaluation-adapter-and-report-flow)
796
+ for the exact request, observation, environment, and persistence boundaries.
797
+
798
+ ### Structured session trajectories (opt-in)
799
+
800
+ Trajectory capture is disabled by default. To enable it for a launched Claude
801
+ Code, OpenCode, or Pi session, set the flag in the project's private `.ai/.env`:
802
+
803
+ ```bash
804
+ CAIRN_TRAJECTORY_CAPTURE=1
805
+ ```
806
+
807
+ The Claude Code SessionEnd hook, OpenCode session-idle plugin, or Pi
808
+ `session_shutdown` extension then normalizes the closed session, redacts it,
809
+ and writes it locally without a model, API key, or remote request. Inspect and
810
+ manage the result from the project root:
811
+
812
+ ```bash
813
+ cairn trajectory list
814
+ cairn trajectory show <session-id-or-unambiguous-prefix>
815
+ cairn trajectory prune --dry-run
816
+ cairn trajectory prune
817
+ ```
818
+
819
+ All commands accept `--json`; `--dry-run` applies to `prune`. Bootstrap creates
820
+ `.ai/trajectory-redaction.json` with an empty custom-pattern list but does not
821
+ enable capture. Add bounded regular-expression entries there only for
822
+ project-specific secrets; built-in credential patterns always apply. See
823
+ [Privacy and data flow](privacy-and-data-flow.md) for the exact capture boundary
824
+ and [Memory storage and deployment](storage.md) for retention and backup.
825
+
826
+ ### Compaction continuity and artifacts (opt-in)
827
+
828
+ The two flags are independent and off by default. Enable either in the private
829
+ project `.ai/.env`, sync the harness assets, and restart both the harness and
830
+ the MCP server when changing tool registration:
831
+
832
+ ```bash
833
+ cd /path/to/project
834
+ printf '%s\n' 'CAIRN_COMPACTION_CAPTURE=1' >> .ai/.env
835
+ printf '%s\n' 'CAIRN_ARTIFACT_STORE=1' >> .ai/.env
836
+ cairn sync --apply # Claude Code PostCompact + SessionStart
837
+ # OpenCode: re-run scripts/sync-opencode-plugin-assets.sh --apply from a clone
838
+ # Restart the harness and cairn-memory after changing flags.
839
+
840
+ # Disable: remove/comment both lines, sync again, and restart.
841
+ ```
842
+
843
+ Compaction continuity accepts only the pinned Claude Code `PostCompact`
844
+ 2.1.219 and 2.1.220 payloads and OpenCode `session.compacted` 1.17.20 payload.
845
+ The Claude hook resolves the local CLI version and passes that exact version to
846
+ the normalizer. Unknown or malformed versions fail open, retain none of the
847
+ unknown payload, and leave only a
848
+ bounded value-free doctor diagnostic. The flag is checked before parsing,
849
+ SDK, subprocess, filesystem, database, network, stdout, stderr, or injection
850
+ work. Capture forwards the harness-produced summary unchanged to the local
851
+ normalizer; it never generates a summary, changes a prompt, or triggers
852
+ compaction.
853
+
854
+ Automatic recovery prefers the exact current/resumed harness session. When a
855
+ fresh session has no match, it chooses the newest valid project compaction. It
856
+ injects provenance, revision, time, age, completeness, and only structured
857
+ goals/decisions/TODOs/critical errors. Old state is still shown with a stale
858
+ warning to validate it against the repository. The redacted raw summary is
859
+ available only through explicit `artifact_read` or `cairn artifact show`.
860
+
861
+ `CAIRN_ARTIFACT_STORE=1` registers exactly `artifact_write`, `artifact_read`,
862
+ `artifact_list`, and `artifact_delete`. The first schema supports exactly
863
+ `compaction_summary`, `diff`, `test_output`, and `generated_file`. Writes are
864
+ bounded inline values; generated-file paths are labels and are never read.
865
+ Operator commands remain available for already-retained local data:
866
+
867
+ ```bash
868
+ cairn artifact list --kind compaction_summary --session SESSION-REF --json
869
+ cairn artifact show ARTIFACT-ID --json
870
+ cairn artifact delete ARTIFACT-ID --dry-run --json
871
+ cairn artifact delete ARTIFACT-ID --json
872
+ cairn artifact prune --dry-run --json
873
+ cairn artifact prune --dry-run --include-protected --json
389
874
  ```
390
875
 
876
+ Defaults are 1 MiB per artifact, 16 MiB per session, 256 MiB total, 30 days,
877
+ eight compaction revisions, and a 256 KiB generated-file snapshot cap. Pruning
878
+ is oldest-first after age/revision eligibility and protects the newest valid
879
+ project compaction unless `--include-protected` or explicit delete is used.
880
+ Hard delete removes the body and derived rows with no tombstone.
881
+
882
+ No key, model, endpoint, or network is required. Stdio access needs only
883
+ `CAIRN_ARTIFACT_STORE`. HTTP requires double consent—both artifact flags—plus
884
+ the existing bearer token, allowed `Host`, CORS policy, and a valid
885
+ `X-Cairn-Project`; the latter derives an isolated server-side artifact root.
886
+ Artifact HTTP does not expose trajectories. See [storage](storage.md#artifact-storage)
887
+ and [privacy](privacy-and-data-flow.md#compaction-and-artifact-flows).
888
+
889
+ ### Hindsight notes (opt-in)
890
+
891
+ Note distillation is a separate asynchronous/one-shot capability. It never runs
892
+ inside SessionEnd, session-idle, `session_shutdown`, or any other online agent
893
+ callback. It consumes only closed, schema-validated trajectories already
894
+ redacted by the capture path. Enable the master flag in the process that runs
895
+ the command:
896
+
897
+ ```bash
898
+ export CAIRN_NOTE_DISTILLATION=1
899
+
900
+ # Incremental current project, or one exact session for deterministic replay
901
+ cd /path/to/project
902
+ cairn notes distill --json
903
+ cairn notes distill --session SESSION-ID --json
904
+
905
+ # Scheduled/manual sweep below one explicit PARA root
906
+ cairn notes distill --all-projects --para-root "$HOME/PARA" --json
907
+
908
+ # Exact signature lookup; stdin avoids quoting a real multiline stack trace
909
+ printf '%s\n' 'TypeError: cache closed' ' at loadCache (/tmp/src/cache.ts:44:2)' \
910
+ | cairn notes search-error --project /path/to/project --json
911
+
912
+ # Validate/rebuild generated indexes, or explicitly promote corroborated notes
913
+ cairn notes doctor --json
914
+ cairn notes doctor --repair --json
915
+ cairn notes promote NOTE-ID --with CORROBORATING-NOTE-ID --confirm --json
916
+ ```
917
+
918
+ The hierarchy is `${CAIRN_AGENTFS_BASE_DIR:-~/.cairnkeep}/notes/README.md`,
919
+ `shared/`, and `projects/<slug--stable-id>/{README.md,knowledge/,hindsight/}`.
920
+ Each Markdown leaf has validated `id`, `title`, `description`, `keywords`,
921
+ `node_type`, and `tags` front matter. Hindsight nodes add a deterministic
922
+ signature and `unresolved`, `resolved`, or `abandoned` lifecycle. A later
923
+ recurrence reopens the same stable node. Generated content is enclosed by
924
+ `cairnkeep:managed:v1` markers; text after that block is preserved. An existing
925
+ unmarked target is treated as a collision and is never overwritten.
926
+
927
+ JSON distillation output separates `created`, `updated`, `already_processed`,
928
+ `enrichment_skipped`, `enrichment_failed`, and session-level `failed` results.
929
+ Project locks reject concurrent writers. Exact lookup normalizes the message,
930
+ causal stack, and component with the same versioned implementation used by the
931
+ index; it runs without embeddings or network access. Promotion requires two
932
+ compatible notes from distinct projects plus `--confirm`, creates one shared
933
+ canonical body, and converts each project note into a provenance reference.
934
+
935
+ Optional prose enrichment requires all of the master flag,
936
+ `CAIRN_NOTE_ENRICHMENT=1`, `CAIRN_LLM_API_KEY`, `CAIRN_LLM_API_URL`, and an
937
+ explicit `CAIRN_NOTE_ENRICHMENT_MODEL`. It sends bounded already-redacted
938
+ evidence to `{CAIRN_LLM_API_URL}/chat/completions`, labels returned prose as
939
+ non-authoritative, and cannot change signature, lifecycle, or provenance. Any
940
+ missing configuration, malformed response, timeout, or provider failure leaves
941
+ the deterministic note usable and is reported as skipped/failed. Review the
942
+ [privacy data flow](privacy-and-data-flow.md#hindsight-note-distillation) before
943
+ enabling it.
944
+
391
945
  ### `cairn memory export|import|path`
392
946
 
393
947
  Relocate named/global memory (one SQLite `.db` per scope under
@@ -414,3 +968,9 @@ cairn audit-timer --on-calendar daily # install + enable the timer
414
968
  cairn audit-timer --render-only ./units # just render the unit files
415
969
  # no systemd? cron: @daily .../scripts/memory-wiki-audit.sh --para-root "$HOME/PARA" --report ...
416
970
  ```
971
+
972
+ The timer inherits its process environment. With `CAIRN_NOTE_DISTILLATION`
973
+ truthy it launches exactly one separate `cairn notes distill --all-projects`
974
+ process after the wiki scan and reports its bounded status. With the flag unset
975
+ it performs no note work. Note failure does not replace wiki findings or change
976
+ their actionable exit status; no credentials are embedded in rendered units.