brainclaw 1.9.0 → 1.9.1

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 (91) hide show
  1. package/README.md +585 -499
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/commands/harvest.js +1 -1
  4. package/dist/commands/hooks.js +73 -73
  5. package/dist/commands/init.js +1 -1
  6. package/dist/commands/install-hooks.js +78 -78
  7. package/dist/commands/mcp-read-handlers.js +57 -14
  8. package/dist/commands/mcp.js +79 -13
  9. package/dist/commands/switch.js +26 -5
  10. package/dist/commands/version.js +1 -1
  11. package/dist/core/agent-capability.js +19 -4
  12. package/dist/core/agent-files.js +119 -119
  13. package/dist/core/codev-prompts.js +38 -38
  14. package/dist/core/default-profiles/doctor.yaml +11 -11
  15. package/dist/core/default-profiles/janitor.yaml +11 -11
  16. package/dist/core/default-profiles/onboarder.yaml +11 -11
  17. package/dist/core/default-profiles/reviewer.yaml +13 -13
  18. package/dist/core/dispatcher.js +1 -1
  19. package/dist/core/entity-operations.js +29 -3
  20. package/dist/core/execution.js +1 -1
  21. package/dist/core/loops/verbs.js +0 -1
  22. package/dist/core/messaging.js +2 -2
  23. package/dist/core/protocol-skills.js +164 -164
  24. package/dist/core/runtime-signals.js +1 -1
  25. package/dist/core/search.js +19 -2
  26. package/dist/core/security-guard.js +207 -207
  27. package/dist/core/spawn-check.js +16 -2
  28. package/dist/core/staleness.js +1 -1
  29. package/dist/core/store-resolution.js +26 -7
  30. package/dist/core/worktree.js +18 -18
  31. package/dist/facts.js +3 -3
  32. package/dist/facts.json +2 -2
  33. package/docs/PROTOCOL.md +1 -1
  34. package/docs/adapters/openclaw.md +43 -43
  35. package/docs/architecture/project-refs.md +328 -328
  36. package/docs/cli.md +2093 -2093
  37. package/docs/concepts/coordination.md +52 -52
  38. package/docs/concepts/coordinator-runbook.md +129 -129
  39. package/docs/concepts/dispatch-lifecycle.md +245 -245
  40. package/docs/concepts/event-log-store.md +928 -928
  41. package/docs/concepts/ideation-loop.md +317 -317
  42. package/docs/concepts/loop-engine.md +520 -511
  43. package/docs/concepts/mcp-governance.md +268 -268
  44. package/docs/concepts/memory.md +84 -84
  45. package/docs/concepts/multi-agent-workflows.md +167 -167
  46. package/docs/concepts/observer-protocol.md +361 -361
  47. package/docs/concepts/plans-and-claims.md +217 -217
  48. package/docs/concepts/project-md-convention.md +35 -35
  49. package/docs/concepts/runtime-notes.md +38 -38
  50. package/docs/concepts/troubleshooting.md +254 -254
  51. package/docs/concepts/workspace-bootstrapping.md +142 -142
  52. package/docs/context-format-changelog.md +35 -35
  53. package/docs/context-format.md +48 -48
  54. package/docs/index.md +65 -65
  55. package/docs/integrations/agents.md +158 -158
  56. package/docs/integrations/claude-code.md +23 -23
  57. package/docs/integrations/cline.md +77 -77
  58. package/docs/integrations/continue.md +55 -55
  59. package/docs/integrations/copilot.md +68 -68
  60. package/docs/integrations/cursor.md +23 -23
  61. package/docs/integrations/kilocode.md +72 -72
  62. package/docs/integrations/mcp.md +377 -377
  63. package/docs/integrations/mistral-vibe.md +122 -122
  64. package/docs/integrations/openclaw.md +92 -92
  65. package/docs/integrations/opencode.md +84 -84
  66. package/docs/integrations/overview.md +115 -115
  67. package/docs/integrations/roo.md +71 -71
  68. package/docs/integrations/windsurf.md +77 -77
  69. package/docs/mcp-schema-changelog.md +360 -356
  70. package/docs/playbooks/integration/index.md +121 -121
  71. package/docs/playbooks/orchestration.md +37 -0
  72. package/docs/playbooks/productivity/index.md +99 -99
  73. package/docs/playbooks/team/index.md +117 -117
  74. package/docs/product/agent-first-model.md +184 -184
  75. package/docs/product/entity-model-audit.md +462 -462
  76. package/docs/product/positioning.md +86 -86
  77. package/docs/quickstart-existing-project.md +107 -107
  78. package/docs/quickstart.md +183 -183
  79. package/docs/release-maintenance.md +79 -79
  80. package/docs/reputation.md +52 -52
  81. package/docs/review.md +45 -45
  82. package/docs/security.md +212 -212
  83. package/docs/server-operations.md +118 -118
  84. package/docs/storage.md +106 -106
  85. package/package.json +80 -65
  86. package/docs/concepts/event-log-store-critique-A.md +0 -333
  87. package/docs/concepts/event-log-store-critique-B.md +0 -353
  88. package/docs/concepts/event-log-store-phase0-measurements.md +0 -58
  89. package/docs/concepts/event-log-store-proposal-A.md +0 -365
  90. package/docs/concepts/event-log-store-proposal-B.md +0 -404
  91. package/docs/concepts/identity-model-proposal.md +0 -371
