@cairnkeep/cli 2.3.0 → 2.4.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 (125) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +221 -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 +1 -1
  10. package/docs/design/meta-agent.md +439 -0
  11. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  12. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  13. package/docs/learning/lessons/L02-installation.md +2 -2
  14. package/docs/learning/lessons/L03-first-project.md +1 -1
  15. package/docs/operating.md +531 -4
  16. package/docs/privacy-and-data-flow.md +417 -0
  17. package/docs/storage.md +252 -0
  18. package/examples/eval/README.md +42 -0
  19. package/examples/eval/adapter.json +12 -0
  20. package/examples/eval/bundled-fake.json +1 -0
  21. package/examples/eval/task-set.json +1 -0
  22. package/mcp-memory-server/dist/artifact-cli.js +184 -0
  23. package/mcp-memory-server/dist/artifact-schema.js +218 -0
  24. package/mcp-memory-server/dist/artifact-store.js +812 -0
  25. package/mcp-memory-server/dist/capability-adapter.js +286 -0
  26. package/mcp-memory-server/dist/capability-cli.js +361 -0
  27. package/mcp-memory-server/dist/capability-config.js +310 -0
  28. package/mcp-memory-server/dist/capability-harness.js +496 -0
  29. package/mcp-memory-server/dist/capability-registry.js +123 -0
  30. package/mcp-memory-server/dist/capability-schema.js +65 -0
  31. package/mcp-memory-server/dist/capability-store.js +431 -0
  32. package/mcp-memory-server/dist/compaction-normalize.js +267 -0
  33. package/mcp-memory-server/dist/eval-cli.js +523 -0
  34. package/mcp-memory-server/dist/eval-plan.js +346 -0
  35. package/mcp-memory-server/dist/eval-process.js +273 -0
  36. package/mcp-memory-server/dist/eval-report.js +609 -0
  37. package/mcp-memory-server/dist/eval-runner.js +827 -0
  38. package/mcp-memory-server/dist/eval-schema.js +368 -0
  39. package/mcp-memory-server/dist/eval-statistics.js +161 -0
  40. package/mcp-memory-server/dist/eval-workspace.js +313 -0
  41. package/mcp-memory-server/dist/failure-signature.js +141 -0
  42. package/mcp-memory-server/dist/index.js +600 -55
  43. package/mcp-memory-server/dist/node-cli.js +84 -0
  44. package/mcp-memory-server/dist/node-schema.js +170 -0
  45. package/mcp-memory-server/dist/node-store.js +455 -0
  46. package/mcp-memory-server/dist/note-cli.js +235 -0
  47. package/mcp-memory-server/dist/note-distiller.js +177 -0
  48. package/mcp-memory-server/dist/note-enrichment.js +93 -0
  49. package/mcp-memory-server/dist/note-schema.js +4 -0
  50. package/mcp-memory-server/dist/note-store.js +985 -0
  51. package/mcp-memory-server/dist/trajectory-cli.js +141 -0
  52. package/mcp-memory-server/dist/trajectory-normalize.js +448 -0
  53. package/mcp-memory-server/dist/trajectory-redaction.js +155 -0
  54. package/mcp-memory-server/dist/trajectory-schema.js +73 -0
  55. package/mcp-memory-server/dist/trajectory-store.js +339 -0
  56. package/mcp-memory-server/package.json +24 -2
  57. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json +9 -0
  58. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json +9 -0
  59. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json +6 -0
  60. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json +119 -0
  61. package/mcp-memory-server/scripts/fixtures/compaction-unknown-version.json +17 -0
  62. package/opencode/capability-contract/plugins/capability-command.ts +319 -0
  63. package/opencode/command/graphify.md +25 -14
  64. package/opencode/plugins/memory-capture.ts +76 -10
  65. package/opencode/plugins/memory-wakeup.ts +58 -6
  66. package/package.json +9 -4
  67. package/pi/extensions/cairnkeep-trajectory.ts +55 -0
  68. package/schemas/artifact.schema.json +133 -0
  69. package/schemas/capability-callback.schema.json +68 -0
  70. package/schemas/capability-contract.schema.json +126 -0
  71. package/schemas/eval-adapter.schema.json +43 -0
  72. package/schemas/eval-protocol.schema.json +114 -0
  73. package/schemas/eval-report.schema.json +423 -0
  74. package/schemas/eval-task-set.schema.json +114 -0
  75. package/schemas/memory-node.schema.json +90 -0
  76. package/schemas/note.schema.json +112 -0
  77. package/schemas/trajectory-redaction.schema.json +25 -0
  78. package/schemas/trajectory.schema.json +82 -0
  79. package/scripts/bootstrap.sh +4 -0
  80. package/scripts/completion.sh +119 -5
  81. package/scripts/doctor.sh +284 -0
  82. package/scripts/fake-eval-adapter.mjs +120 -0
  83. package/scripts/fixtures/capability-harness-contracts.json +408 -0
  84. package/scripts/install-audit-timer.sh +3 -1
  85. package/scripts/lib/capability-opencode-plugin-harness.mjs +291 -0
  86. package/scripts/lib/trajectory-opencode-plugin-harness.mjs +25 -0
  87. package/scripts/lib/trajectory-pi-extension-harness.mjs +42 -0
  88. package/scripts/memory-wiki-audit.sh +49 -0
  89. package/scripts/smoke-bash32.sh +9 -0
  90. package/scripts/sync-claude-assets.sh +99 -6
  91. package/scripts/sync-opencode-graphify-assets.sh +24 -3
  92. package/scripts/sync-opencode-plugin-assets.sh +31 -5
  93. package/scripts/sync-opencode-security-assets.sh +24 -3
  94. package/scripts/sync-opencode-wiki-assets.sh +24 -3
  95. package/scripts/sync-pi-assets.sh +72 -0
  96. package/scripts/test-audit-timer.sh +98 -0
  97. package/scripts/test-bootstrap-untracked.sh +48 -0
  98. package/scripts/test-cli-dispatch.sh +41 -2
  99. package/scripts/test-compaction-hooks.sh +495 -0
  100. package/scripts/test-completion.sh +77 -0
  101. package/scripts/test-doctor.sh +348 -1
  102. package/scripts/test-eval-cli.sh +175 -0
  103. package/scripts/test-launcher-hooks.sh +602 -3
  104. package/scripts/test-notes-cli.sh +114 -0
  105. package/scripts/test-package-install.sh +251 -0
  106. package/scripts/test-phase16-node-lifecycle.sh +50 -0
  107. package/scripts/test-phase17-artifact-lifecycle.sh +370 -0
  108. package/scripts/test-phase18-capability-lifecycle.sh +504 -0
  109. package/scripts/test-phase18-harness-boundary.sh +1060 -0
  110. package/scripts/test-phase19-eval-lifecycle.sh +1015 -0
  111. package/scripts/test-trajectory-cli.sh +87 -0
  112. package/scripts/test-trajectory-hooks.sh +142 -0
  113. package/scripts/test-uninstall.sh +231 -5
  114. package/scripts/uninstall.sh +37 -5
  115. package/scripts/verify-dependency-security.mjs +121 -0
  116. package/scripts/verify-docs-parity.sh +527 -1
  117. package/scripts/verify-phase18-runtime-evidence.mjs +895 -0
  118. package/scripts/verify-phase19-runtime-evidence.mjs +947 -0
  119. package/templates/capabilities.json.template +7 -0
  120. package/templates/env.example.template +51 -0
  121. package/templates/memory-wiki-audit.service.template +1 -1
  122. package/templates/start-claude.sh.template +36 -0
  123. package/templates/start-opencode.sh.template +47 -5
  124. package/templates/start-pi.sh.template +30 -0
  125. package/templates/trajectory-redaction.json.template +4 -0
