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,166 @@
1
+ # Execution contract and capability snapshot
2
+
3
+ Every worker-backed Loop turn now carries an immutable `ExecutionContract v1`.
4
+ It answers a narrower question than the Loop protocol: **what exact work may
5
+ this selected worker launch, under which capabilities and evidence rules?** The
6
+ Loop Engine still owns phases, gates, iteration and convergence. The contract
7
+ freezes one physical attempt at the dispatch boundary.
8
+
9
+ This is shared infrastructure for all five `LoopKind` values. It is neither a
10
+ review-loop feature nor a new workflow engine:
11
+
12
+ | Loop kind | Examples of contracted worker phases |
13
+ |---|---|
14
+ | `review` | `findings`, `author_response`, `followup_review` |
15
+ | `ideation` | `critique` |
16
+ | `implementation` | `execute` |
17
+ | `research` | `investigate`, `synthesize` |
18
+ | `debug` | `reproduce`, `hypothesize`, `isolate`, `fix` |
19
+
20
+ Engine and manual phases do not launch workers and therefore do not create an
21
+ execution contract.
22
+
23
+ ## What is frozen
24
+
25
+ `ExecutionContract v1` contains:
26
+
27
+ - loop, turn, logical attempt epoch, Assignment and AgentRun identity;
28
+ - LoopKind, phase and iteration;
29
+ - completion mode and explicitly typed expected artifacts;
30
+ - a `CapabilityRequirement`;
31
+ - workspace scope, checkout isolation and write policy;
32
+ - dispatch and launch-grant durations;
33
+ - evidence requirements (`turn_id`, `run_id`, nonce and artifact-hash policy);
34
+ - the minimum contract and AttemptAuthority protocol versions a reader must
35
+ understand.
36
+
37
+ The logical attempt epoch belongs to immutable identity. It is distinct from
38
+ the mutable launch-grant epoch, which may increase when an armed generation is
39
+ revoked and safely re-armed before crossing.
40
+
41
+ Canonical JSON recursively sorts object keys, removes `undefined`, preserves
42
+ array order and normalizes strings to Unicode NFC. Its SHA-256 is the immutable
43
+ contract identity. A policy change therefore changes the hash; equivalent
44
+ Unicode and object-key ordering do not.
45
+
46
+ ## Requirement is not observation
47
+
48
+ `CapabilityRequirement` says what the attempt needs. Before reservation,
49
+ Brainclaw resolves the selected agent's declared profile into a separate,
50
+ persisted `CapabilitySnapshot`:
51
+
52
+ ```text
53
+ CapabilityRequirement + selected agent profile
54
+ |
55
+ v
56
+ deterministic resolver
57
+ |
58
+ accepted snapshot or reasons
59
+ ```
60
+
61
+ The resolver checks role, integration surfaces, execution surface and model.
62
+ Current profiles do not attest an exact tool catalog, so a named
63
+ `required_tools` list fails closed with `tool_catalog_unattested`; Brainclaw
64
+ does not infer tool availability from generic MCP or skill support.
65
+
66
+ For worker-backed turns, the resolved snapshot also freezes a
67
+ `HarnessCapabilityBinding`: adapter id/version plus requested and selected model
68
+ string. A CLI model selector is marked `unattested` unless the harness can
69
+ actually attest account-specific availability; it is never mislabeled as an
70
+ exact capability merely because `--model` exists. The adapter cannot change on
71
+ replay or after a feature-flag flip. Runtime model and adapter observations are
72
+ stored separately on AgentRun and compared with the contract-bound snapshot.
73
+ See [Harness adapters](./harness-adapters.md).
74
+
75
+ Default roles follow the work performed by each worker phase: review findings
76
+ and follow-up plus ideation critique require `review`; research requires
77
+ `consult`; review author response, implementation and debug require `execute`.
78
+ A caller can provide a stricter explicit requirement when a protocol needs one.
79
+
80
+ ## Persistence and authority
81
+
82
+ No new store or event journal was introduced.
83
+
84
+ | Record | Persisted contract data | Role |
85
+ |---|---|---|
86
+ | `TurnReservation` | full contract, hash/reference, capability snapshot | authoritative immutable attempt record |
87
+ | `Assignment` | hash/reference + snapshot | business projection |
88
+ | `AgentRun` | hash/reference + snapshot + optional monotone anomaly fence | runtime projection |
89
+
90
+ The Assignment and AgentRun are created or validated before the launch fence
91
+ crosses. Recovery enriches a legacy projection only when its new fields are
92
+ absent; a present, divergent hash or snapshot is a projection conflict. A
93
+ pre-v1 reservation remains readable and adoptable without fabricating a
94
+ historical contract (`contract_status: legacy_uncontracted`). New records
95
+ always persist the complete triplet. A partial triplet fails closed because it
96
+ cannot prove which contract and capability observation belong together.
97
+
98
+ Compatibility is symmetric at the projection boundary. A legacy-shaped caller
99
+ may replay a record that already contains P1 fields without erasing or
100
+ conflicting with them; a P1 caller may enrich an older projection whose fields
101
+ are absent; two present but divergent values remain a hard conflict.
102
+
103
+ ## Launch acceptance
104
+
105
+ The safe order for every worker-backed protocol is:
106
+
107
+ ```text
108
+ validate loop/slot/claim
109
+ -> resolve CapabilitySnapshot
110
+ -> build + hash ExecutionContract
111
+ -> reserve / commit / arm
112
+ -> project Assignment + AgentRun + bindings
113
+ -> cross launch fence exactly once
114
+ ```
115
+
116
+ An adapter that can attest an accepted contract before crossing passes both the
117
+ contract and capability-snapshot hashes to `prepareTurnExecution`; a mismatch
118
+ is rejected before reservation and the caller may reselect a worker. The normal
119
+ CLI path crosses the exactly-once launch fence immediately before spawn. The
120
+ contract variables take precedence over any environment supplied by the invoke
121
+ template. A small child bootstrap then reads the environment it actually
122
+ received, compares it with the immutable reference and:
123
+
124
+ 1. writes a JSON `ack` containing the effective hashes and an
125
+ `accepted`/`rejected` status before invoking the worker;
126
+ 2. exposes the hashes as `BRAINCLAW_EXECUTION_CONTRACT_HASH` and
127
+ `BRAINCLAW_CAPABILITY_SNAPSHOT_HASH`;
128
+ 3. echoes them into the mechanical `completed` or `failed` sentinel; and
129
+ 4. asks the worker to echo them in `LANE-RESULT.json`.
130
+
131
+ The dispatcher validates the acknowledgement, and harvest/reconciliation
132
+ validate terminal evidence against the authoritative reservation reference. A
133
+ missing or different hash after `launch.status=crossed` is a
134
+ `post_crossing_anomaly`: convergence is withheld and `respawn:false` is
135
+ propagated. The first observation is persisted on AgentRun as a monotone fence;
136
+ the bootstrap ack remains a durable fallback signal. Later correct-looking
137
+ lane or sentinel data cannot erase the anomaly. The process may already be
138
+ running, so Brainclaw never converts this condition into a manual fallback or
139
+ a second launch for the generation.
140
+
141
+ Manual execution uses the same environment, child bootstrap, ack and terminal
142
+ sentinels. The bootstrap creates the generation-keyed ack exclusively, so
143
+ re-running a copied command cannot start the same generation twice. Once an
144
+ automatic spawn has been attempted, a missing handshake or spawn error is
145
+ treated as an uncertain post-crossing anomaly: Brainclaw does not offer a
146
+ manual fallback command for that contracted generation. Contracted manual
147
+ commands are currently emitted only by Brainclaw's native adapter; an opaque
148
+ custom adapter cannot self-declare its command safe.
149
+
150
+ ## Version and rollback behavior
151
+
152
+ Contract and protocol readers reject a `minimum_reader_version` above the
153
+ version they implement. Contract fields on TurnReservation, Assignment and
154
+ AgentRun are optional only for dual-reading records written before v1. A fully
155
+ legacy reservation continues under its historical evidence rules; new work is
156
+ contracted; partial or unknown future contracts fail closed instead of being
157
+ silently misinterpreted. The projection compatibility rules above make a
158
+ producer rollback non-destructive for already-enriched records.
159
+
160
+ Reference implementation:
161
+
162
+ - [`src/core/execution-contract.ts`](../../src/core/execution-contract.ts)
163
+ - [`src/core/loops/turn-execution.ts`](../../src/core/loops/turn-execution.ts)
164
+ - [`src/core/loops/attempt-reservation.ts`](../../src/core/loops/attempt-reservation.ts)
165
+ - [`tests/unit/execution-contract.test.ts`](../../tests/unit/execution-contract.test.ts)
166
+ - [`tests/unit/loops-p0c-conformance.test.ts`](../../tests/unit/loops-p0c-conformance.test.ts)
@@ -0,0 +1,166 @@
1
+ # Harness adapters
2
+
3
+ Brainclaw's Loop Engine can launch the same contracted turn through different
4
+ agent harnesses without teaching the engine about Codex, Claude, or their CLI
5
+ output formats. `HarnessAdapter` is the boundary between a logical worker
6
+ attempt and one concrete harness. It is shared by the `review`, `ideation`,
7
+ `implementation`, `research`, and `debug` protocols.
8
+
9
+ This is an execution seam, not another workflow engine. Loop phases, iteration,
10
+ stop conditions, evidence policy, and convergence remain owned by the Loop
11
+ Engine. Process creation, environment, working directory, timeout,
12
+ cancellation, sentinels, and Windows command handling remain owned by
13
+ `ExecutionAdapter`.
14
+
15
+ ## The execution path
16
+
17
+ ```text
18
+ Loop worker phase
19
+ -> resolve capability + immutable harness binding
20
+ -> freeze both in the ExecutionContract capability snapshot
21
+ -> HarnessAdapter.prepare({ contract, prompt, mode })
22
+ -> structured InvokeCommand
23
+ -> ExecutionAdapter.start(...)
24
+ -> raw transport observation
25
+ -> HarnessAdapter.parseOutcome(...)
26
+ -> normalized lane result
27
+ -> server-owned evidence envelope and protocol gates
28
+ ```
29
+
30
+ The adapter receives the prompt separately because `ExecutionContract v1`
31
+ freezes authority and requirements, not the complete prompt body. It returns a
32
+ structured invocation rather than spawning a process itself. On Windows this
33
+ keeps prompts out of argv and out of shell quoting. A direct child receives the
34
+ prompt through stdin. An ack-wrapped child receives the same bytes through a
35
+ private per-run file redirected to stdin; the wrapper removes that file after
36
+ writing the terminal sentinel. This gives native Windows children a reliable
37
+ EOF without embedding the prompt in a second command string.
38
+
39
+ ## Built-in adapters
40
+
41
+ | Adapter | Selection | Native output | Compatibility |
42
+ |---|---|---|---|
43
+ | `prompt-only@1` | Default | Existing prompt/sentinel contract | Byte-for-byte invocation compatibility with the previous path |
44
+ | `codex-cli@1` | `BRAINCLAW_NATIVE_HARNESS=1` for a Codex profile | JSONL (`--json`) | Falls closed to a partial result when a successful process emits invalid structured output |
45
+ | `claude-cli@1` | `BRAINCLAW_NATIVE_HARNESS=1` for a Claude profile | JSON (`--output-format json`) | Rejects an unattested requested model before the launch fence |
46
+
47
+ The feature flag is deliberately opt-in. An unknown or unavailable native
48
+ adapter does not silently become another native adapter. Existing generic
49
+ profiles continue through `prompt-only@1`.
50
+
51
+ ## Platform and opt-in E2E matrix
52
+
53
+ The adapter contract is cross-platform even though the process boundary is
54
+ different. CI runs the default suite on Linux and Windows; on Windows the
55
+ execution adapter owns shell resolution while the harness still supplies a
56
+ structured argv and a separate prompt body.
57
+
58
+ | Coverage | Linux | Windows |
59
+ |---|---|---|
60
+ | Unit/contract tests for prompt-only, Codex and Claude adapters | Default CI suite on Node 22 and 24 | Default CI suite on Node 24 |
61
+ | Command boundary | Direct executable + argv/stdin | ExecutionAdapter shell resolution; ack-wrapped stdin uses an ephemeral redirected file, never prompt re-quoting |
62
+ | Repository E2E suite | Dedicated Linux job | Covered by the default platform suite; no separate Windows E2E job |
63
+ | Real Codex/Claude account smoke | Explicit local opt-in | Explicit local opt-in |
64
+
65
+ The real-account smoke tests are skipped by default because they require an
66
+ installed, authenticated CLI and spend provider quota. Run either one after
67
+ `npm run build:test`:
68
+
69
+ ```powershell
70
+ $env:BRAINCLAW_CODEX_HARNESS_E2E = '1'
71
+ $env:BRAINCLAW_CODEX_E2E_MODEL = 'gpt-5.6-sol' # optional
72
+ node --test dist-test/tests/unit/harness-adapters.test.js
73
+
74
+ $env:BRAINCLAW_CLAUDE_HARNESS_E2E = '1'
75
+ $env:BRAINCLAW_CLAUDE_E2E_MODEL = 'sonnet' # optional
76
+ node --test dist-test/tests/unit/harness-adapters.test.js
77
+ ```
78
+
79
+ Use the equivalent environment-variable syntax on POSIX shells. These flags
80
+ only enable the two native smoke cases; deterministic adapter, parsing,
81
+ capability, restart and evidence-boundary tests always run.
82
+
83
+ ## Requested, resolved, and observed capabilities
84
+
85
+ Model identity has three distinct stages:
86
+
87
+ - **requested** — what the caller asked the harness to run;
88
+ - **resolved** — the exact selector passed to the CLI, frozen with the adapter
89
+ id and version in `CapabilitySnapshot.resolved.harness` before reservation.
90
+ Because neither installed CLI exposes an authoritative account-specific
91
+ model catalogue, an explicit selector is marked `unattested`, not falsely
92
+ labelled `exact`;
93
+ - **observed** — what the harness reports after execution, stored separately on
94
+ `AgentRun.runtime_capability_observation`.
95
+
96
+ Requested and resolved values are immutable attempt inputs. Runtime observation
97
+ never rewrites that snapshot. A different observed model creates a monotone
98
+ execution-contract anomaly: later correct-looking output cannot erase it or
99
+ open a convergence gate. A retry therefore needs a new attempt instead of an
100
+ in-place adapter or model substitution.
101
+
102
+ ## Result and evidence boundary
103
+
104
+ `parseOutcome` converts harness-specific terminal output into a result claim.
105
+ Normalization may map transport failure, invalid JSON, blocked work, or a
106
+ successful result into the common lane-result vocabulary. It may not create an
107
+ `EvidenceEnvelope`, decide that a phase gate passed, or advance a Loop.
108
+
109
+ Native adapters append a versioned terminal contract to the worker prompt. The
110
+ final assistant message inside the Codex JSONL or Claude JSON envelope must be
111
+ exactly one strict object (no prose inference):
112
+
113
+ ```json
114
+ {
115
+ "schema_version": 1,
116
+ "status": "completed",
117
+ "summary": "No blocking findings",
118
+ "body": "Optional details",
119
+ "artifact_type": "verdict",
120
+ "review_verdict": "approve"
121
+ }
122
+ ```
123
+
124
+ `review_verdict` is mandatory for review-verdict work. A missing, unknown, or
125
+ narratively implied verdict remains partial/failed and cannot close a review
126
+ Loop. Other Loop kinds use the same result-claim envelope with their own
127
+ server-owned expected artifact type.
128
+
129
+ Those decisions remain server-controlled. The normal order is:
130
+
131
+ ```text
132
+ transport facts -> harness result claim -> lane-result normalization
133
+ -> reconciliation -> evidence sealing -> gate evaluation
134
+ ```
135
+
136
+ This separation prevents a harness from turning its own prose or exit status
137
+ into proof. The same rule applies to all five Loop kinds; their different
138
+ artifact contracts are interpreted only after normalization.
139
+
140
+ ## Failure and restart rules
141
+
142
+ - Missing native binaries and models known to be unsupported are rejected
143
+ before the launch fence. Other explicit model names are passed unchanged,
144
+ marked `unattested`, and never get a fallback flag; the CLI must either run
145
+ that selector or fail the attempt.
146
+ - A persisted adapter id/version must match the adapter used to prepare a
147
+ restart. A feature-flag change cannot silently move an existing attempt to a
148
+ different harness.
149
+ - Spawn failures and timeouts are transport diagnostics, not protocol
150
+ verdicts.
151
+ - Native structured-output parse failures are explicit partial/failed results,
152
+ never successful fallback to unstructured prose.
153
+ - Observed capability mismatch fences convergence and is recorded once on the
154
+ AgentRun.
155
+ - When a native worker does not write `LANE-RESULT.json`, targeted harvest can
156
+ normalize its terminal runtime logs into the same untrusted lane-result
157
+ ingress. Attempt identity is sourced from the reservation and completion
158
+ signal; reconciliation still seals evidence and evaluates the protocol gate.
159
+
160
+ ## Reference implementation
161
+
162
+ - [`src/core/harness-adapters/`](../../src/core/harness-adapters/)
163
+ - [`src/core/execution-adapters.ts`](../../src/core/execution-adapters.ts)
164
+ - [`src/core/execution-contract.ts`](../../src/core/execution-contract.ts)
165
+ - [`src/core/loops/turn-execution.ts`](../../src/core/loops/turn-execution.ts)
166
+ - [`tests/unit/harness-adapters.test.ts`](../../tests/unit/harness-adapters.test.ts)
@@ -209,12 +209,13 @@ bclaw_coordinate(intent='ideate', task='Should we extract the dispatcher into a
209
209
  - Opens a loop with one `champion` slot = caller.
210
210
  - Stores `task` as a `proposal` artifact (sliced to fit the 4 KB
211
211
  artifact body cap).
212
- - Stops at the proposal phase. The champion drives the loop manually
213
- via `bclaw_loop(intent='turn'|'advance')`.
212
+ - Stops at the manual `proposal` phase. The champion drives lifecycle with
213
+ `bclaw_loop(intent='turn'|'advance')`; `critique`, `revision`, and
214
+ `synthesis` are worker phases, so a trusted caller uses `turn(dispatch=true)`
215
+ when it wants Brainclaw to launch them.
214
216
  - Returns `{loop_id, proposal_artifact_id, mode: 'single_agent',
215
217
  dispatched_critics: 0, current_phase: 'proposal'}`.
216
- - Surfaces a single-agent warning so the caller knows dispatch is
217
- manual.
218
+ - Surfaces a single-agent warning so the caller knows dispatch is not automatic.
218
219
 
219
220
  This is the right mode when you want the loop's structure (memory
220
221
  filter, gate, iteration accounting) but want to drive each turn