@@ -1,404 +0,0 @@
1
- # Event-Log Store — Proposal B (slot B, round 1)
2
-
3
- Status: ideation draft — loop lop_3bf55b9492e0d96c, pln_2290bc70 / pln#543 step 1.
4
- Author: claude-code (slot B), independent round-1 proposal.
5
-
6
- ## Thesis
7
-
8
- Evolve `src/core/event-log.ts` from a best-effort notification stream into the
9
- **source of truth**, and re-cast the existing per-entity JSON files as a
10
- **materialized projection** of that journal. The JSON files do not disappear —
11
- they become a derived cache that is *also* the git-diffable, human-readable,
12
- MCP-cheap representation. This single move satisfies the three hard constraints
13
- that look contradictory at first glance (journal as truth / git-diffable store /
14
- cheap worker-per-call reads), because the projection on disk *is* what today's
15
- readers already consume.
16
-
17
- Core design choices, argued below:
18
-
19
- | Question | Choice | One-line rationale |
20
- |---|---|---|
21
- | Payload format | **Full entity snapshot per event** | Self-contained, gap-tolerant, zero-dep, trivial compaction |
22
- | Ordering authority | **Global monotonic `seq`, assigned under the store lock** | Clock skew neutralized; journal order = mutation order |
23
- | Append safety | O_APPEND single-write + newline framing + torn-tail tolerance | Atomic enough on NTFS+POSIX local FS; reader heals |
24
- | Durability | One `fsync` per `mutate()` (not per event), configurable | Journal-first means tail loss is bounded and healable |
25
- | Rotation | **Immutable segments + checkpoint records**, never rename-away | Rebuildability preserved; cursors survive rotation |
26
- | Projections | Lazy reconciliation at read paths, `seq`-watermark dirty check | House pattern (pln#496); no daemon |
27
- | Migration | Flag-gated dual-write, genesis-snapshot backfill, park-don't-delete | Rollback = flip the flag; JSON files are always valid |
28
-
29
- ---
30
-
31
- ## 1. Event payload format
32
-
33
- ### 1.1 Decision: full entity snapshot per event
34
-
35
- Each state-changing event carries the **complete post-mutation entity**, not a
36
- diff. Record shape (v2; existing events are retroactively "v1"):
37
-
38
- ```jsonc
39
- {
40
- "v": 2,
41
- "seq": 18342, // global monotonic, assigned under store lock
42
- "ts": "2026-06-10T14:03:22.114Z", // informational only — never an ordering key
43
- "writer": "claude-code", // agent name
44
- "writer_id": "agt_...", // optional stable agent id
45
- "pid": 31416, // diagnostic
46
- "action": "update", // existing EventAction union
47
- "item_type": "plan",
48
- "item_id": "pln_2290bc70",
49
- "summary": "step 1 started", // human-facing, optional
50
- "payload": { /* full entity object, schema-valid */ },
51
- "deleted": false // true => payload omitted, tombstone
52
- }
53
- ```
54
-
55
- Deletes are tombstones (`deleted: true`, no payload). Non-entity events
56
- (`session_start`, `run_*` notifications without state change) keep
57
- `payload`-less form — they are observability, not state.
58
-
59
- ### 1.2 Why snapshots beat diffs here
60
-
61
- Considered alternatives:
62
-
63
- - **JSON-Patch (RFC 6902)** — smallest payloads, but: (a) zero-runtime-deps
64
- constraint means hand-rolling a patch engine with its own bug surface;
65
- (b) every event becomes **load-bearing**: one corrupt/torn/skipped line
66
- poisons all subsequent state for that entity. Replay must be perfect or
67
- fail loudly. That is the wrong failure mode for a file-based store that
68
- agents, tests, and humans touch directly.
69
- - **Field-delta (shallow `{changed_fields}`)** — cheaper than full snapshot,
70
- no library needed, but same load-bearing problem for any field ever touched,
71
- plus ambiguity between "field absent = unchanged" and "field removed".
72
- - **Full snapshot** — every event is **idempotent and self-contained**.
73
- Rebuilding an entity = take the latest event for that `item_id`. Rebuilding
74
- the store = one backward scan keeping first-seen per id. A lost or corrupt
75
- line costs *at most the window until the next write of that entity*, never
76
- the entity's integrity. Compaction is trivial (§3). Federation merge is
77
- trivial (§5).
78
-
79
- ### 1.3 Size math (is snapshot affordable?)
80
-
81
- Entities today are ~1–4 KB pretty-printed; compact JSON in the journal ≈
82
- 0.5–2 KB. Current observed rate: ~17k events / ~2 months. Even if v2 events
83
- average 2 KB, that is ~34 MB/2 months ≈ a 10 MB segment roll every ~2–3 weeks.
84
- With checkpoint-based compaction (§3) the *live* journal stays bounded by
85
- (entities × snapshot size) + recent tail. Verdict: snapshot cost is noise at
86
- brainclaw's write rates; we are not building Kafka.
87
-
88
- **Mitigation if an entity type grows large** (e.g. long plan bodies): per-type
89
- opt-in to `payload_ref` pointing at the projection file + content hash — but
90
- this is explicitly **deferred**; do not build it until a real entity exceeds
91
- ~64 KB. (OPEN QUESTION Q1.)
92
-
93
- ---
94
-
95
- ## 2. Append atomicity & durability
96
-
97
- ### 2.1 What we get from the OS
98
-
99
- `fs.appendFileSync` opens `O_APPEND` (POSIX) / `FILE_APPEND_DATA` (Windows).
100
- For **local** filesystems (NTFS, ext4, APFS), a single `write()` of one buffer
101
- to an O_APPEND fd positions atomically; interleaving between processes
102
- produces *concatenated* records, not *interleaved bytes*, for the record sizes
103
- we write. Caveats we must encode in the spec, not just assume:
104
-
105
- - Network filesystems (SMB shares, NFS) do **not** guarantee O_APPEND
106
- atomicity. Spec stance: journal correctness is guaranteed on local FS only;
107
- `bclaw doctor` warns when the store sits on a network mount.
108
- - A crash mid-`write()` can leave a **torn tail** (partial line, no `\n`).
109
-
110
- ### 2.2 Framing: leading + trailing newline
111
-
112
- Each record is written as **one buffer**: `"\n" + JSON + "\n"`.
113
-
114
- The leading `\n` is the cheap, high-value trick: if the previous append tore
115
- (no trailing newline), our leading newline *terminates the torn fragment as
116
- its own malformed line* instead of letting our valid record be absorbed into
117
- it. Damage from a torn write is thereby capped at **exactly one event** — the
118
- torn one — never two. Readers already `split('\n').filter(Boolean)`, so empty
119
- lines from double-`\n` are free.
120
-
121
- Reader rules (normative):
122
- 1. Split on `\n`; skip empty lines.
123
- 2. A line that fails `JSON.parse` or schema validation: skip, count, surface
124
- via `bclaw doctor` (never silently — extend the current swallow).
125
- 3. A torn **tail** (last line, no trailing newline in file) is *expected* after
126
- a crash: skip without warning, but record `torn_tail: true` in doctor output.
127
-
128
- ### 2.3 Write path and the journal-first invariant
129
-
130
- All state-mutating appends happen **inside `mutate()`** (they already do —
131
- `appendEvent` is called from `persistStateUnlocked`). The store lock therefore
132
- serializes mutating appenders; O_APPEND atomicity is the backstop for the
133
- non-mutating observability events (session/run notifications) that may append
134
- without the lock.
135
-
136
- **Order inversion required.** Today `persistStateUnlocked` writes entity JSON
137
- files first, then appends the event (state.ts:196→200). As source of truth the
138
- journal must be written **first**:
139
-
140
- ```
141
- mutate(): append v2 event(s) [+ fsync] → update projection files → bump watermark
142
- ```
143
-
144
- Crash between append and projection write → projection is *stale*, lazy
145
- reconcile heals forward. The reverse order would allow a projection *from the
146
- future* — an event-less state the journal can never explain, and which a
147
- reconciler would wrongly **regress**. Journal-first is the single most
148
- important invariant in this spec.
149
-
150
- ### 2.4 fsync policy
151
-
152
- - Default: **one `fs.fsyncSync(fd)` per `mutate()` call**, after the last
153
- append, before projection writes. Mutations are user-action-frequency, not
154
- hot-loop; one fsync per mutation is affordable on NTFS.
155
- - `appendEvent` outside a mutation (observability events): no fsync.
156
- - Config escape hatch `store.journal.fsync: "mutation" | "never"` for
157
- pathological filesystems. No `"always"` tier until someone demonstrates a
158
- need. (OPEN QUESTION Q2: default for CI/test envs.)
159
- - The current `try/catch` that **swallows append errors must go** for v2
160
- state events: if the journal append fails inside a mutation, the mutation
161
- fails. A source of truth that silently drops writes is not a source of truth.
162
- Observability-only events may keep best-effort semantics.
163
-
164
- ---
165
-
166
- ## 3. Non-lossy rotation: segments + checkpoints
167
-
168
- ### 3.1 Layout
169
-
170
- ```
171
- .brainclaw/events/
172
- HEAD.json # { next_seq, active_segment } — written under store lock
173
- seg-00000001.jsonl # immutable once rolled; name = first seq it contains
174
- seg-00018000.jsonl # active segment (append target)
175
- archive/
176
- events.v1.jsonl # the parked legacy notification log (never deleted)
177
- ```
178
-
179
- - Segment file name encodes the **first seq** in it → locating seq N is a
180
- directory listing + binary search by name, no index file needed.
181
- - Roll when active segment ≥ 10 MB: under the store lock, write a fresh
182
- segment, update `HEAD.json`. **Never rename the active file** — readers
183
- holding it open (Windows!) are unaffected; rolled segments are immutable.
184
- - `HEAD.json` is small and rewritten atomically (temp+rename, as
185
- `writeFileAtomic` already does). It is a cache: if missing/corrupt it is
186
- rebuilt by listing segments and tail-reading the last one.
187
-
188
- ### 3.2 Checkpoints make old segments prunable
189
-
190
- A **checkpoint** is a special record (or run of records) appended like any
191
- event: `{v:2, seq, action:"checkpoint", item_type:"state", payload:{...full
192
- snapshot refs...}}` — concretely, a checkpoint emits one snapshot event per
193
- live entity plus a terminator record. After a checkpoint at seq C, every
194
- segment whose *last* seq < first-snapshot-of-C is **redundant for state
195
- rebuild** and moves to `archive/` (park, never delete — house rule).
196
-
197
- Checkpoint triggers (lazy, no daemon): on segment roll, and on
198
- `bclaw doctor --compact`. Cost: O(live entities), bounded, runs under the
199
- store lock.
200
-
201
- ### 3.3 Cursors become seq watermarks
202
-
203
- Replace `{offset, last_read}` byte cursors with `{last_seq, last_read}`.
204
- This fixes the existing rotation bug class outright: rotation/compaction
205
- **cannot invalidate a seq watermark**. `readUnseenEvents(agent)` = find
206
- segment containing `last_seq+1` (filename binary search), stream forward.
207
- If the watermark predates the oldest non-archived segment, the reader gets
208
- `{gap: true}` and a summary built from the checkpoint instead of replaying
209
- archaeology — notifications degrade gracefully, state rebuild does not need
210
- them.
211
-
212
- ---
213
-
214
- ## 4. Projections: lazy materialization
215
-
216
- ### 4.1 What is a projection
217
-
218
- Exactly the files we have today: `constraints/*.json`, `plans/*.json`, etc.,
219
- written by `saveVersionedJsonFile` (atomic, pretty-printed, git-diffable).
220
- Plus one new manifest:
221
-
222
- ```
223
- .brainclaw/projections.json # { applied_seq: 18342 }
224
- ```
225
-
226
- ### 4.2 Dirty detection = watermark comparison (no dirty flags, no daemon)
227
-
228
- Read path (`loadState`, single-entity gets):
229
-
230
- 1. Read `projections.json.applied_seq` and `HEAD.json.next_seq` — two tiny
231
- file reads, this is the staleness check and it is O(1).
232
- 2. Equal (the overwhelmingly common case: same-process read-after-write, or
233
- MCP worker spawned after a clean mutation) → serve projection files
234
- directly. **This is why worker-per-call stays cheap**: the fresh-path adds
235
- two small reads to today's behavior, nothing else.
236
- 3. Behind → acquire store lock, replay events `(applied_seq, head)` onto the
237
- projection (apply snapshot / tombstone per record), bump `applied_seq`,
238
- release. Replay cost is proportional to the *gap*, not the store.
239
- 4. Lock unavailable (reader racing a writer) → serve the stale projection
240
- with a `stale: true` annotation rather than block reads. (OPEN QUESTION
241
- Q3: is stale-read acceptable for claims? Probably not — claims may need
242
- read-through-journal.)
243
-
244
- This is precisely the pln#496 lazy-reconcile pattern: convergence at read
245
- paths, no background process.
246
-
247
- ### 4.3 How `mutateState` / `persistState` migrate
248
-
249
- End-state shape (phase 3, §6):
250
-
251
- ```ts
252
- mutateState(fn):
253
- mutate(lock):
254
- state = loadState() // projection, reconciled if behind
255
- result = fn(state)
256
- events = diffToEvents(prev, state) // snapshot events for changed entities
257
- appendEvents(events) + fsync
258
- applyToProjection(events) // same writes syncDirectory does today
259
- writeWatermark(applied_seq = last seq)
260
- return result
261
- ```
262
-
263
- `diffToEvents` compares by entity identity + shallow equality — note this
264
- replaces today's "rewrite every file on every mutation" with **write only what
265
- changed**, which is where the "single-entity ops not O(store)" perf target is
266
- actually won. `syncDirectory`'s deleteMissing semantics map to tombstone
267
- events; the trp_d5595086 guard (never unlink unparseable files) carries over
268
- unchanged on the projection side.
269
-
270
- The full-state-load in `mutateState` stays in phase 1–2 (it's correct, just
271
- not optimal); per-entity loads land in phase 3 once registries
272
- (assignments/runs/loops, which already do per-entity files) and State entities
273
- share the journal apply path.
274
-
275
- ---
276
-
277
- ## 5. Causal ordering & multi-writer
278
-
279
- ### 5.1 Inside one store
280
-
281
- - **`seq` is the only ordering authority.** Assigned under the store lock from
282
- `HEAD.json.next_seq`. Since every state mutation already runs under that
283
- lock (mutation-pipeline invariant), seq assignment adds zero new
284
- coordination.
285
- - `ts` is diagnostic. Clock skew, DST, multi-machine clones — none of it can
286
- reorder state, because nothing orders by `ts`.
287
- - Observability events appended outside the lock get `seq: null` and are
288
- excluded from state rebuild; they order best-effort by file position. If
289
- this proves annoying, they can cheaply take the lock — but do not let
290
- notification traffic contend with mutations by default.
291
- - Per-writer `(writer_id, writer_seq)` is **also** recorded (a tiny counter in
292
- the agent's cursor file) — unused locally, load-bearing for federation.
293
-
294
- ### 5.2 Federation (Pull-and-Materialize consumes the journal)
295
-
296
- The journal is the sync substrate the federation decisions assumed:
297
-
298
- - A remote pulls segments (append-only files = rsync/git/dumb-bus friendly,
299
- no daemon).
300
- - Materialization replays *foreign* snapshot events into the local store as
301
- **signaling entities** (candidates/handoffs) per the
302
- cross-project-signaling decision — foreign events do not directly mutate
303
- local execution entities.
304
- - Idempotency key for merge = `(origin_store_id, seq)`; duplicate pulls are
305
- no-ops. `origin_store_id` is stamped once per segment header record, not
306
- per event, to save bytes.
307
- - Conflict semantics (same entity edited in two stores): **out of scope
308
- here**; the journal guarantees each side's history is complete and
309
- replayable, which is the precondition. (OPEN QUESTION Q4 — Codex schema
310
- review territory.)
311
-
312
- ### 5.3 Adversarial scenarios (pre-answered for the critique round)
313
-
314
- | Attack | Outcome under this design |
315
- |---|---|
316
- | Crash mid-append | Torn tail; leading-`\n` framing caps loss at 1 event; reader skips; doctor reports. Mutation that crashed never confirmed → caller retries; projection never ahead of journal (journal-first). |
317
- | Two mutating writers | Impossible by construction — store lock (hardened today, token-owned, refresh-padded) serializes them; seq assignment is inside the lock. |
318
- | Stray appender without lock | O_APPEND keeps records intact; record has `seq:null` → cannot corrupt state order. |
319
- | Rotation during read | Rolled segments are immutable; active segment never renamed; seq watermarks survive. The v1 bug (rename + cursor reset) is structurally removed. |
320
- | Clock skew / ts collision | Irrelevant to state: seq orders. |
321
- | 100k-event store | Reads never replay history: fresh-path is O(1) check + projection read; stale-path replays only the gap; rebuild-from-zero bounded by last checkpoint. |
322
- | Corrupt line mid-segment | Snapshot self-containment: only that event lost; later snapshot of same entity supersedes. Doctor flags the segment. |
323
- | `HEAD.json` corrupt/lost | Rebuilt from segment listing + tail read; it is a cache, not truth. |
324
- | `projections.json` lost | Worst case: full replay from last checkpoint. Truth intact. |
325
-
326
- ---
327
-
328
- ## 6. Migration plan
329
-
330
- Flag: `store.journal_v2: off | dual | primary` (config.yaml), default `off`.
331
-
332
- **Phase 0 — ship the format, change nothing.** Land record schema (zod),
333
- segment reader/writer, doctor checks. v1 `events.jsonl` untouched.
334
-
335
- **Phase 1 — `dual`: journal-first dual-write.**
336
- - `bclaw migrate journal` (one-shot, upgrade-style): backup store; emit
337
- **genesis checkpoint** = one snapshot event per current entity built from
338
- the projection files (the only truth we have — the 17k v1 events are not
339
- reconstructible and are not translated, just parked to
340
- `events/archive/events.v1.jsonl`, still readable for history/forensics);
341
- initialize `HEAD.json`, `projections.json`.
342
- - `persistStateUnlocked` reordered: append v2 events → existing
343
- `writeStateDirectories` → watermark. Notifications switch to seq cursors.
344
- - **Rollback:** set flag `off`, restore nothing — projection files were
345
- written on every mutation and are exactly today's format. Park the
346
- `events/` dir. Zero data loss in either direction. This must not regress
347
- today's lock/mutation hardening: the only change inside the lock is
348
- ordering + two small file writes.
349
-
350
- **Phase 2 — `primary`: reads heal from journal.** Lazy reconcile on read
351
- paths (§4.2). Acceptance gate: kill -9 storms in tests (crash between append
352
- and projection) always converge.
353
-
354
- **Phase 3 — per-entity ops.** Single-entity mutations append + patch one
355
- projection file without full-store load/rewrite; registries unify on the same
356
- path. Perf gates: `bclaw_work` cold read < 1 s on a 100k-event store;
357
- single-entity op cost independent of store size.
358
-
359
- Each phase ships dark behind the flag, soak-tested by dogfooding (this repo's
360
- own store is the canary, ~17k events of realistic traffic).
361
-
362
- ---
363
-
364
- ## 7. Hard-constraint checklist
365
-
366
- - **Zero new runtime deps**: snapshots need no diff lib; zod (already present)
367
- validates records; segments are plain JSONL. ✔
368
- - **Windows + POSIX**: O_APPEND/FILE_APPEND_DATA local-FS semantics; no rename
369
- of open files; no fsync assumptions beyond Node built-ins; paths via
370
- existing `memoryDir`. ✔
371
- - **Git-diffable store identity**: projection files unchanged in format and
372
- location; segments are append-only (clean diffs); archives parked. ✔
373
- - **No daemon**: all convergence at read/write paths under the existing lock. ✔
374
- - **Cheap MCP worker-per-call**: fresh-path = two extra tiny reads. ✔
375
-
376
- ## 8. Open questions (for Codex review / Juan product call)
377
-
378
- - **Q1** Large-payload escape hatch (`payload_ref`): spec now or explicitly
379
- defer? (I say defer; flagging because it changes the record schema if added.)
380
- - **Q2** fsync default in tests/CI — `"never"` to keep suite fast, or same as
381
- prod to keep fidelity? (Test-runner env contamination history says: same as
382
- prod, measure first.)
383
- - **Q3** Are stale projection reads acceptable when the lock is contended, or
384
- must claims/locks-adjacent entities read through the journal? Product call:
385
- consistency vs. never-blocking reads.
386
- - **Q4** Federation conflict semantics (LWW per entity vs field merge vs
387
- always-surface-as-candidate). Needs Codex's schema-review pass; journal
388
- design above is agnostic.
389
- - **Q5** Should journal segments be committed by `commitMemoryChange`
390
- (memory-git) or gitignored within the store's internal repo? Append-only
391
- files diff well, but double-storing 10 MB segments in git history may bloat;
392
- checkpoint-only commits are a middle ground.
393
- - **Q6** Observability events: keep them in the same journal (current
394
- proposal) or split notification stream from state journal entirely? Same
395
- file is simpler; split avoids notification traffic inflating segments.
396
-
397
- ## Memory ids relied upon
398
-
399
- - feedback_lazy_reconcile_pattern (pln#496) — projection reconciliation shape (§4).
400
- - trp_d5595086 — never-unlink-unparseable guard carried into projection apply (§4.3); load-swallow lesson behind §2.2 reader rules.
401
- - federation_architecture_decisions / cross_project_signaling_vs_execution — no daemon, Pull-and-Materialize, signaling-only foreign writes (§5.2).
402
- - trp_e85e9fbe — Windows/POSIX divergence discipline (§2.1, §7); CI-on-both-platforms gate for phases.
403
- - trp_09988deb — config/backup hygiene → upgrade-style backup + park-don't-delete in migration (§6).
404
- - feedback_bisect_state_before_code — motivates doctor-visible counters over silent skips (§2.2).