brainclaw 1.26.2 → 1.28.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 (88) hide show
  1. package/README.md +13 -0
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli/register-coordination.js +65 -1
  4. package/dist/commands/attempt-authority.js +80 -0
  5. package/dist/commands/harvest.js +140 -61
  6. package/dist/commands/loop.js +34 -0
  7. package/dist/commands/loops-handlers.js +143 -15
  8. package/dist/commands/mcp-catalog.js +52 -18
  9. package/dist/commands/mcp-schemas.generated.js +64 -0
  10. package/dist/commands/mcp-write-claims.js +128 -1
  11. package/dist/commands/mcp-write-coordination.js +149 -76
  12. package/dist/core/agent-capability.js +1 -1
  13. package/dist/core/agentrun-reconciler.js +148 -22
  14. package/dist/core/agentruns.js +254 -29
  15. package/dist/core/assignment-request-schema.js +7 -0
  16. package/dist/core/assignment-sweeper.js +5 -3
  17. package/dist/core/assignments.js +131 -33
  18. package/dist/core/claim-request-schema.js +7 -0
  19. package/dist/core/claims.js +53 -2
  20. package/dist/core/dispatch-status.js +16 -6
  21. package/dist/core/dispatcher.js +51 -51
  22. package/dist/core/entity-operations.js +20 -0
  23. package/dist/core/events.js +4 -0
  24. package/dist/core/execution-adapters.js +189 -14
  25. package/dist/core/execution-contract.js +345 -0
  26. package/dist/core/execution.js +130 -16
  27. package/dist/core/facade-schema.js +3 -0
  28. package/dist/core/harness-adapters/base.js +150 -0
  29. package/dist/core/harness-adapters/claude.js +39 -0
  30. package/dist/core/harness-adapters/codex.js +57 -0
  31. package/dist/core/harness-adapters/harvest.js +109 -0
  32. package/dist/core/harness-adapters/index.js +8 -0
  33. package/dist/core/harness-adapters/prompt-only.js +13 -0
  34. package/dist/core/harness-adapters/registry.js +48 -0
  35. package/dist/core/harness-adapters/result.js +33 -0
  36. package/dist/core/harness-adapters/types.js +2 -0
  37. package/dist/core/ideation-loop-close.js +25 -2
  38. package/dist/core/instruction-templates.js +3 -2
  39. package/dist/core/loop-turn-dispatch.js +235 -0
  40. package/dist/core/loops/artifact-contract.js +11 -0
  41. package/dist/core/loops/attempt-authority.js +496 -0
  42. package/dist/core/loops/attempt-generations.js +509 -0
  43. package/dist/core/loops/attempt-reservation.js +197 -35
  44. package/dist/core/loops/attempt-rollout.js +404 -0
  45. package/dist/core/loops/attempt-takeover.js +155 -0
  46. package/dist/core/loops/bootstrap-acquire.js +7 -3
  47. package/dist/core/loops/brief-assembly.js +21 -4
  48. package/dist/core/loops/evidence.js +188 -0
  49. package/dist/core/loops/facade-schema.js +75 -11
  50. package/dist/core/loops/gate-policy.js +533 -0
  51. package/dist/core/loops/impl-bind.js +91 -81
  52. package/dist/core/loops/index.js +9 -0
  53. package/dist/core/loops/iteration-engine.js +31 -19
  54. package/dist/core/loops/kind-policies.js +90 -0
  55. package/dist/core/loops/lock.js +71 -13
  56. package/dist/core/loops/reconcile-turn.js +237 -18
  57. package/dist/core/loops/result-reducers.js +113 -10
  58. package/dist/core/loops/store.js +34 -3
  59. package/dist/core/loops/turn-execution.js +480 -0
  60. package/dist/core/loops/types.js +127 -3
  61. package/dist/core/loops/verbs.js +335 -99
  62. package/dist/core/loops/verify-command.js +105 -20
  63. package/dist/core/loops/workspace-digest.js +54 -0
  64. package/dist/core/review-loop-close.js +25 -3
  65. package/dist/core/review-loop-turn-dispatch.js +210 -161
  66. package/dist/core/runtime-signals.js +62 -25
  67. package/dist/core/schema.js +40 -0
  68. package/dist/core/spawn-check.js +3 -2
  69. package/dist/core/upgrades/backup.js +27 -4
  70. package/dist/facts.js +9 -8
  71. package/dist/facts.json +8 -7
  72. package/docs/cli.md +49 -1
  73. package/docs/concepts/attempt-authority.md +407 -0
  74. package/docs/concepts/evidence-attestations.md +135 -0
  75. package/docs/concepts/execution-contract.md +166 -0
  76. package/docs/concepts/harness-adapters.md +166 -0
  77. package/docs/concepts/ideation-loop.md +5 -4
  78. package/docs/concepts/loop-engine.md +302 -113
  79. package/docs/index.md +4 -1
  80. package/docs/integrations/codex.md +3 -3
  81. package/docs/integrations/mcp.md +59 -5
  82. package/docs/loops/debug.md +144 -0
  83. package/docs/loops/ideation.md +158 -0
  84. package/docs/loops/implementation.md +174 -0
  85. package/docs/loops/research.md +136 -0
  86. package/docs/loops/review.md +200 -0
  87. package/docs/mcp-schema-changelog.md +18 -5
  88. package/package.json +1 -1