@@ -17,6 +17,21 @@ request unless the corresponding endpoint and credential are configured.
17
17
  | `route_check` | A health request, with no memory or prompt content | `CAIRN_ROUTE_ENDPOINT` |
18
18
  | `context_explore` | Repository path and query are passed to the configured local executable | `CAIRN_EXPLORE_BINARY`; any further data flow is controlled by that tool |
19
19
  | Remote HTTP memory | MCP requests and responses, including memory content | The explicitly registered Cairnkeep HTTP server |
20
+ | Opt-in trajectory capture | None | Local `<project>/.agentfs/trajectory.db` only; no model or HTTP path exists |
21
+ | Opt-in capability callback records | None | Payload-free final metadata in local `<project>/.agentfs/trajectory.db`; HTTP transport is always skipped |
22
+ | Opt-in deterministic note distillation | None | Reads redacted closed trajectories; writes local Markdown + manifest under `${CAIRN_AGENTFS_BASE_DIR:-~/.cairnkeep}/notes/` |
23
+ | Separately opted-in note enrichment | `CAIRN_LLM_API_KEY` | Sends bounded redacted note evidence to the explicit `CAIRN_LLM_API_URL` chat endpoint |
24
+ | Typed lifecycle and inline `memory_import` over local stdio | None | Values, metadata, digests, replay bindings, and history remain in the selected local store |
25
+ | Typed lifecycle and inline `memory_import` over authenticated HTTP | MCP requests and responses, including supplied values | The explicitly registered Cairnkeep HTTP server; import results omit values |
26
+ | Project/shared note lifecycle over local stdio | None | Canonical Markdown, history, replay, and journals remain in the server-controlled notes root |
27
+ | Project/shared note lifecycle over authenticated HTTP | MCP requests and responses, including complete note records | The explicitly registered server; clients send logical keys, never server filesystem paths |
28
+ | Claude `PostCompact` compaction capture | None | Redacted local `<project>/.agentfs/artifacts.db`; no model or HTTP path |
29
+ | OpenCode `session.compacted` + local SDK fetch | None | Redacted local `<project>/.agentfs/artifacts.db`; no model or HTTP path |
30
+ | Explicit artifact tools over local stdio | None | Inline artifact values remain in the local project artifact store |
31
+ | Separately enabled artifact tools over HTTP | MCP artifact requests and explicit read responses | The authenticated server selected by the client; requires `CAIRN_ARTIFACT_STORE`, `CAIRN_ARTIFACT_HTTP`, bearer/Host checks, and validated project identity |
32
+ | Automatic compaction recovery | None | Structured categories are injected locally; raw summary and other bodies are excluded |
33
+ | Explicit artifact read/show | The redacted body only when HTTP is separately enabled and the operator/client explicitly reads it | Local terminal/stdio, or the explicitly registered authenticated server |
34
+ | Opt-in evaluation coordinator | Adapter-defined inference may leave the machine only according to the explicit operator-owned adapter and inherited environment | Local adapter subprocess; Cairnkeep supplies no provider, endpoint, credential, model, or network default |
20
35
 
