brainclaw 1.8.0 → 1.9.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 (140) hide show
  1. package/README.md +12 -11
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +138 -13
  4. package/dist/commands/add-step.js +1 -1
  5. package/dist/commands/bootstrap.js +2 -26
  6. package/dist/commands/check-security-mcp.js +50 -33
  7. package/dist/commands/check-security.js +86 -43
  8. package/dist/commands/claim.js +22 -21
  9. package/dist/commands/confirm.js +26 -0
  10. package/dist/commands/context-diff.js +1 -1
  11. package/dist/commands/dispatch-watch.js +142 -0
  12. package/dist/commands/doctor.js +113 -2
  13. package/dist/commands/estimation-report.js +115 -16
  14. package/dist/commands/harvest.js +285 -22
  15. package/dist/commands/init.js +123 -21
  16. package/dist/commands/loops-handlers.js +4 -0
  17. package/dist/commands/mcp-read-handlers.js +198 -29
  18. package/dist/commands/mcp.js +588 -92
  19. package/dist/commands/memory.js +21 -17
  20. package/dist/commands/migrate.js +81 -17
  21. package/dist/commands/prune.js +78 -4
  22. package/dist/commands/reflect.js +26 -20
  23. package/dist/commands/register-agent.js +57 -1
  24. package/dist/commands/repair.js +20 -0
  25. package/dist/commands/session-end.js +15 -6
  26. package/dist/commands/session-start.js +18 -1
  27. package/dist/commands/setup-security.js +39 -18
  28. package/dist/commands/setup.js +26 -27
  29. package/dist/commands/stale.js +16 -2
  30. package/dist/commands/uninstall.js +126 -34
  31. package/dist/commands/update-step.js +6 -0
  32. package/dist/commands/worktree.js +60 -0
  33. package/dist/core/actions.js +12 -3
  34. package/dist/core/agent-capability.js +11 -13
  35. package/dist/core/agent-files.js +844 -547
  36. package/dist/core/agent-integrations.js +0 -3
  37. package/dist/core/agent-inventory.js +67 -0
  38. package/dist/core/agent-registry.js +163 -29
  39. package/dist/core/agentrun-reconciler.js +33 -2
  40. package/dist/core/agentruns.js +7 -1
  41. package/dist/core/ai-agent-detection.js +31 -44
  42. package/dist/core/archival.js +15 -9
  43. package/dist/core/assignment-reconciler.js +56 -0
  44. package/dist/core/assignment-sweeper.js +127 -4
  45. package/dist/core/assignments.js +69 -11
  46. package/dist/core/bootstrap.js +233 -67
  47. package/dist/core/brainclaw-version.js +22 -0
  48. package/dist/core/candidates.js +21 -1
  49. package/dist/core/claims.js +313 -150
  50. package/dist/core/config.js +6 -1
  51. package/dist/core/context-diff.js +148 -20
  52. package/dist/core/context.js +129 -8
  53. package/dist/core/coordination.js +22 -3
  54. package/dist/core/dispatch-status.js +79 -5
  55. package/dist/core/dispatcher.js +64 -11
  56. package/dist/core/entity-operations.js +45 -24
  57. package/dist/core/entity-registry.js +31 -5
  58. package/dist/core/event-log.js +138 -21
  59. package/dist/core/events/checkpoint.js +258 -0
  60. package/dist/core/events/genesis.js +220 -0
  61. package/dist/core/events/journal.js +507 -0
  62. package/dist/core/events/materialize.js +126 -0
  63. package/dist/core/events/registry-post-image.js +110 -0
  64. package/dist/core/events/verify.js +109 -0
  65. package/dist/core/execution-adapters.js +23 -0
  66. package/dist/core/facade-schema.js +38 -0
  67. package/dist/core/gc-semantic.js +130 -5
  68. package/dist/core/handoff-snapshot.js +68 -0
  69. package/dist/core/ids.js +19 -8
  70. package/dist/core/instruction-templates.js +34 -115
  71. package/dist/core/io.js +39 -3
  72. package/dist/core/json-store.js +10 -1
  73. package/dist/core/lock.js +153 -28
  74. package/dist/core/loops/bootstrap-acquire.js +25 -1
  75. package/dist/core/loops/facade-schema.js +2 -0
  76. package/dist/core/loops/hooks/survey-signals-baseline.js +36 -0
  77. package/dist/core/loops/index.js +1 -0
  78. package/dist/core/loops/presets/bootstrap.js +7 -0
  79. package/dist/core/loops/store.js +17 -0
  80. package/dist/core/loops/verbs.js +24 -1
  81. package/dist/core/markdown.js +8 -76
  82. package/dist/core/mcp-command-resolution.js +245 -0
  83. package/dist/core/memory-compactor.js +5 -3
  84. package/dist/core/memory-lifecycle.js +282 -0
  85. package/dist/core/merge-risk.js +150 -0
  86. package/dist/core/messaging.js +8 -1
  87. package/dist/core/migration.js +11 -1
  88. package/dist/core/observer-mode.js +26 -0
  89. package/dist/core/operations/memory-mutation.js +90 -65
  90. package/dist/core/operations/plan.js +27 -1
  91. package/dist/core/protocol-skills.js +210 -0
  92. package/dist/core/reflection-safety.js +6 -7
  93. package/dist/core/reputation.js +84 -2
  94. package/dist/core/runtime-signals.js +71 -9
  95. package/dist/core/runtime.js +84 -1
  96. package/dist/core/schema.js +114 -0
  97. package/dist/core/security-detectors.js +125 -0
  98. package/dist/core/security-extract.js +189 -0
  99. package/dist/core/security-guard.js +107 -29
  100. package/dist/core/security-packages.js +121 -0
  101. package/dist/core/security-scoring.js +76 -9
  102. package/dist/core/security.js +34 -2
  103. package/dist/core/sequence.js +11 -2
  104. package/dist/core/setup-flow.js +141 -13
  105. package/dist/core/staleness.js +72 -1
  106. package/dist/core/state.js +250 -54
  107. package/dist/core/store-resolution.js +19 -5
  108. package/dist/core/worktree.js +72 -8
  109. package/dist/facts.js +8 -8
  110. package/dist/facts.json +7 -7
  111. package/docs/PROTOCOL.md +223 -0
  112. package/docs/cli.md +11 -10
  113. package/docs/concepts/coordinator-runbook.md +129 -0
  114. package/docs/concepts/event-log-store-critique-A.md +333 -0
  115. package/docs/concepts/event-log-store-critique-B.md +353 -0
  116. package/docs/concepts/event-log-store-phase0-measurements.md +58 -0
  117. package/docs/concepts/event-log-store-proposal-A.md +365 -0
  118. package/docs/concepts/event-log-store-proposal-B.md +404 -0
  119. package/docs/concepts/event-log-store.md +928 -0
  120. package/docs/concepts/identity-model-proposal.md +371 -0
  121. package/docs/concepts/memory.md +5 -4
  122. package/docs/concepts/observer-protocol.md +361 -0
  123. package/docs/concepts/parallel-merge-protocol.md +71 -0
  124. package/docs/concepts/plans-and-claims.md +43 -0
  125. package/docs/concepts/skills.md +78 -0
  126. package/docs/concepts/workspace-bootstrapping.md +61 -0
  127. package/docs/integrations/agents.md +4 -4
  128. package/docs/integrations/cline.md +10 -11
  129. package/docs/integrations/codex.md +2 -2
  130. package/docs/integrations/continue.md +5 -5
  131. package/docs/integrations/copilot.md +14 -12
  132. package/docs/integrations/openclaw.md +7 -6
  133. package/docs/integrations/overview.md +7 -7
  134. package/docs/integrations/roo.md +3 -3
  135. package/docs/integrations/windsurf.md +6 -6
  136. package/docs/mcp-schema-changelog.md +29 -2
  137. package/docs/quickstart.md +48 -47
  138. package/docs/security.md +174 -15
  139. package/docs/storage.md +4 -2
  140. package/package.json +8 -6