@@ -0,0 +1,407 @@
1
+ # Attempt authority
2
+
3
+ `AttemptAuthority` is the kind-neutral execution authority used by every
4
+ worker-backed phase of the Loop Engine. It answers three questions that must
5
+ never be inferred from process IDs, mutable projections, or telemetry:
6
+
7
+ 1. was the logical turn committed;
8
+ 2. which physical generation may spawn;
9
+ 3. which generation may settle the turn.
10
+
11
+ It is not a sixth loop protocol and it adds no event journal. Review, ideation,
12
+ implementation, research, and debug all use the same substrate. Loop events
13
+ remain the causal history; runtime events remain telemetry; Assignment and
14
+ AgentRun remain queryable projections.
15
+
16
+ ## Identity model
17
+
18
+ One logical turn may now have several physical runs.
19
+
20
+ | Identity | Lifetime | Rule |
21
+ |---|---|---|
22
+ | `turn_id` | logical turn | stable across takeover and retry |
23
+ | `assignment_id` | logical work assignment | stable across takeover and retry |
24
+ | `attempt_epoch` | physical generation | starts at `0`, increases by one |
25
+ | `run_id` | physical execution | fresh for every epoch |
26
+ | `launch_nonce` | launch fence | fresh and unpredictable for every epoch |
27
+ | `contract_hash` | execution contract | recomputed for the generation's run and workspace |
28
+ | `workspace_digest` | isolated workspace | binds the real workspace path to the turn and epoch |
29
+
30
+ For backward compatibility, the first worker phase to occupy a slot in a
31
+ protocol iteration uses the historical deterministic identity derived from
32
+ `(loop_id, slot_id, iteration)`. When the same slot is reused by a different
33
+ worker phase in that iteration, Brainclaw derives a versioned phase-qualified
34
+ identity from `(loop_id, slot_id, phase, iteration)`. The resolver adopts an
35
+ existing compatible legacy or phase-qualified reservation before minting
36
+ anything, so same-phase retries remain exactly-once and in-flight upgrades keep
37
+ their durable identity. A different logical phase is a different turn; a
38
+ takeover remains a new physical generation of the same turn.
39
+
40
+ Evidence for an AttemptAuthority v2 generation is accepted only when the full
41
+ tuple matches:
42
+
43
+ ```text
44
+ (assignment_id, turn_id, attempt_epoch, run_id, launch_nonce,
45
+ contract_hash, workspace_digest)
46
+ ```
47
+
48
+ Matching only `turn_id`, `run_id`, and `nonce` remains supported for legacy
49
+ reservations. Once a turn has a v2 generation chain, the complete tuple is
50
+ mandatory. An old worker can therefore finish late, but its output cannot
51
+ mutate the loop or the current AgentRun.
52
+
53
+ ## Durable decisions
54
+
55
+ The original `TurnReservation` still owns logical commit and the immutable
56
+ generation-zero contract. AttemptAuthority v2 adds small immutable decision
57
+ cells under `.brainclaw/memory/loops/attempt-generations/<turn_id>/`; rollout
58
+ guards and signed ACKs live separately under
59
+ `.brainclaw/memory/loops/attempt-authority-v2/rollout/`:
60
+
61
+ - one initial-generation cell per turn;
62
+ - `launch(epoch)`, decided once as `crossed` or `revoked`;
63
+ - `close(epoch)`, decided once as `settled`, `takeover`, `retry`, or
64
+ `cancelled`;
65
+ - an optional `head.json`, which is only a rebuildable cache.
66
+
67
+ Settlement and takeover contend on the same `close(epoch)` cell. Exactly one
68
+ wins. A takeover embeds the complete successor generation in that cell, so a
69
+ crash after the decision but before projections is repaired by replaying the
70
+ same successor; it never mints another run.
71
+
72
+ The publish protocol is intentionally conservative on Windows and POSIX:
73
+
74
+ 1. create a temporary file on the same volume;
75
+ 2. write, flush, `fsync`, and close it;
76
+ 3. publish the final path with a hard-link create-if-absent;
77
+ 4. read and adopt the incumbent on `EEXIST`.
78
+
79
+ There is no rename fallback. If the filesystem cannot provide this no-clobber
80
+ primitive, v2 mutations fail closed. Orphan temporary files are
81
+ non-authoritative and safe to remove later.
82
+
83
+ ## Surfaces and their roles
84
+
85
+ Four event/state surfaces exist. Their responsibilities are deliberately
86
+ non-overlapping; only the first one answers whether a process may launch or a
87
+ result may settle.
88
+
89
+ | Surface | Role | Owner | Authority rule |
90
+ |---|---|---|---|
91
+ | `TurnReservation` plus immutable `initial`, `launch(epoch)` and `close(epoch)` cells | **Authoritative** execution decisions | `AttemptAuthority` | The first no-clobber decision wins. Mutable projections never override it. |
92
+ | Loop `LoopEvent` journal | **Causal** protocol history | Loop Engine | Replays phases, artifacts, gates and generation-change causes; it does not grant spawn authority. |
93
+ | `RuntimeEvent` stream | **Telemetry** | execution/runtime layer | Reports processes, heartbeats, adapters and diagnostics; it is evidence for an operator, never a launch/settlement decision. |
94
+ | Legacy project `events.jsonl` | **Compatibility-only** audit stream | legacy consumers | Retained for compatibility. New AttemptAuthority or registry logic must not depend on it; journal v2 carries registry projections. |
95
+
96
+ Assignment, AgentRun, Claim, slot and `head.json` records are queryable
97
+ projections rather than a fifth event surface. The separation invariant is:
98
+ `AttemptAuthority` decides execution, `LoopEvent` explains protocol causality,
99
+ `RuntimeEvent` observes execution, and `events.jsonl` serves old readers.
100
+
101
+ ## What can run in parallel
102
+
103
+ Brainclaw does not concurrently rewrite shared JSON files. That proved fragile
104
+ in particular on Windows. Instead it narrows serialization to the decision
105
+ that actually needs consensus:
106
+
107
+ - capability resolution, liveness collection, contract construction, isolated
108
+ workspace preparation, and writer signatures can run in parallel;
109
+ - each rollout writer publishes its own immutable ACK file independently;
110
+ - contenders race on one immutable `launch(epoch)` or `close(epoch)` cell;
111
+ - Assignment, AgentRun, loop event, runtime event, and head updates are
112
+ sequential, idempotent projections that may be replayed after a crash.
113
+
114
+ This gives parallel preparation without permitting concurrent mutation of the
115
+ same file. The global mutation pipeline remains serialized for ordinary store
116
+ entities; AttemptAuthority decisions do not hold that lock while agents work.
117
+
118
+ ## Ordered dispatch
119
+
120
+ P0A characterised the legacy order as
121
+ `reserve → commit → durable projections → legacy launch CAS → spawn →
122
+ reconcile`. That order remains the compatibility prefix: a crash before the
123
+ launch CAS is repairable, and a replay observing an already-crossed legacy
124
+ decision never spawns again.
125
+
126
+ The shipped v2 order extends that prefix rather than bypassing it. During the
127
+ Release-B cutover, generation zero is anchored only after the legacy launch
128
+ decision has crossed. A crash between either boundary is repaired
129
+ idempotently: the initial cell embeds the same immutable generation, and the
130
+ v2 `launch(0)` cell remains the final spawn fence. Successor generations skip
131
+ identity reminting for the logical work and use the v2 path below.
132
+
133
+ The common worker path is
134
+ [`prepareTurnExecution`](../../src/core/loops/turn-execution.ts):
135
+
136
+ 1. validate the loop phase, slot, claim, capabilities, and workspace policy;
137
+ 2. reserve and commit the stable logical turn;
138
+ 3. freeze its ExecutionContract and capability snapshot;
139
+ 4. create or validate all durable projections (Assignment, AgentRun, claim, and slot) before crossing;
140
+ 5. cross `launch(0)` immediately before spawn;
141
+ 6. when Release B is active, anchor generation zero in the v2 chain;
142
+ 7. accept completion only after rechecking the full fence under the loop lock;
143
+ 8. race settlement on `close(epoch)` before applying loop projections.
144
+
145
+ For v2, worker MCP lifecycle reports stop at `accepted`, `started`, and
146
+ `progress`. A worker cannot set the stable Assignment terminal or release its
147
+ Claim, even with the current fence. It writes full-fence `LANE-RESULT.json`;
148
+ settlement seals that result first, then Brainclaw projects Assignment,
149
+ AgentRun, Claim, artifacts, and loop state.
150
+
151
+ After takeover, re-entering the same common path keeps the logical Assignment,
152
+ projects the successor AgentRun and contract, then races `launch(next_epoch)`.
153
+ Only the caller whose publish returns `won: true` may spawn. Replays adopt the
154
+ crossed cell and do not spawn.
155
+
156
+ The per-kind phase graph, artifacts, gates, iteration, and stop condition stay
157
+ in the [Loop Engine](./loop-engine.md). AttemptAuthority does not decide what a
158
+ review verdict means, when an ideation synthesis is sufficient, or whether an
159
+ implementation/debug verification is green.
160
+
161
+ ## Functional API
162
+
163
+ The kind-neutral facade is
164
+ [`src/core/loops/attempt-authority.ts`](../../src/core/loops/attempt-authority.ts).
165
+ Its public operations map directly to the decisions above:
166
+
167
+ - `prepareAttempt` and `projectAndCross` implement the legacy
168
+ reserve/commit/projection/cross compatibility prefix. `projectAndCross`
169
+ authorises a spawn only when it returns `kind: 'won'`.
170
+ - `inspectAttempt`, `matchEvidence`, `revokeAttempt`, and `abortAttempt`
171
+ expose read-strict inspection and the irreversible legacy decision axes.
172
+ - `bootstrapAttemptAuthorityV2` anchors generation zero without minting a
173
+ second logical Assignment.
174
+ - `prepareAttemptTakeoverV2` closes the current epoch with a complete successor
175
+ embedded in the immutable cell; `takeoverLoopAttempt` applies the loop-level
176
+ causal and replayable projections.
177
+ - `crossActiveAttemptGenerationV2` arbitrates the successor's launch cell.
178
+ Only its `won: true` result carries spawn authority.
179
+ - `settleActiveAttemptGenerationV2` seals result evidence, competes on
180
+ `close(epoch)`, and exposes the incumbent verdict to losing/replaying calls.
181
+ - `resolveTurnGenerationChain` and `rebuildAttemptGenerationHead` read the
182
+ immutable chain and repair the non-authoritative head.
183
+
184
+ The low-level cell functions live in
185
+ [`attempt-generations.ts`](../../src/core/loops/attempt-generations.ts); signed
186
+ membership and authority-home checks live in
187
+ [`attempt-rollout.ts`](../../src/core/loops/attempt-rollout.ts). The Loop Engine
188
+ still owns artifacts, phase transitions, gates and convergence. Harness and
189
+ execution adapters translate/execute a contracted turn but never call these
190
+ functions to approve their own output.
191
+
192
+ ## Takeover and retry
193
+
194
+ A coordinator may fence a crossed generation and arm a successor with:
195
+
196
+ ```bash
197
+ brainclaw loop takeover <loop_id> \
198
+ --slot <slot_id> \
199
+ --turn-id <turn_id> \
200
+ --expected-epoch <n> \
201
+ --cause "heartbeat and process evidence are stale" \
202
+ --liveness-evidence "no heartbeat for 30m; wrapper exited" \
203
+ --external-effect-policy idempotent \
204
+ --next-workspace-path <existing-isolated-directory> \
205
+ --agent <loop-coordinator>
206
+ ```
207
+
208
+ The equivalent MCP call is `bclaw_loop(intent="takeover", ...)`. It records an
209
+ `attempt_generation_changed` LoopEvent, projects an `attempt_takeover`
210
+ RuntimeEvent, interrupts the old AgentRun best-effort, and creates the new
211
+ AgentRun. Its response is deliberately `spawn_authority: false`: the caller
212
+ must dispatch the same logical turn through the common path, which crosses the
213
+ new launch cell immediately before process creation.
214
+
215
+ Takeover requires:
216
+
217
+ - the loop coordinator identity;
218
+ - the expected active epoch;
219
+ - a non-empty cause and liveness evidence;
220
+ - an existing linked Git worktree of the same repository, with a distinct
221
+ gitdir and top-level path that does not alias the prior workspace (including
222
+ through a Windows junction);
223
+ - effects declared `none`, `idempotent`, or protected by an external fence.
224
+
225
+ An operation with non-idempotent external effects and no external fencing must
226
+ not be taken over automatically. Human recovery must first establish the
227
+ external system's outcome or fence it there.
228
+
229
+ ## Two-release activation
230
+
231
+ AttemptAuthority v2 is a writer compatibility boundary. Enabling a new writer
232
+ beside an old binary would let the old binary ignore generation cells, so the
233
+ rollout has two releases.
234
+
235
+ Release A:
236
+
237
+ 1. deploy v2-aware readers and guarded writers everywhere;
238
+ 2. stop or drain pre-Release-A processes;
239
+ 3. prepare one immutable membership guard;
240
+ 4. let every active writer sign and publish its own ACK in parallel;
241
+ 5. activate the guard only after every active writer ACKs the same digest.
242
+
243
+ Release B permits the first v2 generation write. CLI support:
244
+
245
+ ```bash
246
+ brainclaw attempt-authority status --json
247
+ brainclaw attempt-authority prepare --writers <agent_id...>
248
+ brainclaw attempt-authority ack --membership-epoch 1 --agent-id <agent_id>
249
+ brainclaw attempt-authority activate --membership-epoch 1
250
+ ```
251
+
252
+ Membership epochs form an immutable chain. A genuinely offline writer can be
253
+ revoked only by a later membership epoch chained to the active activation. A
254
+ pre-Release-A binary cannot sign or honor this guard; it must be stopped or
255
+ removed by deployment/service control before activation.
256
+
257
+ ## Migration and rollout runbook
258
+
259
+ Treat the first v2 generation cell as the irreversible cutover boundary.
260
+ Release A can be rolled back while no v2 cell exists; Release B cannot be
261
+ downgraded in place.
262
+
263
+ 1. **Drain and inventory.** Stop new loop dispatch, let active generations
264
+ settle, list every process/service/host capable of writing this store, and
265
+ stop pre-Release-A binaries. An offline writer is not implicitly safe: it
266
+ must be removed from service control or excluded by a new membership epoch.
267
+ 2. **Create and verify a private backup.** With writers quiescent, run:
268
+
269
+ ```bash
270
+ node scripts/store-snapshot.mjs create --store .brainclaw
271
+ node scripts/store-snapshot.mjs verify --snapshot <snapshot-directory>
272
+ ```
273
+
274
+ Keep the snapshot outside the repository. Record its manifest hash and the
275
+ output of `brainclaw attempt-authority status --json`. The detailed storage
276
+ procedure is [store-snapshot.md](../playbooks/store-snapshot.md).
277
+ 3. **Release A guard.** Run `prepare`, let every active writer run `ack` in
278
+ parallel, then run `activate`. Re-run `status --json` and verify the
279
+ membership epoch, authority home and ACK digest before enabling Release B.
280
+ `prepare`/`ack` also exercise the hard-link create-if-absent primitive; a
281
+ filesystem that cannot provide it stops the rollout here.
282
+ 4. **Canary Release B.** Enable v2 on the authority home only. Run one
283
+ no-external-effect worker attempt, then one explicit retry/takeover in a
284
+ linked Git worktree. Verify that the Assignment stays stable, epochs and
285
+ AgentRuns change, one `close(epoch)` winner exists, stale output is rejected,
286
+ and `head.json` can be rebuilt from the chain. Observe LoopEvents and
287
+ RuntimeEvents separately; neither may contradict the decision cells.
288
+ 5. **Expand.** Resume ordinary dispatch only after the canary and targeted
289
+ tests are green. Add or revoke writers through a new, digest-chained
290
+ membership epoch; never edit an activated guard or ACK in place.
291
+
292
+ **Abort before cutover.** If no v2 initial/launch/close cell was ever written,
293
+ stop writers, restore the verified pre-cutover snapshot into an empty directory
294
+ with `store-snapshot.mjs restore`, verify it, and re-point the workspace under
295
+ the previous release. Do not restore over a live store.
296
+
297
+ **Recovery after cutover.** Once any v2 cell exists, disabling the feature flag
298
+ or installing an old writer is a forbidden downgrade. First stop all writers,
299
+ export the current v2 store with `store-snapshot.mjs create`, verify the export,
300
+ and restore it only into an empty directory using a v2-capable binary. Preserve
301
+ the immutable generation/rollout cells and use the same local authority-home
302
+ identity; a restore on another device is a passive replica until explicitly
303
+ re-authorised. A pre-v2 backup may be inspected or used to recover unrelated
304
+ data, but it must not replace a store whose v2 history has started.
305
+
306
+ ## Authority home and federation
307
+
308
+ Every v2 fence carries an `authority_home`:
309
+
310
+ ```text
311
+ { store_instance_id, device_id }
312
+ ```
313
+
314
+ The identity is random and stored outside the project store in the user's
315
+ Brainclaw registry (`~/.brainclaw/store-instances/` by default). It is not
316
+ derived from a path or hostname. Copying a `.brainclaw` directory therefore
317
+ does not copy write authority. `BRAINCLAW_AUTHORITY_IDENTITY_ROOT` may relocate
318
+ that local registry for managed or test environments.
319
+
320
+ Only the activated authority home may write v2 cells. Federated replicas are
321
+ passive readers of those cells; they do not arbitrate takeover independently.
322
+ A copied or foreign device fails with `authority_home_mismatch`.
323
+
324
+ ## Recovery
325
+
326
+ - No initial-generation cell: use the legacy reservation path. A Release-B
327
+ writer anchors generation zero only after the legacy launch has crossed.
328
+ - `launch(epoch)` absent: projections may be repaired, then callers may contend
329
+ on launch.
330
+ - `launch(epoch) = crossed`, `close(epoch)` absent: the run may still produce
331
+ acceptable full-fence evidence; do not respawn it.
332
+ - `close(epoch) = takeover|retry`: the embedded successor is authoritative.
333
+ Replay missing projections and continue from it.
334
+ - `close(epoch) = settled`: replay any missing terminal projections; never
335
+ takeover that generation.
336
+ - Corrupt cells, broken chain links, authority mismatch, incompatible writer,
337
+ or unsupported hard links: fail closed and require operator repair.
338
+
339
+ `head.json`, AgentRun status, RuntimeEvents, and process liveness never override
340
+ the immutable chain. The head can always be rebuilt from generation and close
341
+ cells.
342
+
343
+ ## Invariants (I1–I18)
344
+
345
+ - **I1 — Stable logical identity.** `turn_id` and `assignment_id` do not
346
+ change across retry or takeover.
347
+ - **I2 — Fresh physical identity.** Every epoch has a fresh `run_id`, nonce,
348
+ workspace identity/digest and generation contract hash.
349
+ - **I3 — Single execution authority.** Only reservation and immutable
350
+ generation decision cells decide launch or settlement.
351
+ - **I4 — Irreversible logical commit.** A committed reservation never becomes
352
+ aborted; an aborted reservation never becomes committed.
353
+ - **I5 — One launch verdict per epoch.** `crossed` and `revoked` are exclusive,
354
+ and only the caller that creates `crossed` may spawn.
355
+ - **I6 — One close verdict per epoch.** `settled`, `takeover`, `retry`, and
356
+ `cancelled` are mutually exclusive.
357
+ - **I7 — One receivable generation.** A closed generation is never active;
358
+ late output stays audit-only.
359
+ - **I8 — Full-fence evidence.** V2 acceptance matches assignment, turn, epoch,
360
+ run, nonce, contract hash and workspace digest.
361
+ - **I9 — Evidence before projections.** Settlement seals immutable result
362
+ evidence before applying terminal Assignment, AgentRun, Claim, artifact or
363
+ loop projections.
364
+ - **I10 — Projections are replayable.** Assignment, AgentRun, Claim, slot,
365
+ events and head can be created-or-validated again after a crash without
366
+ changing authority.
367
+ - **I11 — Shared mutable JSON is serialized.** Parallel work prepares inputs or
368
+ publishes disjoint immutable files; it never concurrently rewrites one JSON
369
+ projection.
370
+ - **I12 — No-clobber means hard link.** Final cells use same-volume temp,
371
+ fsync/close and hard-link create-if-absent. There is no rename fallback.
372
+ - **I13 — Authority home is local.** Only the activated
373
+ `(store_instance_id, device_id)` may mutate v2 cells.
374
+ - **I14 — Federation is passive.** Replicas may validate/replay the chain but
375
+ never promote themselves during a partition.
376
+ - **I15 — Writer rollout is explicit.** Release B requires one activated,
377
+ signed membership epoch whose active writers all ACK the same digest and
378
+ both `minimum_writer_version` and `minimum_reader_version`.
379
+ - **I16 — External effects are fenced.** Automatic takeover is forbidden for
380
+ non-idempotent external effects without an external fence.
381
+ - **I17 — Recovery is decision-driven.** Digests and immutable cells determine
382
+ the next action; clocks, PIDs, heartbeats and marker files are supporting
383
+ liveness evidence only.
384
+ - **I18 — Event roles remain separate.** AttemptAuthority is authoritative,
385
+ LoopEvent causal, RuntimeEvent telemetry, and `events.jsonl`
386
+ compatibility-only. No fifth journal is introduced.
387
+
388
+ ## Code map
389
+
390
+ - [`attempt-reservation.ts`](../../src/core/loops/attempt-reservation.ts) —
391
+ logical reservation and legacy launch authority.
392
+ - [`attempt-generations.ts`](../../src/core/loops/attempt-generations.ts) —
393
+ immutable generation, launch, close, and head primitives.
394
+ - [`attempt-rollout.ts`](../../src/core/loops/attempt-rollout.ts) — signed
395
+ writer membership and local authority identity.
396
+ - [`attempt-authority.ts`](../../src/core/loops/attempt-authority.ts) — common
397
+ facade, bootstrap, takeover, crossing, and settlement.
398
+ - [`attempt-takeover.ts`](../../src/core/loops/attempt-takeover.ts) — loop-level
399
+ takeover transaction and replayable projections.
400
+ - [`reconcile-turn.ts`](../../src/core/loops/reconcile-turn.ts) — full-fence
401
+ validation, close-cell settlement, and business convergence.
402
+ - [`turn-execution.ts`](../../src/core/loops/turn-execution.ts) — common worker
403
+ dispatch path for all five LoopKinds.
404
+
405
+ See also [Execution contract](./execution-contract.md),
406
+ [Harness adapters](./harness-adapters.md), and the
407
+ [Loop Engine](./loop-engine.md).
@@ -0,0 +1,135 @@
1
+ # Evidence envelopes, attestations, and protocol gates
2
+
3
+ The Loop Engine does not treat an artifact's text as proof that a transition
4
+ is safe. Every new loop binds `gate-policy-v1`; every artifact committed by
5
+ the engine receives a server-sealed `EvidenceEnvelope` before it enters the
6
+ thread.
7
+
8
+ This contract applies equally to the five shipped loop kinds: review,
9
+ ideation, implementation, research, and debug. It refines their gates; it
10
+ does not introduce a separate workflow or event store.
11
+
12
+ ## EvidenceEnvelope v1
13
+
14
+ An envelope binds:
15
+
16
+ - the artifact digest, including id, phase, type, body/ref, producer,
17
+ production time, critique links, and iteration;
18
+ - the exact subject: loop, artifact, phase, iteration and, when available,
19
+ slot, turn, assignment, claim, run, launch-nonce digest/epoch, execution-contract
20
+ hash, command digest, and workspace digest;
21
+ - a server-derived producer and ingress channel;
22
+ - an observation time and explicit validity window;
23
+ - independent attestations;
24
+ - a canonical SHA-256 integrity seal.
25
+
26
+ Ingress callers never submit an envelope. `complete_turn`, turn reconciliation,
27
+ the engine verify runner, operator-input handlers, bootstrap hooks, and
28
+ `add_artifact` all seal at their server-controlled commit boundary. Likewise,
29
+ `produced_by` is derived by the server; the public `add_artifact` input no
30
+ longer accepts it as authority. A direct `add_artifact` commit is intentionally
31
+ audit-only: its observation carries `artifact:write`, not `gate:artifact`.
32
+ The internal evidence context and sealing helper are omitted from the public
33
+ Loop Engine barrel; public completion and artifact functions explicitly strip
34
+ any runtime object that attempts to smuggle such a context.
35
+
36
+ The SHA-256 seal detects accidental or local-store tampering. It is not a
37
+ remote cryptographic identity signature: Brainclaw's local store remains in
38
+ the trusted computing base.
39
+
40
+ ## Attestations are independent
41
+
42
+ There is deliberately no global confidence score and no ordering such as
43
+ “verification is stronger than approval”. A policy asks for the exact right
44
+ and attestation it needs:
45
+
46
+ | Attestation | Meaning | Typical right |
47
+ | --- | --- | --- |
48
+ | `claim` | the result is bound to the recorded claim/attempt subject | `subject:claim` |
49
+ | `observation` | the engine observed and committed an artifact | `gate:artifact` |
50
+ | `verification` | Brainclaw ran the opener-configured command | `gate:command_green` |
51
+ | `approval` | an authorized reviewer slot returned an accepted verdict | `gate:reviewer_green` |
52
+
53
+ A worker or adapter can report a passing `verify_report`, but it cannot grant
54
+ itself `verification`. A generic artifact insertion can store a deliverable or
55
+ an accepted verdict for audit, but cannot open either a generic artifact gate
56
+ or a reviewer gate.
57
+
58
+ Gate authority is an explicit tuple, not a producer-kind shortcut: policy
59
+ matches the ingress channel, producer kind, attestation kind, issuer, right,
60
+ and required subject fields. For example, `reviewer_green` accepts an approval
61
+ issued by `brainclaw:review-slot` through `complete_turn` or a fully bound
62
+ `reconcile_turn`; `command_green` accepts only `brainclaw:verify-command`.
63
+
64
+ ## Gate evaluation
65
+
66
+ One evaluator is used by terminal stop conditions, phase-advance gates, and
67
+ iteration exits. A decision records:
68
+
69
+ - `passed`, plus distinct `strict_passed` and `legacy_passed` dimensions;
70
+ - policy version and rollout mode;
71
+ - a digest of the evaluated condition;
72
+ - accepted evidence ids;
73
+ - rejected artifact ids with machine-readable reasons.
74
+
75
+ The decision is attached to causal `phase_advanced`,
76
+ `phase_advance_blocked`, and automatic `closed` LoopEvents. RuntimeEvents
77
+ remain best-effort telemetry and never authorize a transition.
78
+
79
+ The evaluator rejects missing evidence on strict threads, invalid seals,
80
+ artifact/subject mismatches, cross-loop or cross-iteration replay, evidence
81
+ predating the loop, future timestamps, unauthorized channel/producer/issuer
82
+ combinations, missing execution bindings or rights, and duplicate payloads in
83
+ threshold gates. If an envelope is present but invalid, legacy behavior is
84
+ never used as a fallback.
85
+
86
+ The engine snapshots the workspace bytes immediately before and after
87
+ `verify_command`, then again at the evidence commit boundary. A concurrent
88
+ mutation changes the digest and forces the report red. The command argv and
89
+ its digest plus the stable workspace digest are copied into the report and its
90
+ sealed subject. Gate policy requires those bindings to match the configured
91
+ command and recomputes the current workspace digest whenever the gate is read;
92
+ a post-verification mutation therefore invalidates an earlier green report.
93
+ Reconciled worker evidence likewise binds the run, launch generation, and
94
+ execution contract. Pre-P1 reservations retain a deterministic hash of their
95
+ immutable legacy reservation fields so an in-flight historical attempt can
96
+ converge without pretending it carried a v1 execution contract.
97
+
98
+ Reviewer approval is scoped to the current loop iteration and to the slot's
99
+ current turn, assignment, and claim projections. A creator/admin recovery may
100
+ settle a slot, but it is recorded as coordinator evidence and cannot mint the
101
+ reviewer's approval.
102
+
103
+ Negative convergence is fail-closed too: an invalid critique cannot be used
104
+ to manufacture “no new critique”. After at least one full ideation cycle, a
105
+ settled critique round with no eligible new critiques is evaluated before the
106
+ quantitative critique gate. “Settled” is causal: the last trusted critic
107
+ successful completion emits an engine-owned `critique_window_closed` artifact
108
+ for that iteration. An open, assigned, running, failed, or cancelled critic
109
+ turn therefore cannot create saturation merely by staying silent.
110
+
111
+ ## Rollout and legacy threads
112
+
113
+ `LoopThread.evidence_policy` makes compatibility explicit:
114
+
115
+ - absent: pre-policy thread; unsealed legacy artifacts retain legacy gate
116
+ semantics (including duplicate threshold counting), while any present
117
+ envelope is still validated;
118
+ - `{version: "gate-policy-v1", mode: "shadow"}`: writers seal evidence and
119
+ decisions report both the independently composed strict and legacy results,
120
+ while the legacy outcome controls the transition;
121
+ - `{version: "gate-policy-v1", mode: "strict"}`: only policy-eligible evidence
122
+ influences gates.
123
+
124
+ New loops default to `strict`. Set `BRAINCLAW_EVIDENCE_ENVELOPES=shadow` for a
125
+ measured rollout, or `off` before opening a loop to create an explicit
126
+ pre-policy thread. The policy is frozen on the thread: changing the process
127
+ flag later does not silently downgrade an already-strict loop.
128
+
129
+ ## Persistence model
130
+
131
+ Evidence lives on `LoopArtifact`; gate decisions live in the existing
132
+ append-only LoopEvent journal. Brainclaw does not add an evidence database or
133
+ a second event journal. The attempt reservation, execution contract,
134
+ artifact, evidence envelope, and causal event remain separately inspectable
135
+ parts of one execution history.