21
36
  Model endpoints may be local or remote. Cairnkeep cannot determine a provider's
22
37
  retention, training, or logging policy; verify it before sending confidential
@@ -36,6 +51,408 @@ Project-scoped and named/global database locations are documented in
36
51
  the remote server host; changing a storage environment variable on the client
37
52
  does not relocate that server's databases.
38
53
 
54
+ Typed metadata adds no default egress. Values, canonical types/tags, complete
55
+ history snapshots, SHA-256 import digests, replay bindings, and note transaction
56
+ backups/journals are sensitive local data at rest. Dry-run and commit responses
57
+ contain scopes/address spaces, digests, keys, actions, and counts—not supplied
58
+ or displaced values. No model, API key, endpoint, or network is required.
59
+
60
+ The existing explicitly configured embedding search is the sole related egress:
61
+ eligible key/value/type/tag text and the query may be sent to
62
+ `CAIRN_MEMORY_EMBEDDING_URL` (or its documented fallback). Hard filters run
63
+ before that request; endpoint failure returns to local substring search. Note
64
+ search is always local and never uses embeddings.
65
+
66
+ Canonical note storage includes manifest records, Markdown, indexes, history,
67
+ import replay state, staged bytes, backups, and journals. Prepared/committing
68
+ recovery restores verified pre-images. Committed-before-cleanup recovery
69
+ verifies and preserves the completed mutation, then removes only transaction
70
+ artifacts. Unverifiable committed state remains blocked and is not rolled back.
71
+ Default uninstall retains this state; explicit `--purge-memory` backs it up
72
+ before removal and its `revert.sh` restores it.
73
+
74
+ Artifact envelopes, redacted content, digests, indexes, dedupe bindings,
75
+ revision counters, latest pointers, SQLite sidecars, backups, and explicit
76
+ read/CLI output are sensitive data at rest. They are not application-level
77
+ encrypted. Default uninstall retains them; purge is backup-first and the
78
+ generated revert restores exact bytes. Hard delete and explicit prune remove
79
+ content plus derived references without keeping a tombstone.
80
+
81
+ Evaluation reports and task-local note snapshots are also sensitive local data
82
+ at rest under `<project>/.agentfs/eval/experiments/`. Directories are private,
83
+ reports are mode `0600`, snapshots are read-only, and checkpoints are bounded
84
+ atomic replacements. Default uninstall retains them; explicit
85
+ `--purge-memory PROJECT` backs up the whole `.agentfs/` boundary before removal
86
+ and its generated `revert.sh` restores exact bytes, modes, and layout. Explicit
87
+ `cairn eval delete` and `prune` remove selected contained experiment trees with
88
+ no hidden tombstone.
89
+
90
+ ## Evaluation adapter and report flow
91
+
92
+ `CAIRN_EVAL` is unset/off by default. With it off, `cairn eval` returns a fixed
93
+ disabled result before task-set or adapter reads, workspace/report creation,
94
+ database access, subprocess execution, or network activity. A credential in
95
+ the environment never enables evaluation. When enabled, Cairnkeep remains a
96
+ local coordinator; the operator selects and owns any inference performed by
97
+ the explicit adapter program.
98
+
99
+ The per-observation flow is:
100
+
101
+ ```text
102
+ validated committed task set + explicit adapter program/args
103
+ → fresh task/pass/arm HOME, TMP, XDG, workspace, notes, and output roots
104
+ → one bounded schema-v1 request on adapter stdin
105
+ → exactly one bounded strict schema-v1 observation on adapter stdout
106
+ → independent deterministic task verifier
107
+ → allow-listed local observation + atomic partial/final report checkpoint
108
+ → workspace cleanup
109
+ ```
110
+
111
+ The request fields are exactly schema version, experiment/task identity, arm,
112
+ repetition, pass, relative workspace path, nullable relative notes path, fixed
113
+ task input, declared limits, paired seed, expected capability digest, and
114
+ relative output path. The fixed task input is prompt-like content delivered to
115
+ the adapter for execution; it is not copied into the report. Run 2 can receive
116
+ only the immutable note snapshot distilled from that same task's Run 1
117
+ trajectory. No other task's notes, checkout, HOME, temporary directory, XDG
118
+ state, or output directory is reused.
119
+
120
+ The supplied `workspace_path` is the task's Cairnkeep project root for
121
+ trajectory-producing harness work. Offline Run 1 distillation performs an
122
+ exact session lookup only in that task workspace's local
123
+ `.agentfs/trajectory.db`; `trajectory_ref` is a bounded session identifier and
124
+ not a path. If the exact session is absent or stored under a different root,
125
+ the report retains failed note missingness and Run 2 receives no snapshot.
126
+ Cairnkeep does not retry the source or parent root, recursively discover
127
+ stores, inspect siblings, or accept an adapter-selected absolute locator.
128
+
129
+ The strict stdout observation can contain only terminal status and a value-free
130
+ error code; turns with an exact semantics ID; independently optional input,
131
+ output, reasoning, cache-read, cache-write, and total token counts; optional
132
+ amount-plus-currency cost; bounded harness/adapter/model/config identities;
133
+ observed capability digest; and bounded trajectory/artifact references. It has
134
+ no pass field—the verifier owns pass state. Unknown fields, oversized output,
135
+ invalid UTF-8/JSON, semantic mismatches, and capability-digest mismatches are
136
+ rejected or retained as explicit missing/invalid observations rather than
137
+ being inferred.
138
+
139
+ The adapter inherits the operator's process environment so an operator-owned
140
+ harness can use its existing secrets. Cairnkeep overrides only isolated
141
+ task-local HOME/TMP/XDG roots and explicit capability-arm values. Secret values
142
+ and full environment snapshots are never report fields. Adapter stderr is
143
+ inherited for live operator diagnostics and is never buffered into evidence.
144
+ Adapter stdout is discarded after strict observation validation. Prompts,
145
+ model outputs, verifier stdout/stderr, arbitrary errors, workspace contents,
146
+ and complete environment values are not persisted in reports. Identifiers and
147
+ references are bounded and value-minimized so metadata cannot become a free
148
+ text channel.
149
+
150
+ Reports persist only the versioned schedule and terminal/process/verifier
151
+ states; allow-listed observations; expected/observed capability state and
152
+ digests; task-local note outcome/manifests/digests; revisions and task/adapter
153
+ digests; runtime/component identifiers; explicit optional metrics; populations,
154
+ missingness, warnings, and uncertainty metadata. The stable JSON report is the
155
+ source of truth; human output is rendered only from validated JSON. Local
156
+ report and snapshot retention/removal is documented in
157
+ [storage](storage.md#evaluation-report-and-note-snapshot-storage).
158
+
159
+ `validate` performs no adapter or network call. `run` and `ablate` print the
160
+ deterministic serial invocation estimate and require explicit `--yes` before
161
+ execution. Cairnkeep supplies no live adapter, provider, endpoint, credential,
162
+ model, retry, or network default. The packaged fake adapter is deterministic,
163
+ network-free, and permanently scoped as framework-only evidence.
164
+
165
+ SIGINT/SIGTERM stops new schedule admission, terminates the active adapter,
166
+ checkpoints cancellation, performs bounded cleanup, and retains a partial
167
+ report. POSIX process groups receive TERM followed by bounded KILL escalation.
168
+ There is no Windows descendant-process-tree termination guarantee.
169
+
170
+ Turns aggregate only under exact matching compatibility IDs. Missing token
171
+ components/totals, unknown verifier outcomes, incompatible turn semantics, and
172
+ failed or absent note snapshots remain explicit missingness. Reports show both
173
+ the full committed population and note-eligible subset with paired counts and
174
+ seeded uncertainty; they do not convert framework output into a causal,
175
+ significance, quality, or efficiency claim.
176
+
177
+ ## Compaction and artifact flows
178
+
179
+ Both `CAIRN_COMPACTION_CAPTURE` and `CAIRN_ARTIFACT_STORE` are default-off and
180
+ independent. With a flag off, its adapter returns before new parsing, SDK,
181
+ subprocess, file/database, network, stdout/stderr, or context-injection work.
182
+ Credentials never imply either flag. No model call, telemetry, automatic
183
+ remote discovery, or default egress is introduced.
184
+
185
+ Claude local capture:
186
+
187
+ ```text
188
+ documented PostCompact stdin (session/cwd/trigger/compact_summary)
189
+ → supported-version validation
190
+ → recursive redaction
191
+ → truncate / digest / index / write
192
+ → local .agentfs/artifacts.db
193
+ ```
194
+
195
+ OpenCode local capture:
196
+
197
+ ```text
198
+ session.compacted session ID
199
+ → local SDK get(session) + messages(session)
200
+ → select the pinned completed summary shape
201
+ → recursive redaction
202
+ → truncate / digest / index / write
203
+ → local .agentfs/artifacts.db
204
+ ```
205
+
206
+ Only Claude Code `PostCompact` 2.1.219 and 2.1.220 and OpenCode
207
+ `session.compacted` 1.17.20 are pinned. Unknown versions/shapes fail open. Their payload is not logged, serialized,
208
+ temporarily copied, digested, indexed, or retained; doctor receives only a
209
+ bounded value-free reason. Artifact candidates are redacted before digest, index, temporary storage, or any Cairnkeep write.
210
+ Truncation also happens only after redaction. The same recursive
211
+ built-ins and bounded project redaction file used by trajectory capture apply.
212
+
213
+ Explicit artifact ingress:
214
+
215
+ ```text
216
+ local stdio artifact_write (CAIRN_ARTIFACT_STORE)
217
+ → bounded inline schema → redact → truncate → digest/index/write locally
218
+
219
+ authenticated HTTP artifact_write (both artifact flags)
220
+ → bearer + Host/CORS + X-Cairn-Project
221
+ → server-derived project root → same bounded redaction/write path
222
+ ```
223
+
224
+ The four accepted kinds are `compaction_summary`, `diff`, `test_output`, and
225
+ `generated_file`. Generated-file paths are labels only and are never read;
226
+ binary or oversized bodies become metadata-only. There is no ambient command
227
+ output capture, filesystem watcher, or caller-selected path dereference.
228
+
229
+ Recovery egress is local and concise:
230
+
231
+ ```text
232
+ current-session latest valid compaction, else newest valid project compaction
233
+ → provenance/freshness/completeness + four structured categories
234
+ → harness SessionStart/system-transform context
235
+ ```
236
+
237
+ Raw summaries are on-demand only through explicit `artifact_read` or
238
+ `cairn artifact show`; automatic recovery never injects them or another
239
+ artifact body. Lists, writes, deletes, pruning, and doctor omit bodies. Stored
240
+ redacted content is still sensitive and can be exposed by an explicit read.
241
+
242
+ HTTP artifact egress is separately enabled by `CAIRN_ARTIFACT_HTTP` in
243
+ addition to `CAIRN_ARTIFACT_STORE` and existing authenticated transport
244
+ controls. It exposes only the four artifact tools, never trajectories,
245
+ compaction hooks, or a generic filesystem. With HTTP consent absent, remote
246
+ clients cannot observe artifact schemas or content. Artifacts have no default egress.
247
+ Artifacts have no telemetry; local capture/recovery never uses the HTTP route.
248
+
249
+ ## Structured trajectory capture
250
+
251
+ `CAIRN_TRAJECTORY_CAPTURE` is unset and off by default. When explicitly set to
252
+ `1`, `true`, `yes`, or `on`, the existing Claude Code SessionEnd hook reads the
253
+ harness-owned transcript file named in its hook event, or the existing OpenCode
254
+ session-idle plugin requests that session's structured messages and parts from
255
+ the local SDK, or the Pi `session_shutdown` extension reads Pi's active branch
256
+ through its read-only session manager. Cairnkeep does not change, copy, retain,
257
+ or delete Claude Code's source transcript, and it does not control OpenCode or
258
+ Pi's own source retention.
259
+ Those harness-owned sources remain a separate privacy boundary.
260
+
261
+ The capture path allow-lists user messages, visible model outputs, tool
262
+ invocations, tool results, system events, timestamps, and usage/cost fields
263
+ when the harness exposes them. It does not store hidden reasoning text;
264
+ reasoning blocks and unknown record types are counted as omissions. Records are
265
+ normalized into the versioned trajectory schema, recursively redacted, bounded
266
+ by UTF-8 serialized size, and only then passed to AgentFS. Built-in rules redact
267
+ secret-like object keys, bearer/API-key/password forms, credential-bearing URLs,
268
+ private-key blocks, and exact values of secret-like environment variables.
269
+ For Pi, model and thinking-level changes become system events; hidden thinking
270
+ blocks are never written. Provider error messages become redacted system events
271
+ so failed sessions remain useful to later hindsight processing. Only the active branch is captured. The extension
272
+ passes the structured branch to the same local normalizer/store path, kills a
273
+ stalled capture subprocess after three seconds, and always fails open so a
274
+ capture error cannot change the Pi session's outcome.
275
+
276
+ Bootstrap writes `.ai/trajectory-redaction.json` with no custom rules and does
277
+ not enable capture. A project may add up to 32 bounded regular expressions to
278
+ that file, or select another project-contained file with
279
+ `CAIRN_TRAJECTORY_REDACTION_FILE`. Invalid, unreadable, outside-project, or
280
+ uncompilable configuration fails that capture attempt before any partial write.
281
+ Custom patterns are defense in depth: they can miss an unknown secret shape or
282
+ redact too broadly, so captured sessions must still be treated as sensitive.
283
+
284
+ The redacted v1 session and time index live only at
285
+ `<project>/.agentfs/trajectory.db`. Default limits are 5 MiB per session, 256
286
+ MiB logical total and 30 days. Capture and explicit `cairn trajectory prune`
287
+ apply age and oldest-first budget retention. `list`, `show`, `prune --dry-run`,
288
+ doctor output, SQLite `-wal`/`-shm` files, backups and terminal output remain
289
+ local but can expose redacted project content, paths, error output and metadata
290
+ to anyone who can access them. Cairnkeep provides no application-level
291
+ encryption; use host encryption and access controls.
292
+
293
+ Trajectory capture has no MCP tool or remote HTTP endpoint and never calls an
294
+ LLM, embedding service, AnythingLLM, telemetry service, or other network
295
+ destination. With the flag unset, the hook/plugin does not create or touch the
296
+ trajectory database. `cairn doctor --repair` may rebuild missing metadata and
297
+ indexes from valid full records but does not discard an invalid full record;
298
+ only capture retention, explicit prune, or explicit uninstall purge removes
299
+ trajectory sessions.
300
+
301
+ ## Capability callback flow
302
+
303
+ Callback measurement has three consents, all separately default-off where
304
+ applicable:
305
+
306
+ ```text
307
+ CAIRN_CAPABILITY_CONTRACT enabled
308
+ + managed logging.callbacks enabled (or CAIRN_CAPABILITY_LOGGING)
309
+ + CAIRN_TRAJECTORY_CAPTURE enabled
310
+ + local non-HTTP invocation
311
+ -> resolve effective state and digest
312
+ -> durably issue one payload-free local operating marker
313
+ -> run the unchanged capability owner
314
+ -> check current three-consent authorization and effective state again
315
+ -> consume the exact issued marker once with one payload-free final record
316
+ ```
317
+
318
+ The resulting three-state privacy contract is precise:
319
+
320
+ - With the master contract off, Cairnkeep preserves exact legacy behavior. It
321
+ installs or invokes no capability hook/plugin, never introduces a capability
322
+ block, and creates no pending lease, callback final, or other capability
323
+ measurement state.
324
+ - With the master contract on and the target disabled, the fixed block always
325
+ occurs before owner I/O. When all three consents are enabled, the issued
326
+ lease is atomically consumed into exactly one D-25/D-26 value-free
327
+ `disabled` final. If either measurement consent—managed callback logging or
328
+ local trajectory capture—is off, the same fixed block remains in force and
329
+ no pending or final state is written.
330
+ - With the master contract on and the target enabled, turning either
331
+ measurement consent off leaves owner execution unchanged and writes no
332
+ pending or final state.
333
+
334
+ Consent affects measurement, never the disabled policy decision. No branch may
335
+ record arguments, results, prompts, query text, paths, raw errors, or secrets.
336
+
337
+ An operating start with absent consent, HTTP transport, or a local-store fault
338
+ returns the existing unmeasured bypass without issuing a handle. At finish,
339
+ the contract flag and local trajectory capture are checked before managed
340
+ status resolution; current three-consent authorization is checked again at finish,
341
+ and managed logging, capability enablement, state source, and the effective
342
+ configuration digest must still match. Revoked consent or stale
343
+ configuration defensively consumes a matching existing marker without a final
344
+ record. This invalidation never creates a database, remains value-free and
345
+ fail-open, and cannot resurrect the invocation after re-enable.
346
+
347
+ A schema-valid handle alone proves nothing. Unissued, strict-field-mismatched,
348
+ expired, stale, and replayed handles return a non-finalizing result and cannot
349
+ produce final callback evidence. A mismatch does not consume the authentic
350
+ marker; the untouched issued handle can still settle exactly once.
351
+
352
+ There is no remote/HTTP callback persistence. HTTP callbacks are never persisted.
353
+ Pending markers and final records never use remote project routing.
354
+ There is no payload, no telemetry, no analytics, no log export, and no callback
355
+ network request. This is a no-payload, no-telemetry, no-network boundary.
356
+ Existing authenticated HTTP MCP behavior is unchanged.
357
+
358
+ The strict schema permits only these final fields:
359
+
360
+ - `schema_version`
361
+ - `capability_id`
362
+ - `invocation_id` (`cap:<uuid>`, unique per invocation)
363
+ - `correlation_id` (the explicit harness session when exposed, otherwise one
364
+ stable per-process/run `cairn:<uuid>`; never `unknown`)
365
+ - `harness` (`claude-code`, `opencode`, `pi`, or `other`)
366
+ - `source` (`mcp`, `notes-cli`, `audit-timer`, `operating-command`, or
367
+ `operating-workflow`)
368
+ - `transport` (`stdio`, `local-process`, or `harness-command` for persisted
369
+ records; HTTP is skipped)
370
+ - `started_at`, `finished_at`, and non-negative `duration_ms`
371
+ - `outcome` (`success`, `error`, `timeout`, or `disabled`) and, for a
372
+ non-success outcome, one stable value-free `error_code`
373
+ - `state_source` (`environment`, `project`, or `compatibility`)
374
+ - `configuration_digest` (the SHA-256 digest of the effective state snapshot)
375
+
376
+ There is no start record in callback evidence. A transient marker under
377
+ `capability-callback/v1/pending/` contains only the same strict handle scalars;
378
+ it is not a trajectory session or callback record, is bounded by the callback
379
+ retention/cap policy, and is consumed once. Final records remain under the
380
+ unchanged `capability-callback/v1/record/` schema and allow-list. One atomic
381
+ final record is attempted after a terminal outcome; an operating disabled
382
+ result may record `disabled`. A store open, lock, schema, validation, or write
383
+ failure is fail-open: it cannot change the owner result, thrown error, timeout
384
+ behavior, stdout/stderr, or exit status.
385
+
386
+ Neither issuance nor the final-record constructor receives or persists
387
+ arguments, results, prompts, query text, memory values, file paths, stack
388
+ traces, raw errors, secrets, credentials, arbitrary metadata, or user-supplied
389
+ messages/details. The timer starts only immediately before the capability-owned
390
+ body after state resolution, and ends after its outcome before final
391
+ presentation; discovery, configuration, guard, and unrelated harness overhead
392
+ are excluded.
393
+
394
+ When one of `memory.write`, `memory.search`, `route.check`, or
395
+ `context.explore` is disabled, it is omitted from MCP registration and cannot
396
+ produce a callback record. The schema-v1 `cairn capabilities status --json`
397
+ snapshot and its `configuration_digest` are the evidence for that omitted tool.
398
+ This is operating evidence only, not a task result, telemetry event, or
399
+ evaluation/quality claim.
400
+
401
+ ## Hindsight note distillation
402
+
403
+ `CAIRN_NOTE_DISTILLATION` is unset/off by default. With it off, every public
404
+ notes operation returns before reading trajectories, walking projects, opening
405
+ the note store, acquiring a lock, or making a request. Capture does not imply
406
+ distillation, and the presence of any API credential does not enable it.
407
+
408
+ The local deterministic flow is:
409
+
410
+ ```text
411
+ harness-owned source
412
+ → allow-listed, redacted, bounded trajectory in <project>/.agentfs/trajectory.db
413
+ → conservative failure/validation evidence
414
+ → versioned message + stack + component signature and lifecycle reducer
415
+ → local managed Markdown, README indexes, and hidden manifest under ~/.cairnkeep/notes/
416
+ ```
417
+
418
+ Only explicit failed tool results, structured nonzero statuses, provider-error
419
+ events, equivalent later successful validation calls, and a bounded set of
420
+ explicit abandonment phrases affect lifecycle. The note stores normalized
421
+ error/component data, session IDs/digests, timestamps, outcome evidence, and
422
+ optional generated prose; it does not copy full trajectories or hidden
423
+ reasoning. Visible Markdown omits absolute checkout roots. The hidden manifest
424
+ retains canonical project roots for local scheduling, lookup maps, lifecycle
425
+ records, and processed-session digests. Occurrence history is capped at 1024
426
+ entries per note and processed-session history at 4096 digests per project.
427
+ Notes have no automatic age deletion and survive trajectory pruning; inspect or
428
+ delete them under your local retention policy.
429
+
430
+ Markdown and manifest files, locks, backups, terminal JSON, and promoted shared
431
+ notes are sensitive derived project data. They are owner-readable local files,
432
+ not encrypted by Cairnkeep. Generated `cairnkeep:managed:v1` blocks may be
433
+ rewritten; text after the block is preserved. Promotion is explicit and local,
434
+ requires compatible evidence from two distinct projects plus `--confirm`, and
435
+ creates one shared canonical note with project provenance references.
436
+
437
+ Optional enrichment adds a second, independent egress boundary. It runs only
438
+ when `CAIRN_NOTE_ENRICHMENT` and the master flag are truthy and key, base URL,
439
+ and model are all explicitly configured. One changed note's bounded redacted
440
+ error, component, lifecycle, and attempt strings are sent to the configured
441
+ OpenAI-compatible `/chat/completions` endpoint; full trajectories, manifests,
442
+ manual Markdown, credentials, and hidden reasoning are not request fields.
443
+ Requests use a bounded output, timeout, strict response schema, and at most one
444
+ retry. Returned prose is labeled non-authoritative and cannot change identity,
445
+ lifecycle, exact keys, or provenance. Missing configuration or provider failure
446
+ leaves the deterministic note intact.
447
+
448
+ Redaction is best effort, not proof that content is non-sensitive. Unknown
449
+ secret formats or secrets embedded in unusual free text can survive imperfect
450
+ patterns and therefore reach local notes or, if enrichment is enabled, the
451
+ configured endpoint. Inspect redaction rules and representative local notes
452
+ before enabling enrichment for confidential projects. Endpoint operation,
453
+ retention, training, access control, jurisdiction, and deletion policy are the
454
+ operator's responsibility; Cairnkeep cannot verify them.
455
+
39
456
  The official container stores all databases below `/data`. A named volume
40
457
  persists them after container replacement and remains sensitive data. Sandbox
41
458
  workspace mode also retains a repository copy in its named volume. Neither