@@ -0,0 +1,361 @@
1
+ # Observer Protocol — language-agnostic read-only surfaces
2
+
3
+ Status: spec (pln#560 step 1). Pivot deliverable: it serves the VS Code
4
+ extension, the JetBrains plugin, and any future surface identically. Companion
5
+ to `event-log-store.md` (the journal this protocol consumes) and the VS Code
6
+ vision §5 (the UX it powers).
7
+
8
+ ## 1. The one rule
9
+
10
+ **An observability surface is a pure consumer of the event journal. It never
11
+ acquires a store lock, never writes inside `.brainclaw/`, never runs a polling
12
+ timer against the MCP server for display.** It tails the append-only journal,
13
+ projects board state in memory, and refreshes the affected section when a new
14
+ record arrives. The MCP server is reserved for *actions* (accept / release /
15
+ dispatch / transition) through a separate, lazily-created client.
16
+
17
+ Why this exists (2026-06-10 calibration): the prior extension "read" the board
18
+ by calling a path that mutated and git-committed the entire store under the
19
+ lock (`autoAcknowledge → persistState`, held >5s), ran the agent-run reconciler
20
+ twice per poll with locked writes, created ~120 locked "unverified" event files
21
+ per hour per run, and impersonated the parent shell's agent identity —
22
+ consuming that agent's cursor. A dashboard is not an agent. This protocol makes
23
+ that class of bug unrepresentable: a conforming observer *cannot* write.
24
+
25
+ ## 2. What the observer reads
26
+
27
+ The journal defined in `event-log-store.md`:
28
+
29
+ ```
30
+ .brainclaw/events/
31
+ meta.json # { next_seq, active_segment, entity_revs } — a cache
32
+ seg-<firstSeq>.jsonl # immutable once rolled; named by first seq it holds
33
+ seg-<firstSeq>.jsonl # active = lexicographically-last segment
34
+ checkpoints/ # NOT YET EMITTED by the writer (see §5) — directory
35
+ ckpt-<seq>.json # may be absent today; self-contained state manifest
36
+ ```
37
+
38
+ **Segment naming is normative.** `firstSeq` is encoded as a **decimal,
39
+ zero-padded to 8 digits** (e.g. `seg-00018342.jsonl`) so directory-listing
40
+ lex-sort matches numeric seq order with no parsing. Implementations MUST emit
41
+ and accept exactly this format; any other padding (or none) breaks the
42
+ binary-search-by-filename in §5. The 8-digit field overflows at `seq > 1e8`;
43
+ at the historical write rate (~17k events to date) this is decades away, but
44
+ a journal that crosses the boundary requires a coordinated widen-and-pad
45
+ migration in writer + every observer (deferred until needed; flagged here so
46
+ no implementer assumes the pad width is incidental).
47
+
48
+ `meta.json` is advisory only (§4): the observer reads it to cheaply detect
49
+ "did anything change" but never trusts its `entity_revs` or `next_seq` for
50
+ correctness — those are derived from the journal tail itself.
51
+
52
+ Record envelope (v2), one JSON object per line:
53
+
54
+ ```jsonc
55
+ { "v": 2, "seq": 18342, "ts": "…", "writer": "w_…", "agent": "claude-code",
56
+ "action": "update", "item_type": "plan", "item_id": "pln_…",
57
+ "entity_rev": 7, "summary": "…", "payload": { /* full post-image */ } }
58
+ ```
59
+
60
+ Action → class mapping (the observer needs the class, not a hardcoded verb
61
+ list — it is `ACTION_CLASS_BY_ACTION` in `event-log.ts`, and MUST be mirrored
62
+ in every implementation or fetched from a shared manifest):
63
+
64
+ | Class | Effect on the projection |
65
+ |---|---|
66
+ | `entity-state` (`create`,`update`,`accept`,`reject`,`claim`,`release_claim`,`rollback`,`upgrade`,`backfill`) | upsert `payload` at `(item_type,item_id)` |
67
+ | `tombstone` (`delete`) | remove `(item_type,item_id)` |
68
+ | `journal-meta` (`checkpoint_ref`,`journal_note`,`seq_repair`,`federation_apply`) | ignore for state; `checkpoint_ref` is a bootstrap hint (§5) |
69
+ | `observability` (`session_start`,`session_end`,`assignment_offered`,`assignment_progress`,`run_progress`) | activity feed only — never a state upsert |
70
+ | `registry-lifecycle` (`assignment_*`,`run_*`) | upsert when `payload` present (phase 1.5+), else a status/activity signal |
71
+
72
+ The observer is **forward-compatible**: an unknown `action` whose class it
73
+ cannot resolve is applied as `entity-state` iff it carries a `payload` and an
74
+ `item_id`, else treated as an activity signal. Never crash on an unknown verb.
75
+
76
+ ## 3. The cursor lives OUTSIDE the store
77
+
78
+ The observer's read position is a **seq watermark** persisted in *client*
79
+ storage, never in `.brainclaw/`:
80
+
81
+ - VS Code: `ExtensionContext.workspaceState`, key `bclaw.observer.cursor.<project_id>`.
82
+ - JetBrains: `PropertiesComponent` / project-scoped state, same key shape.
83
+ - Generic: any client-private kv keyed by `project_id`.
84
+
85
+ Shape: `{ seq: number, checkpoint_seq: number }`. `seq` = highest record seq
86
+ applied; `checkpoint_seq` = the checkpoint the in-memory projection was last
87
+ seeded from (for fast re-bootstrap). The store's own `.cursors/` directory is
88
+ the AGENTS' read position and is **off-limits** to observers — touching it is
89
+ the identity-leak bug this protocol forbids.
90
+
91
+ Rationale: a watermark survives segment rotation, compaction, and archival
92
+ (byte offsets do not). It is private to the surface, so N observers never
93
+ interfere with each other or with agents.
94
+
95
+ ## 4. Change detection — a file watch, not a poll, not a lock
96
+
97
+ The observer watches the journal directory for growth and reacts:
98
+
99
+ 1. Watch `.brainclaw/events/` (the active segment's size/mtime, and creation of
100
+ new `seg-*.jsonl`). VS Code: `FileSystemWatcher` on `events/seg-*.jsonl` +
101
+ `meta.json`. JetBrains: `VirtualFileListener` / NIO `WatchService`. Generic
102
+ fallback: stat the active segment on a *long* interval (≥10 s) — this is a
103
+ stat, not an MCP call, and acquires no lock.
104
+ 2. On a growth signal, **tail forward** from `cursor.seq` (§5) and apply records
105
+ to the in-memory projection.
106
+ 3. `meta.json` is advisory only; never trust it for correctness — the journal
107
+ tail is the truth (it may be a stale cache mid-write). Use it only to detect
108
+ "did anything change" cheaply.
109
+
110
+ There is no MCP server process for display. The watcher is OS-level; the read
111
+ is a file read. Under the 2026-06-10 load (3 workers + open surface) this yields
112
+ zero lock acquisitions by the surface — the validation gate (step 3).
113
+
114
+ ## 5. Bootstrap and tail algorithm
115
+
116
+ **Status of checkpoint emission (2026-06-12, pln#543 step 4 landed):** the
117
+ writer does NOT yet produce `checkpoints/ckpt-*.json` files — checkpoint
118
+ emission ships with step 3/5 of pln#543. Until then, the empty-seed + full
119
+ tail path below is the **primary** cold-start path in production, not a
120
+ degenerate fallback. The checkpoint-first path is the spec the consumer must
121
+ implement for forward-compatibility; an observer that hard-requires a
122
+ checkpoint at activation is broken against today's store. The perf targets
123
+ in §10 assume checkpoint emission; until it ships, "activation → first
124
+ summary" is bounded by the full tail length instead (~10 MB of segments in
125
+ the typical case, sub-second in practice, but the budget no longer has
126
+ slack).
127
+
128
+ **Cold start (no cursor, or cursor below the oldest live segment):**
129
+
130
+ 1. **If a verified checkpoint exists**, load the newest `ckpt-<S>.json` →
131
+ seed the in-memory projection (full post-image set at head `S`). Set
132
+ `cursor.checkpoint_seq = S`. (Today: this branch is dead until the
133
+ writer ships checkpoint emission.)
134
+ 2. **Otherwise** (today's primary path), seed from the empty projection
135
+ with `cursor.checkpoint_seq = 0`.
136
+ 3. Tail every record with `seq > checkpoint_seq` across segments in
137
+ (segment, file-line) order; apply by class (§2). With no checkpoint
138
+ this is a full replay from seq 1, bounded by retention (the
139
+ `events/archive/` floor — segments are park-don't-deleted past the
140
+ second-newest verified checkpoint; with no checkpoint, no segment is
141
+ ever eligible for archive, so "the journal" = "every segment ever
142
+ written" until checkpoint emission ships).
143
+ 4. Set `cursor.seq` to the last applied seq. Render.
144
+
145
+ If the cursor's `seq` is **below the oldest non-archived segment's first
146
+ seq** (gap — segments archived past the watermark), discard the cursor and
147
+ cold-start: notifications degrade, state never does. Today no segment is
148
+ ever archived (gc requires a verified checkpoint floor, see §2.3 of the
149
+ store spec), so this branch is unreachable in production until checkpoint
150
+ emission ships — but the rule is normative regardless: an observer that
151
+ crashes on a gap is broken against any future store.
152
+
153
+ **Warm tail (cursor present, within live segments):**
154
+
155
+ 1. Binary-search the segment whose name (`seg-<firstSeq>`) contains
156
+ `cursor.seq + 1` (filenames sort by first seq).
157
+ 2. Stream forward from that point across segments; apply by class.
158
+ 3. A **torn tail** (final line unparseable or missing trailing `\n`) is expected
159
+ crash residue mid-write by an agent — skip it; it reappears complete on the
160
+ next growth signal. Never block on it.
161
+ 4. A mid-file unparseable line is logged and skipped (do not halt the tail).
162
+ 5. Advance `cursor.seq` only over records actually applied.
163
+
164
+ Replay order is always (segment order, then file-line order) — never sorted by
165
+ seq (matches the store's own reducer; a dup `seq` from a lock-steal applies
166
+ later-line-wins, harmlessly, in a read-only projection).
167
+
168
+ ## 6. Board projection — which records touch which section
169
+
170
+ The in-memory projection is `Map<item_type, Map<item_id, payload>>` plus a
171
+ bounded recent-activity ring (observability + registry signals, last N). The
172
+ board sections are derived; a record invalidates only the sections its
173
+ `item_type` feeds, and only those re-render (push-by-affected-section, §5.3):
174
+
175
+ | `item_type` | Invalidates sections |
176
+ |---|---|
177
+ | `plan` | IN_PROGRESS, SPRINTS, BACKLOG, ATTENTION (badge), SYSTEM (counts) |
178
+ | `claim` | IN_PROGRESS, AGENTS (roster freshness) |
179
+ | `assignment` | IN_PROGRESS, ATTENTION (blocked/failed), "Recently terminal" |
180
+ | `agent_run` | IN_PROGRESS (worker rows), AGENTS, "Recently terminal" |
181
+ | `candidate` | ATTENTION (human-review), CANDIDATES |
182
+ | `action` | ATTENTION (the dominant attention input) |
183
+ | `constraint`/`decision`/`trap` | SYSTEM (counts), TRAPS |
184
+ | `handoff` | ACTIVITY, SYSTEM (counts) |
185
+ | `sequence` | SPRINTS |
186
+ | `session`/`*_progress` (observability) | ACTIVITY feed only — never a section state change |
187
+
188
+ `attention_required` is computed by the observer from the projection (actions +
189
+ human candidates + blocked/failed assignments + failed runs + evidence-
190
+ contradicted terminals), matching what the server-side composite returns — the
191
+ surface must not under-count by reading "actions only" (the pln#559 fix, now in
192
+ the projection rule).
193
+
194
+ ### 6.1 Dual-mode coverage gap (CLOSED by pln#568 phase 1.5)
195
+
196
+ > **Status (pln#568):** the writer-side gap below is **closed**. The
197
+ > registry / coordination families (claim, assignment, agent_run,
198
+ > action_required [journaled under item_type `state`], candidate, sequence,
199
+ > and SHARED runtime_note) now emit full entity-state **post-images** on their
200
+ > persist chokepoint (`src/core/events/registry-post-image.ts`), and the
201
+ > observer materializer projects them (`board-projection.ts` ARRAY_SLOT).
202
+ >
203
+ > **Cutover signal (O2, resolved):** an observer switches a registry family
204
+ > from the MCP `board_summary` seed to the journal only once the journal
205
+ > carries the `journal_note` kind **`registry_genesis`** marker — emitted by
206
+ > `runRegistryGenesisSupplement` (run via `brainclaw migrate --enable-journal`)
207
+ > after it backfills every pre-existing registry entity. The marker is the
208
+ > safety gate: without a complete backfill a partially-journaled store would
209
+ > undercount the attention badge (trp#559). `BoardObserver.registryAuthoritative()`
210
+ > tracks the marker (sticky, re-derived on cold start by replaying from the
211
+ > checkpoint floor); `mergeCounts(journal, seed, journalActive, registryAuthoritative)`
212
+ > takes claims/assignments/runs/actions from the journal when it is set, and
213
+ > from the seed otherwise. `agents`/`sessions` are never journaled → always seed.
214
+ > A store that has NOT run the supplement keeps the seed (no regression).
215
+ >
216
+ > The historical (pre-pln#568) description below is kept for context.
217
+
218
+ The journal classifies records into five classes (§2). In phase 1 / `dual`
219
+ mode — what runs today after pln#543 step 4 — **registry-lifecycle records
220
+ are payload-OPTIONAL** (event-log-store.md §2.1.1, J4); the dual-write path
221
+ in `src/core/event-log.ts:152` forwards `assignment_*` and `run_*` events to
222
+ the journal with `item_id` only, no `payload`. The §2 rule "upsert when
223
+ payload present, else a status/activity signal" means today's journal carries
224
+ **no post-images for `assignment` or `agent_run`**: the in-memory projection
225
+ has zero rows for those item_types, and the materializer
226
+ (`src/core/events/materialize.ts`) only enumerates the 5 memory families
227
+ (constraint/decision/trap/handoff/plan).
228
+
229
+ Consequence for the §6 mapping table: until phase 1.5 ships, the rows that
230
+ the table claims `assignment` / `agent_run` / `claim` populate (IN_PROGRESS
231
+ worker rows, ATTENTION blocked/failed, Recently terminal under IN_PROGRESS)
232
+ cannot be drawn from the journal alone. A conforming observer in dual mode
233
+ MUST:
234
+
235
+ - Seed those sections at activation from a **single observer-flagged
236
+ `bclaw_context(kind: "board_summary")`** call (no timer, no poll) — that
237
+ read is lock-free under the §8 observer contract (validated against
238
+ `getDispatchStatus` and `loadAssignment`/`loadAgentRun`, which are pure
239
+ projection reads — `mcp-read-handlers.ts:1916`, `json-store.ts:47`); and
240
+ - Mark those sections "live-view degraded" in the tooltip until phase 1.5,
241
+ so the operator can tell journal-driven sections (memory entities,
242
+ attention badges) from MCP-seeded sections (workers, lifecycle).
243
+
244
+ Memory-entity sections (plan / constraint / decision / trap / handoff /
245
+ sequence / handoff-derived ACTIVITY) ARE journal-driven today via the
246
+ per-entity diff in `persistState` (`src/core/state.ts:400`) — the protocol
247
+ delivers its full value for them.
248
+
249
+ ### 6.2 Section ID glossary
250
+
251
+ The §6 table uses display names; the canonical IDs in
252
+ `vscode-extension/src/board-tree.ts:321` are `attention | in-progress |
253
+ sprints | backlog | system | agents | candidates | activity | plans | claims
254
+ | assignments | runs | actions | handoffs | sprint | traps | cross-project`.
255
+ "Recently terminal" is **not** a top-level section — it is a sub-node
256
+ rendered under `in-progress`. The board also surfaces `cross-project`
257
+ (federation incoming signals) and `linked_projects`, neither of which has a
258
+ single journal `item_type` today: cross-project signals arrive via the
259
+ handoff/candidate streams (already covered), `linked_projects` is derived
260
+ from project config and is intentionally NOT a journal concern.
261
+
262
+ The projection is **state**, not administrative belief: a worker row's health
263
+ comes from evidence in the records (commits/fs signals carried on
264
+ registry-lifecycle payloads when present), not from a bare status field that the
265
+ 2026-06-10 log proved lies. Where richer evidence requires it, the surface MAY
266
+ call `bclaw_dispatch_status` through the actions client (§7) — that is a
267
+ read-only MCP call, used sparingly (per visible terminal row), not a poll.
268
+
269
+ ## 7. Actions go through a separate, lazy MCP client
270
+
271
+ Mutations (accept candidate, release claim, dispatch, transition, complete step)
272
+ are the *only* reason an observer talks to the MCP server. Rules:
273
+
274
+ - One lazily-created MCP client per project, spun up on first action, idle-timed
275
+ out after inactivity. Never created just to display.
276
+ - Distinct from any agent session: the client identifies as an **observer
277
+ principal** (see §8), so its calls never adopt an agent's claim/cursor.
278
+ - After an action, the observer does NOT optimistically mutate its projection;
279
+ it waits for the resulting journal record(s) to arrive via the tail (§5) and
280
+ re-projects. Single source of truth, no split-brain. (A short-lived "pending"
281
+ affordance on the clicked item is a UI concern, not projection state.)
282
+ - `bclaw_dispatch_status` and other read-only facades are permitted through this
283
+ client for on-demand evidence enrichment, but are never on a timer.
284
+
285
+ ## 8. Observer identity (no impersonation, no side effects)
286
+
287
+ The surface declares itself an observer so the server suppresses every write a
288
+ read would otherwise trigger:
289
+
290
+ - Transport signal: `BRAINCLAW_OBSERVER=1` in the action client's env, and/or
291
+ MCP `clientInfo.name = "brainclaw-observer/<surface>"`.
292
+ - Server contract (already implemented, pln#558): observer reads do not
293
+ `autoAcknowledge`, do not run agent-run reconciliation, do not advance
294
+ `readUnseenEvents` cursors, do not implicit-heartbeat or auto-register an
295
+ identity. This protocol is the client half of that contract: even the read-
296
+ only facade calls in §7 carry the observer flag.
297
+ - The observer never presents an agent name as the actor of anything. Actions
298
+ the human triggers are attributed to the human operator principal, not to a
299
+ spawned agent.
300
+
301
+ ## 9. Failure modes and degradation
302
+
303
+ | Condition | Behavior |
304
+ |---|---|
305
+ | `events/` absent (journal off / not migrated) | Fall back to a single MCP `board_summary` read at activation (no timer); show a "journal off — limited live view" hint. The surface still works, just not push-driven. |
306
+ | Cursor gap (archived past watermark) | Cold-start from newest checkpoint (§5); silent — state is correct, only missed-activity history is lost. |
307
+ | Checkpoint missing/corrupt | Fall back to the previous checkpoint, replay more segments (the two-checkpoint floor guarantees one exists); if none, seed empty + full tail. |
308
+ | Torn / unparseable line | Skip, keep tailing (§5). |
309
+ | Active segment shrinks / meta regresses | Trust the journal tail, re-derive; never write a "repair" (that is an agent/doctor job). |
310
+ | Watch unavailable (network FS, sandbox) | Degrade to a long-interval stat of the active segment; still zero locks. |
311
+
312
+ ## 10. Performance budget (vision §5.3, restated as observer obligations)
313
+
314
+ | Operation | Target | Hard limit | How the protocol meets it |
315
+ |---|---|---|---|
316
+ | Activation → first summary | 500 ms | 2 s | seed from newest checkpoint, no full replay |
317
+ | Summary refresh | 300 ms | 1 s | apply only the new tail records |
318
+ | Section expand (warm) | 50 ms | 200 ms | projection is in memory; expand reads the map |
319
+ | Section expand (cold) | 500 ms | 2 s | first projection build from checkpoint+tail |
320
+ | Action round-trip | 500 ms | 2 s | lazy MCP client; result observed via tail |
321
+
322
+ Out of budget → surface in tooltip + a "performance degraded" status-bar
323
+ indicator (never escalate by calling a heavier path — that is the contention-
324
+ breeds-contention bug this protocol exists to kill).
325
+
326
+ ## 11. Language-agnostic conformance checklist
327
+
328
+ A surface in any language conforms iff:
329
+
330
+ 1. It reads only files under `.brainclaw/events/` (+ checkpoints) and writes
331
+ nothing under `.brainclaw/`.
332
+ 2. Its cursor is a seq watermark in client-private storage, keyed by
333
+ `project_id`, never in the store's `.cursors/`.
334
+ 3. It seeds from the newest verified checkpoint and tails by (segment, line)
335
+ order, applying records by action *class*, tolerant of unknown verbs and torn
336
+ tails.
337
+ 4. Change detection is an OS file watch (or long-interval stat) — never an MCP
338
+ poll, never a lock.
339
+ 5. Mutations go through a separate lazy MCP client flagged as an observer
340
+ principal; the projection updates only from the resulting journal records.
341
+ 6. `attention_required` and worker health are computed from journal evidence,
342
+ not from administrative status fields alone.
343
+
344
+ Reference implementation: the VS Code extension (pln#560 step 2). The JetBrains
345
+ plugin (next plan) implements this same checklist in Kotlin — its existence is
346
+ the cross-language validation that this protocol, not the TypeScript code, is
347
+ the contract.
348
+
349
+ ## 12. OPEN QUESTIONS
350
+
351
+ Carried from the 2026-06-12 symmetric review (pln#560 step 1, this branch).
352
+ Each is something the spec text cannot close on its own; one or more must
353
+ be answered before the JetBrains plugin (Kotlin) ships.
354
+
355
+ | # | Sev | Question |
356
+ |---|---|---|
357
+ | O1 | MED | **Shared `ACTION_CLASS_BY_ACTION` manifest.** §2 says implementations MUST mirror the table "or fetch it from a shared manifest." Today only the TS version exists (`src/core/events/journal.ts:66`); a Kotlin implementer would re-type 42 entries by hand and silently drift on the 43rd. Should this ship as a generated JSON next to `event-log-store.md` (single source of truth, both runtimes load it) or as part of a versioned schema bundle? Recommend the generated JSON — the table is small and changes per spec revision, not per release. |
358
+ | O2 | RESOLVED (pln#568) | **Phase-1.5 cutover signal for §6.1.** Resolved with a `journal_note` kind **`registry_genesis`** marker emitted by `runRegistryGenesisSupplement` after it backfills every pre-existing registry entity (`brainclaw migrate --enable-journal`). Observers detect it (`BoardObserver.registryAuthoritative()`, sticky + re-derived on cold start) and switch the registry counts from the MCP seed to the journal via `mergeCounts(..., registryAuthoritative)`. Chosen over a `meta.json` version bump because meta is a rebuildable cache (§2.3) — the marker is a durable journal record, the source of truth, and survives a meta rebuild. Open follow-up: when checkpoints start emitting (today `checkpoint_seq=0`), the checkpoint must encode the capability so a cold start past the marker's segment still re-derives authority. |
359
+ | O3 | LOW | **`bclaw_dispatch_status` enrichment scope.** §6 + §7 allow it "per visible terminal row" but the wording is ambiguous between "terminal-state row" (Recently terminal) and "row currently visible in the terminal UI" (every IN_PROGRESS row). Settle: probably the first (only failed/silent_death rows want the evidence digest) — but the contract must say so, otherwise an implementor renders an O(workers) burst on every refresh. |
360
+ | O4 | LOW | **Segment pad-width upgrade path.** §2 pins 8-digit decimal padding; the writer is 8-digit too (`src/core/events/journal.ts:214 SEGMENT_PAD=8`). At ~17k events historical, the 1e8 ceiling is decades out — but a future widen would require coordinated writer + every observer roll-out. Carry the migration recipe (pad-width in `meta.json`?) here so a future maintainer doesn't have to rediscover it. |
361
+ | O5 | LOW | **File watch semantics on Windows network mounts.** §4 falls back to a "long-interval stat" when the watcher is unavailable. VS Code's `FileSystemWatcher` on a junction-linked worktree (the brainclaw dispatch substrate) may fire on the link target's mtime but not the source-of-truth segment writes from another process; verify against the dispatch worktree machinery (`pln#498` junctions) before declaring the watch path universal. |
@@ -0,0 +1,71 @@
1
+ # Parallel-lane merge protocol
2
+
3
+ Status: operational (pln#396). When a sequence runs multiple lanes in parallel
4
+ worktrees, their branches must land back on the base without silently
5
+ overwriting each other. This is the minimum protocol; `brainclaw worktree
6
+ check` is its tool.
7
+
8
+ ## The risk
9
+
10
+ Each lane is a worktree branched from the base (master) at dispatch time. Two
11
+ lanes editing the same file will, on the second merge, either conflict
12
+ (visible, recoverable) or — worse — produce a clean-but-wrong merge where one
13
+ lane's change is silently dropped or a file is parasitically deleted
14
+ (trp_merge_wipes_node_modules class). File-level overlap between lanes is the
15
+ predictor; `worktree check` surfaces it before `git merge`, not after.
16
+
17
+ ## Before merging any lane
18
+
19
+ ```
20
+ brainclaw worktree check
21
+ ```
22
+
23
+ It reports, per live worktree lane: the files it changes (committed since base
24
+ + uncommitted tracked), the owning claim / session / agent, and — the payload —
25
+ **which files are touched by more than one lane**. Exit code:
26
+
27
+ - `0` — lanes are disjoint. Merge in any order; no cross-lane conflict possible.
28
+ - `3` — overlapping files exist. Follow the ordering rule below.
29
+
30
+ `brainclaw worktree merge <branch>` runs the same check inline and prints an
31
+ advisory warning if the branch overlaps another live lane (it never blocks —
32
+ the operator decides).
33
+
34
+ ## Ordering rule when lanes overlap
35
+
36
+ 1. **Merge the overlapping lanes one at a time**, never as a batch.
37
+ 2. After merging lane A, **rebase (or re-create) the still-pending overlapping
38
+ lanes onto the new base** so they see A's change, then re-run `worktree
39
+ check`. The overlap on the merged file should now be gone (the pending lane
40
+ either already has A's change or will conflict explicitly on rebase, where
41
+ it is resolvable in isolation).
42
+ 3. Disjoint lanes (no shared files with anything merged) can merge freely at any
43
+ point — `check` confirms they carry no risk.
44
+ 4. Prefer merging the **smallest / most foundational** overlapping lane first
45
+ (fewer files, or the one others build on), so the rebases that follow are
46
+ the cheap direction.
47
+
48
+ ## When automatic reconciliation is not possible
49
+
50
+ `worktree check` is a *predictor*, and `worktree merge` auto-restores parasitic
51
+ deletions but does **not** resolve real content conflicts. Escalate to a human
52
+ (or a single coordinator session doing the merge serially) when:
53
+
54
+ - Two lanes changed overlapping *hunks* of the same file (not just the same
55
+ file) — git will conflict; resolve in the main worktree, do not `--force`.
56
+ - A lane's worktree carries **uncommitted** changes to a shared file (it spawned
57
+ from HEAD; those edits never reach the merge). `check` flags these as
58
+ `(+N uncommitted)` — harvest or discard them deliberately before merging
59
+ (a dead worker's stranded edits are the feedback_review_loop_symmetric_fixer
60
+ / trp#545 case).
61
+ - The overlap is in a generated / high-churn file (lockfiles, `dist/`) — prefer
62
+ regenerating post-merge over merging both sides.
63
+
64
+ ## Invariants
65
+
66
+ - The check is pure-read: only `git diff` / `git status`, no store lock, no
67
+ mutation — safe to run anytime, even mid-dispatch with workers live.
68
+ - `.brainclaw/` and `.gitignore` are never counted as conflict surface (store-
69
+ internal + birth noise).
70
+ - A flagged overlap that turns out disjoint at the hunk level costs one glance;
71
+ the protocol deliberately over-reports rather than miss a real conflict.
@@ -75,6 +75,49 @@ task with estimate est:30min actual:45min [ratio:1.5x]
75
75
 
76
76
  A ratio below 1.0 means the task finished faster than expected (early). Above 1.0 means it took longer (over).
77
77
 
78
+ ### Step-level estimation (pln#495)
79
+
80
+ Estimation can be captured per **step**, not just per plan — which removes a real
81
+ source of noise. A plan's wall-clock span (`created_at`→`completed_at`) counts
82
+ the idle time *between* steps as if it were work: if steps 1–5 finish in a
83
+ morning and step 6 lands the next day, the plan-level elapsed smears an 18h gap
84
+ that was never effort. Summing per-step durations excludes those gaps.
85
+
86
+ Set step estimates and actuals via `add-step` / `update-step`:
87
+
88
+ ```bash
89
+ brainclaw add-step <plan-id> "write unit tests" --estimate 30
90
+ brainclaw update-step <plan-id> <step-id> --status in_progress # stamps started_at
91
+ brainclaw update-step <plan-id> <step-id> --status done # stamps completed_at
92
+ # or record an explicit actual:
93
+ brainclaw update-step <plan-id> <step-id> --actual-effort 45m
94
+ ```
95
+
96
+ `estimated_effort` accepts the same forms as the plan-level `--estimate` (an
97
+ integer of minutes, or a legacy duration string like `2h` / `30m`).
98
+
99
+ `estimation-report` then prefers step-level data and tags each plan with its
100
+ **measurement source**:
101
+
102
+ - **`step`** — the highest quality: `estimated_minutes` is the sum of step
103
+ estimates (used only when *every* step has one), and `elapsed_minutes` is the
104
+ sum of per-step durations (used only when *every* step is measurable, via an
105
+ explicit `actual_effort` or both `started_at`+`completed_at`). Idle gaps
106
+ between steps are excluded.
107
+ - **`plan_string`** — fell back to the plan-level `actual_effort` string.
108
+ - **`plan_wallclock`** — fell back to the plan's `created_at`→`completed_at`
109
+ span (the noisiest; what older plans use).
110
+
111
+ The report's summary breaks the median ratio down per source
112
+ (`step-derived: 1.0x · plan-wallclock: 0.4x …`) so you can see how much
113
+ calibration error was wall-clock contamination vs real estimation drift, and the
114
+ chart tags each line (`✓step` / `~wall`).
115
+
116
+ **Migration:** none required. A plan whose steps carry no estimation data — or a
117
+ plan with no steps at all — keeps working exactly as before via the fallback
118
+ chain. Mixed plans (some steps estimated, some not) fall back to plan-level
119
+ entirely rather than reporting a misleading partial sum.
120
+
78
121
  ## Claims
79
122
 
80
123
  Claims make current ownership explicit.
@@ -0,0 +1,78 @@
1
+ # Skills — agent-profile vs workflow-protocol
2
+
3
+ Brainclaw writes two orthogonal kinds of agent skill. The same agent can load
4
+ both; they answer different questions.
5
+
6
+ ## Agent-profile skills
7
+
8
+ The agent's **landing page** for brainclaw — one per agent profile
9
+ (`openclaw`, `nanoclaw`, `nemoclaw`, `picoclaw`, `zeroclaw`) plus the universal
10
+ `.agents/skills/brainclaw/SKILL.md` discovered by every agent that honors the
11
+ shared `.agents/skills/` convention (Cursor, Copilot, Roo, OpenCode, Codex,
12
+ Kilo, Mistral…). It answers *"what is brainclaw and how do I load context here?"*
13
+ and points at `bclaw_work` as the entry verb.
14
+
15
+ Source: `src/core/agent-files.ts` (`ensureUniversalBrainclawSkill`, the per-profile
16
+ writers). These files are **generated, not committed** — they are gitignored and
17
+ materialized by `brainclaw export` / setup, so the source of truth is the code,
18
+ never a checked-in `SKILL.md`.
19
+
20
+ ## Workflow-protocol skills (pln#519)
21
+
22
+ **Workflow-decomposed** skills that package a critical brainclaw protocol so the
23
+ agent loads *the right one at the right moment* instead of skimming a monolithic
24
+ AGENTS.md. Three ship today, namespaced `brainclaw-*`:
25
+
26
+ | Skill | Trigger |
27
+ |---|---|
28
+ | `brainclaw-session` | starting / resuming / closing a session; before claiming a scope |
29
+ | `brainclaw-memory-capture` | recording a decision / constraint / trap / handoff at the right type |
30
+ | `brainclaw-multi-agent` | delegating, reviewing, dispatching, driving a loop |
31
+
32
+ They carry `metadata.protocol: true` in frontmatter so skill-loader UIs can list
33
+ protocols separately from profile skills, and `metadata.brainclaw_version` (set
34
+ to `package.json:version` at write time) so a loader can detect a cached skill
35
+ that predates a brainclaw upgrade. Each follows the "process not prose" shape:
36
+ **When to use → Workflow → Anti-rationalizations → Red flags → Verification**.
37
+
38
+ Source: `src/core/protocol-skills.ts` (single source of truth; content is
39
+ embedded, not read from a repo file, so it installs identically from source or
40
+ an npm install). Written to `.agents/skills/<id>/SKILL.md` by
41
+ `ensureProtocolSkills`, wired into `writeDetectedAgentAutoConfig` for every agent
42
+ whose capability profile declares `hasSkills: true`. Generated + gitignored, like
43
+ the profile skills.
44
+
45
+ ### Design invariants
46
+
47
+ - **No dynamic state.** A protocol-skill never embeds a concrete `claim_id` /
48
+ `loop_id` / `plan_id` — it tells the agent *when* to call a facade and *how to
49
+ read* live state, never *what the current state is*. (Enforced by a test.)
50
+ - **Facade-only.** Skills reference canonical-grammar / facade verbs by name;
51
+ they never re-implement `mcp.ts` logic. A protocol change updates the skill,
52
+ not the other way round.
53
+ - **Both MCP and CLI.** Each workflow shows the MCP call AND the `brainclaw …`
54
+ CLI fallback, because MCP is not always wired (cold start; a dispatched worker
55
+ in a worktree without `.brainclaw/`, trp#336).
56
+ - **Capped at 3.** Setup and troubleshooting protocols are deferred until an
57
+ empirical friction shows the three are insufficient (design §E.2). A 4th
58
+ protocol needs a runtime_note documenting the gap it closes.
59
+
60
+ ## Namespace claim
61
+
62
+ Brainclaw owns the `brainclaw-*` prefix under `.agents/skills/`. Other tools that
63
+ install skills into the shared directory must avoid `brainclaw-` ids to prevent
64
+ collisions.
65
+
66
+ ## Supply chain
67
+
68
+ Protocol-skills ship **inside the npm package**. Brainclaw does **not** currently
69
+ support installing external skills (no `brainclaw skill install <url>`, no loading
70
+ from arbitrary paths). If external installs are ever added they will require a
71
+ deliberate trust boundary (local-only or brainclaw-signed); that is out of scope
72
+ today.
73
+
74
+ ## Staleness
75
+
76
+ If `brainclaw --version` reports newer than a skill's `brainclaw_version`,
77
+ re-run `brainclaw export --all --write` to regenerate. The version tag lets a
78
+ caching loader detect drift; it does not self-heal.
@@ -25,6 +25,26 @@ It establishes the first shared memory foundation for the workspace:
25
25
  - writes to the detected agent's native instruction file (Cursor, Claude Code, Windsurf, etc.)
26
26
  - creates `AGENTS.md` and `.github/copilot-instructions.md`
27
27
 
28
+ ## Empty memory: one rule
29
+
30
+ "Bootstrap" historically named three different systems (init scaffolding, the
31
+ `bclaw_bootstrap` brownfield extractor, and the bootstrap ideation loop). When
32
+ the memory store is empty, every surface — the `bclaw_work` hint, the
33
+ `bclaw_setup` quick-init preview, and the `brainclaw init` preflight — now
34
+ emits the same decision rule (`resolveEmptyMemoryRecommendation`):
35
+
36
+ - **Repo with existing content** → run `bclaw_bootstrap` (CLI: `brainclaw bootstrap`)
37
+ to extract initial context from docs, manifests, native agent files, and git
38
+ history.
39
+ - **Greenfield repo** (nothing to extract) → open a bootstrap loop to ideate
40
+ the project vision: `bclaw_coordinate(intent='ideate', preset='bootstrap')`
41
+ (CLI: `brainclaw bootstrap-loop`).
42
+
43
+ The two routes are chainable in either order: extract first, then open a loop
44
+ for whatever vision the docs could not provide — or ideate first, then extract
45
+ once content exists. On greenfield, the brownfield preflight scan is skipped
46
+ entirely (there is nothing to harvest yet).
47
+
28
48
  ## Good integration pattern
29
49
 
30
50
  1. check whether the workspace is initialized
@@ -39,6 +59,47 @@ It may simply mean the workspace has not been onboarded yet.
39
59
 
40
60
  This lets a single machine support multiple very different workspaces without forcing one static instruction layer to fit all of them equally well.
41
61
 
62
+ ## init = single project entry point
63
+
64
+ `brainclaw init` is the single code path for turning a project into a
65
+ brainclaw-aware workspace, whether invoked from a terminal, from the
66
+ `bclaw_setup` MCP tool's quick-init step, or from a multi-repo
67
+ `brainclaw setup`. After detecting the local AI agent, init runs the
68
+ per-agent slice of the machine prerequisites (the same writes `setup`
69
+ performs at machine scope, scoped to the detected agent) so an agent
70
+ landing in the carte-blanche / fresh-repo case does not need a separate
71
+ shell-out + session reload. The slice is idempotent — each `ensure*`
72
+ function returns "skipped" when the agent's user-scope config doesn't
73
+ exist, and the writes are short-circuited in `BRAINCLAW_TEST_MODE` or
74
+ when `--skip-agent-bootstrap` is passed. `setup` is rescoped to
75
+ multi-repo / machine-bootstrap; `setup-machine` is the explicit
76
+ machine-only path.
77
+
78
+ ### `init --force`
79
+
80
+ `--force` rebuilds managed identity fields (project_id, current_agent,
81
+ storage_dir, topology) but **merges through the existing config** so
82
+ curator personalisations (redaction patterns, sensitive paths,
83
+ governance overrides, claim TTL, cross-project links, custom markdown
84
+ caps) survive the reset. Before any write, a sibling backup is taken at
85
+ `.brainclaw.bak-<timestamp>/` — the standard recovery-backups pattern
86
+ used by `brainclaw upgrade`. Recovery: `brainclaw upgrade --rollback`.
87
+
88
+ ## Solo-agent fresh defaults
89
+
90
+ A fresh `brainclaw init` seeds `governance.curators` with the human
91
+ running init. Without this, the default `approval_policy: 'review'`
92
+ combined with `curators: []` trapped every reflective note in pending
93
+ forever — a surprise that doesn't show up until enough memory has
94
+ accumulated to notice. The merge logic preserves any explicit curator
95
+ list on an existing store, so this only takes effect on fresh installs.
96
+
97
+ On an empty store, `bclaw_work` carries an explicit
98
+ `bclaw_create(entity='plan')` hint in `next_actions` alongside the
99
+ bootstrap recommendation: the bootstrap covers *vision*, the plan
100
+ affordance covers *work* itself. The two are independent — both can
101
+ appear simultaneously.
102
+
42
103
  ## Multi-project workspaces
43
104
 
44
105
  A workspace may contain multiple brainclaw-initialized child projects (each with its own `.brainclaw/` store). In this topology:
@@ -80,10 +80,10 @@ The agent can use `bclaw_setup` to walk through the process interactively.
80
80
  ### Per-agent manual setup
81
81
 
82
82
  ```bash
83
- brainclaw enable-agent claude-code
84
- brainclaw enable-agent cursor
85
- brainclaw export --format claude-md --write
86
- brainclaw export --detect --write # auto-detect and write all formats
83
+ brainclaw enable-agent claude-code
84
+ brainclaw enable-agent cursor
85
+ brainclaw export --format claude-md --write
86
+ brainclaw export --detect --write # auto-detect and write the current agent format
87
87
  ```
88
88
 
89
89
  ### Regenerating after changes