@enderfga/claw-orchestrator 7.14.0 → 7.16.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 (145) hide show
  1. package/README.md +162 -135
  2. package/configs/autoloop-reviewer-prompt.md +1 -1
  3. package/configs/engines/README.md +31 -11
  4. package/dist/bin/acp-server.js +7 -12
  5. package/dist/bin/acp-server.js.map +1 -1
  6. package/dist/bin/cli.js +84 -46
  7. package/dist/bin/cli.js.map +1 -1
  8. package/dist/bin/mcp-server.js +2 -54
  9. package/dist/bin/mcp-server.js.map +1 -1
  10. package/dist/src/acp-server.d.ts +17 -9
  11. package/dist/src/acp-server.js +149 -30
  12. package/dist/src/acp-server.js.map +1 -1
  13. package/dist/src/autoloop/agent-tools.d.ts +2 -0
  14. package/dist/src/autoloop/agent-tools.js +7 -0
  15. package/dist/src/autoloop/agent-tools.js.map +1 -1
  16. package/dist/src/autoloop/dispatcher.d.ts +6 -0
  17. package/dist/src/autoloop/dispatcher.js +72 -9
  18. package/dist/src/autoloop/dispatcher.js.map +1 -1
  19. package/dist/src/autoloop/planner-tools.js +2 -0
  20. package/dist/src/autoloop/planner-tools.js.map +1 -1
  21. package/dist/src/claude-goal-session.d.ts +147 -0
  22. package/dist/src/claude-goal-session.js +355 -0
  23. package/dist/src/claude-goal-session.js.map +1 -0
  24. package/dist/src/council.d.ts +8 -0
  25. package/dist/src/council.js +45 -12
  26. package/dist/src/council.js.map +1 -1
  27. package/dist/src/dashboard/index.html +28 -14
  28. package/dist/src/embedded-server.js +27 -8
  29. package/dist/src/embedded-server.js.map +1 -1
  30. package/dist/src/engine-spawn.js +3 -1
  31. package/dist/src/engine-spawn.js.map +1 -1
  32. package/dist/src/fanout.d.ts +13 -3
  33. package/dist/src/fanout.js +10 -0
  34. package/dist/src/fanout.js.map +1 -1
  35. package/dist/src/inbox-manager.d.ts +9 -0
  36. package/dist/src/inbox-manager.js +8 -2
  37. package/dist/src/inbox-manager.js.map +1 -1
  38. package/dist/src/index.d.ts +2 -0
  39. package/dist/src/index.js +135 -53
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/kernel/agent-step.d.ts +6 -0
  42. package/dist/src/kernel/agent-step.js.map +1 -1
  43. package/dist/src/kernel/conditions.d.ts +1 -1
  44. package/dist/src/kernel/conditions.js +1 -1
  45. package/dist/src/kernel/engine.d.ts +28 -8
  46. package/dist/src/kernel/engine.js +93 -19
  47. package/dist/src/kernel/engine.js.map +1 -1
  48. package/dist/src/kernel/nodes/agent.js +2 -0
  49. package/dist/src/kernel/nodes/agent.js.map +1 -1
  50. package/dist/src/kernel/nodes/council.js +9 -4
  51. package/dist/src/kernel/nodes/council.js.map +1 -1
  52. package/dist/src/kernel/nodes/fanout.js +5 -0
  53. package/dist/src/kernel/nodes/fanout.js.map +1 -1
  54. package/dist/src/kernel/nodes/goal.d.ts +19 -5
  55. package/dist/src/kernel/nodes/goal.js +81 -34
  56. package/dist/src/kernel/nodes/goal.js.map +1 -1
  57. package/dist/src/kernel/nodes/subflow.js +9 -1
  58. package/dist/src/kernel/nodes/subflow.js.map +1 -1
  59. package/dist/src/kernel/nodes/ultraapp.d.ts +1 -0
  60. package/dist/src/kernel/nodes/ultraapp.js +1 -0
  61. package/dist/src/kernel/nodes/ultraapp.js.map +1 -1
  62. package/dist/src/kernel/nodes/verifier.js +5 -1
  63. package/dist/src/kernel/nodes/verifier.js.map +1 -1
  64. package/dist/src/kernel/projections.d.ts +1 -0
  65. package/dist/src/kernel/projections.js +7 -1
  66. package/dist/src/kernel/projections.js.map +1 -1
  67. package/dist/src/kernel/templates/index.d.ts +16 -11
  68. package/dist/src/kernel/templates/index.js +14 -8
  69. package/dist/src/kernel/templates/index.js.map +1 -1
  70. package/dist/src/kernel/types.d.ts +37 -13
  71. package/dist/src/kernel/types.js.map +1 -1
  72. package/dist/src/logger.d.ts +6 -0
  73. package/dist/src/logger.js +14 -0
  74. package/dist/src/logger.js.map +1 -1
  75. package/dist/src/mcp-annotations.d.ts +13 -0
  76. package/dist/src/mcp-annotations.js +100 -0
  77. package/dist/src/mcp-annotations.js.map +1 -0
  78. package/dist/src/models.d.ts +7 -0
  79. package/dist/src/models.js +9 -0
  80. package/dist/src/models.js.map +1 -1
  81. package/dist/src/openai-compat.d.ts +11 -5
  82. package/dist/src/openai-compat.js +55 -54
  83. package/dist/src/openai-compat.js.map +1 -1
  84. package/dist/src/persistent-acp-session.d.ts +7 -0
  85. package/dist/src/persistent-acp-session.js +26 -2
  86. package/dist/src/persistent-acp-session.js.map +1 -1
  87. package/dist/src/persistent-codex-app-session.d.ts +2 -0
  88. package/dist/src/persistent-codex-app-session.js +18 -9
  89. package/dist/src/persistent-codex-app-session.js.map +1 -1
  90. package/dist/src/persistent-codex-session.d.ts +14 -3
  91. package/dist/src/persistent-codex-session.js +22 -19
  92. package/dist/src/persistent-codex-session.js.map +1 -1
  93. package/dist/src/persistent-custom-session.d.ts +9 -0
  94. package/dist/src/persistent-custom-session.js +41 -21
  95. package/dist/src/persistent-custom-session.js.map +1 -1
  96. package/dist/src/persistent-grok-session.js +1 -1
  97. package/dist/src/persistent-grok-session.js.map +1 -1
  98. package/dist/src/persistent-session.d.ts +9 -0
  99. package/dist/src/persistent-session.js +23 -0
  100. package/dist/src/persistent-session.js.map +1 -1
  101. package/dist/src/run-ledger.d.ts +3 -3
  102. package/dist/src/session-manager.d.ts +45 -1
  103. package/dist/src/session-manager.js +199 -34
  104. package/dist/src/session-manager.js.map +1 -1
  105. package/dist/src/types.d.ts +34 -11
  106. package/dist/src/types.js.map +1 -1
  107. package/dist/src/ultraapp/build.d.ts +5 -11
  108. package/dist/src/ultraapp/build.js +5 -11
  109. package/dist/src/ultraapp/build.js.map +1 -1
  110. package/dist/src/ultraapp/council-adapter.d.ts +5 -2
  111. package/dist/src/ultraapp/council-adapter.js +6 -6
  112. package/dist/src/ultraapp/council-adapter.js.map +1 -1
  113. package/dist/src/ultraapp/files.d.ts +7 -0
  114. package/dist/src/ultraapp/files.js +7 -1
  115. package/dist/src/ultraapp/files.js.map +1 -1
  116. package/dist/src/ultraapp/manager.d.ts +23 -3
  117. package/dist/src/ultraapp/manager.js +170 -72
  118. package/dist/src/ultraapp/manager.js.map +1 -1
  119. package/dist/src/ultraapp/narrator.js +1 -1
  120. package/dist/src/ultraapp/narrator.js.map +1 -1
  121. package/dist/src/ultraapp/versions.d.ts +27 -5
  122. package/dist/src/ultraapp/versions.js +58 -13
  123. package/dist/src/ultraapp/versions.js.map +1 -1
  124. package/openclaw.plugin.json +4 -13
  125. package/package.json +13 -6
  126. package/skills/SKILL.md +52 -24
  127. package/skills/references/acp.md +140 -61
  128. package/skills/references/autoloop.md +148 -38
  129. package/skills/references/claude-cli-tracking.md +9 -6
  130. package/skills/references/cli.md +139 -108
  131. package/skills/references/council.md +44 -22
  132. package/skills/references/dashboard.md +37 -21
  133. package/skills/references/getting-started.md +49 -15
  134. package/skills/references/inbox.md +13 -8
  135. package/skills/references/mcp.md +25 -17
  136. package/skills/references/multi-engine.md +161 -145
  137. package/skills/references/observability.md +90 -58
  138. package/skills/references/openai-compat.md +172 -82
  139. package/skills/references/sessions.md +101 -65
  140. package/skills/references/tools.md +217 -158
  141. package/skills/references/ultra.md +7 -7
  142. package/skills/references/ultraapp.md +101 -56
  143. package/skills/references/verification.md +112 -56
  144. package/skills/references/workflow.md +275 -97
  145. package/assets/banner.jpg +0 -0
@@ -5,10 +5,13 @@ fails, when to stop, and how to come back after the process dies.
5
5
 
6
6
  ## Every mode runs on it
7
7
 
8
- `council_start`, `fanout_start`, `ultraplan_start`, `ultrareview_start` and
9
- `autoloop_start` all create a kernel run. Their result shapes —
10
- `CouncilSession`, `FanoutSession`, `UltraplanResult`, `UltrareviewResult`,
11
- `AutoloopState` — are _projected_ from the run record.
8
+ `workflow_start` runs a spec you write or a [built-in template](#built-in-templates);
9
+ `clawo solve`, `clawo fanout` and `clawo goal` start the same templates from a
10
+ terminal. The mode tools run on the same kernel: `council_start`, `fanout_start`,
11
+ `ultraplan_start`, `ultrareview_start` and `autoloop_start` each create a kernel
12
+ run, and so does an UltraApp build. The modes' result shapes — `CouncilSession`,
13
+ `FanoutSession`, `UltraplanResult`, `UltrareviewResult`, `AutoloopState` — are
14
+ _projected_ from the run record, so `workflow_list` lists every one of them.
12
15
 
13
16
  The mode engines (`Council`, `Fanout`, the autoloop planner/coder/reviewer
14
17
  dispatcher) do the work; the kernel owns lifecycle, persistence and listing. A
@@ -24,7 +27,8 @@ Every state transition is checkpointed **before the next step begins**:
24
27
  run.json the mutable checkpoint, rewritten atomically (tmp + rename)
25
28
  events.jsonl append-only audit + SSE source
26
29
  incarnation.json which creation of this run id this is, and its fence counter
27
- lease.json who is executing it right now
30
+ lease.json who is executing it right now (removed when the run ends)
31
+ lease.lock the exclusive lock every write takes, present only while held
28
32
  .tx/ a committed batch awaiting application (see below)
29
33
  nodes/<id>/ per-node artifacts
30
34
  evidence/<id>/ evidence bundles
@@ -32,8 +36,11 @@ Every state transition is checkpointed **before the next step begins**:
32
36
 
33
37
  Splitting the immutable spec from the mutable checkpoint is what makes recovery
34
38
  total: if `run.json` is missing or half-written, state is rebuilt by replaying
35
- `events.jsonl` against `spec.json`. The atomic rewrite makes that path rare; the
36
- replay makes it survivable anyway.
39
+ `events.jsonl` against `spec.json`: the run's state and outcome, and each node's
40
+ state, attempts, visits, output preview and evidence id. Node payloads (`data`),
41
+ cost, consensus votes, the base commit and the test snapshot are only in
42
+ `run.json`. The atomic rewrite makes that path rare; the replay makes it
43
+ survivable anyway.
37
44
 
38
45
  A kernel resumes at a **node boundary**, never mid-node, and at the node the run
39
46
  was on — not at the first pending node, since a node on a branch the run never
@@ -53,8 +60,14 @@ again after the resume.
53
60
  **This makes node execution at-least-once, not exactly-once.** There is no
54
61
  idempotency key and no side-effect commit marker, so a node that wrote files and
55
62
  then died before its checkpoint runs again from the top. Workflows whose nodes
56
- are not safe to repeat need to make them safe. Resume is also explicit —
57
- `workflow_resume` — not automatic.
63
+ are not safe to repeat need to make them safe.
64
+
65
+ Resume is explicit — `workflow_resume` or `clawo workflow resume` — not automatic.
66
+ Resuming a run that already ended returns it unchanged. Custom-engine configs are
67
+ never written to disk (they can carry credentials), so a run whose agents used one
68
+ is resumed through `workflow_resume` with `agentCustomEngineRefs:
69
+ { "<agent>": "<REF>" }`, each config read as JSON from `CLAWO_CUSTOM_ENGINE_<REF>`
70
+ (upper-cased) on the orchestrator's host.
58
71
 
59
72
  ### One owner, and one way to write
60
73
 
@@ -111,10 +124,11 @@ names four things, and all four are checked on every durable write:
111
124
  each has its own owner id, or both would read the other's claim as their own.
112
125
  - **Atomic acquisition.** The check and the write happen inside the lock, so two
113
126
  processes cannot both see "free" and both claim the run.
114
- - **An independent heartbeat.** Renewed on a timer, not only at checkpoints: a
115
- run executing one long node makes no checkpoints, and must not look abandoned
116
- for it. On the same host a live pid is the authority and is never judged stale
117
- for going quiet; the heartbeat is the fallback for a holder on another machine.
127
+ - **An independent heartbeat.** Renewed every 15 s on a timer, not only at
128
+ checkpoints: a run executing one long node makes no checkpoints, and must not
129
+ look abandoned for it. On the same host a live pid is the authority and is never
130
+ judged stale for going quiet; the heartbeat is the fallback for a holder on
131
+ another machine, whose lease goes stale after 60 s without a renewal.
118
132
  - **In-process too.** Starting a run whose id is already live retires the
119
133
  previous run first. A lease cannot see inside one process, and two live runs
120
134
  sharing an id would write over each other's checkpoints.
@@ -138,28 +152,70 @@ is unrecoverable and reads back as "not found".
138
152
 
139
153
  ## Node kinds
140
154
 
141
- | Kind | Does |
142
- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
143
- | `agent` | One session, one turn |
144
- | `fanout` | N agents in parallel, optional synthesis |
145
- | `council` | The existing council engine, votes recorded as advisory |
146
- | `verifier` | Runs an acceptance contract, writes evidence — see [`verification.md`](./verification.md) |
147
- | `human_gate` | Parks the run until a person answers |
148
- | `router` | Picks the next node from declarative conditions; a backwards route is the loop |
149
- | `subflow` | Runs another workflow as a step and adopts its verdict |
150
- | `autoloop` | A long-lived Planner / Coder / Reviewer loop; its executor is injected. On the node's first visit (`visits === 1`) the shared fresh-start boundary applies (dirty-worktree refusal + run-local `tasks/<config.runId>/.gitignore`); resume and later visits skip it. |
151
- | `goal` | A Codex `/goal` on one `codex-app` thread, run until the goal finishes — see [below](#goal) |
152
- | `ultraapp_*` | UltraApp's synth and deploy stages; their executors are injected too |
153
-
154
- Every node takes `retry: { max, backoffMs }`, `timeoutMs`, and
155
- `onFailure: 'fail' | 'continue'`.
156
-
157
- Direct agent bindings use the same optional `engine`, `model`, and `effort`
158
- fields. Fan-out and council entries configure them per agent; `effort` accepts
159
- `low`, `medium`, `high`, `xhigh`, `max`, `ultra`, or `auto`. Omission preserves
160
- the session default.
161
-
162
- Fan-out agents may also set `persona` for role instructions. When no non-empty
155
+ | Kind | Does |
156
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
157
+ | `agent` | One session, one turn. A new session per attempt unless `sessionName` pins one |
158
+ | `fanout` | N agents in parallel on a shared prompt, optional synthesis pass. Succeeds when at least one agent does |
159
+ | `council` | The council engine; its votes are recorded as advisory. Fails only when the council itself errors |
160
+ | `verifier` | Runs an acceptance contract, writes evidence — see [`verification.md`](./verification.md). `"contract": "run"` with no run-level contract passes through and verifies nothing; an inline contract with no recognised check is refused at start |
161
+ | `human_gate` | Parks the run until a person answers; a rejection fails the run |
162
+ | `router` | Picks the next node from declarative conditions; a backwards route is the loop |
163
+ | `subflow` | Runs another workflow as a child run and adopts its verdict, with the tree digest the verdict was taken against; a child that ends `unverified` hands up no verdict |
164
+ | `autoloop` | A long-lived Planner / Coder / Reviewer loop; its executor is injected. On the node's first visit (`visits === 1`) the shared fresh-start boundary applies (dirty-worktree refusal + run-local `tasks/<config.runId>/.gitignore`); resume and later visits skip it. |
165
+ | `goal` | A `/goal` on one Codex `codex-app` thread or Claude Code session, run until the goal finishes — see [below](#goal) |
166
+ | `ultraapp_synth`, `ultraapp_deploy` | UltraApp's synth and deploy stages; their executors are injected too |
167
+
168
+ ### Spec and node fields
169
+
170
+ A `WorkflowSpec` is `{ name, nodes, cwd?, contract?, maxNodeVisits? }`. `cwd` is
171
+ the run's working directory (a start call's `cwd` overrides it), `contract` is the
172
+ run-level acceptance contract, and `maxNodeVisits` (default 50) bounds every loop.
173
+ A spec is checked before anything runs: a missing name, no nodes, a missing or
174
+ duplicate node id, a `next`, route or `default` naming a node that does not exist,
175
+ a route with no condition, a `retry.max` that is not a non-negative integer, a
176
+ `timeoutMs` that is not a positive number, and a `maxNodeVisits` that is not a
177
+ positive integer are all refused.
178
+
179
+ With a run-level contract the kernel appends one more node, the verifier
180
+ `__verify`, unless a node already declares `"contract": "run"`.
181
+
182
+ Every node has an `id` and a `kind`, plus these optional fields:
183
+
184
+ | Field | Default | Meaning |
185
+ | ----------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
186
+ | `next` | the next node in `nodes` order | Explicit successor; a router ignores it and uses `default`. The run ends after the last node in `nodes` order |
187
+ | `retry` | `{ "max": 0 }` | `max` extra attempts per visit, waiting `backoffMs` (default 1000) × the attempt number between them |
188
+ | `timeoutMs` | 30 min; none for `autoloop` and `goal` nodes | Wall clock for one attempt |
189
+ | `onFailure` | `fail` | `fail` ends the run; `continue` records the failure and moves on |
190
+
191
+ The kind-specific fields:
192
+
193
+ | Kind | Fields |
194
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
195
+ | `agent` | `prompt` (required); `engine`, `model`, `effort`, `acpAgent` (a bundled ACP preset id, for `engine: "acp"`); `cwd` (default: the run's); `sessionName`, to reuse one named session across nodes; `permissionMode` (default `bypassPermissions`); `appendSystemPrompt`, standing instructions for the session |
196
+ | `fanout` | `prompt` (required, the shared task) and `agents` (required); `synthesize`, `synthesisEngine` (default `claude`), `synthesisModel`, `synthesisAcpAgent`, `synthesisPermissionMode` (default `bypassPermissions`); `maxTurnsPerAgent` and `maxBudgetUsd`, per agent; `cwd`; `agentTimeoutMs` |
197
+ | `council` | `task` (required) and `agents` (required); `projectDir` (default: the run's cwd); `maxRounds` (default 15, as for `council_start`); `maxTurnsPerAgent` and `maxBudgetUsd`, per agent; `defaultPermissionMode`; `agentTimeoutMs` |
198
+ | `verifier` | `contract` (required): an inline contract, or `"run"` for the run-level one; `cwd` (default: the run's) |
199
+ | `human_gate` | `prompt` (required) |
200
+ | `router` | `routes` (required): `[{ "when": <condition>, "to": <node id> }]`, first match wins; `default`, taken when nothing matches (omitted: fall through to the next node) |
201
+ | `subflow` | `workflow` (required): an inline `WorkflowSpec`, or the id of an existing run, whose stored spec runs again as a new child run. Template names do not resolve here |
202
+ | `goal` | `objective` (required); `engine`, `cwd`, `model`, `effort`, `sandboxMode`, `launcher`, `threadId`, `tokenBudget`, `capacityRetry`, `stallMs` — see [Goal](#goal) |
203
+ | `autoloop` | `workspace` and `config` (the `autoloop_start` options). Started by `autoloop_start` |
204
+ | `ultraapp_synth`, `ultraapp_deploy` | `appRunId`, `runDir`, `slug`; the deploy stage adds `version` and `codebasePath`. Started by UltraApp builds |
205
+
206
+ An entry in a fan-out's or council's `agents` takes `name` (required), `engine`,
207
+ `model`, `effort`, `acpAgent`, `persona`, `prompt`, `permissionMode` and `baseUrl`;
208
+ a fan-out agent also takes `sandboxMode`, and a council agent `ultracode` and
209
+ `emoji` (shown with its name in its charter and the transcript). An agent
210
+ node and each of these entries use `engine`, `model` and `effort` the same way;
211
+ `effort` accepts `low`, `medium`, `high`, `xhigh`, `max`, `ultra`, or `auto`, and
212
+ omitting any of them preserves the session default. `customEngine` is not a spec
213
+ field — a spec is written to disk and a custom-engine config can carry
214
+ credentials — so custom engines reach a run in memory, through `fanout_start` or
215
+ `council_start`, and are re-supplied on resume as described above.
216
+
217
+ A council agent's `persona` is its role; one with no `persona` uses its `prompt`
218
+ instead. Fan-out agents may also set `persona` for role instructions. When no non-empty
163
219
  per-agent `prompt` exists, the executor sends the persona followed by a
164
220
  `## Shared task` section containing the node's shared prompt. A non-empty
165
221
  per-agent `prompt` remains a complete override and takes precedence over both
@@ -193,11 +249,18 @@ surface. These closed forms are evaluated and nothing else:
193
249
  { "type": "and", "all": [ /* … */ ] }
194
250
  ```
195
251
 
196
- `and` is the only recursive one, and nesting is capped at six deep. Use it rather
197
- than chaining two routers: **a router whose routes all miss falls through to the
198
- next node**, so a gate that failed to match simply hands control to the router
199
- after it, which then matches on its own. Chaining reads as AND and behaves as
200
- "whatever the last router says".
252
+ `node_failed` and `node_succeeded` read the named node's current state (a node
253
+ that has not run is neither). `verified` holds when the named node produced
254
+ evidence and the run's current verdict is `verified`. `visits_lt` counts how many
255
+ times control has entered the node. Routes are tried in order and the first match
256
+ wins.
257
+
258
+ `and` is the only recursive one, and nesting is capped at six deep; an `and` with
259
+ an empty `all` is false. Use it rather than chaining two routers: **a router whose
260
+ routes all miss falls through to the next node** (or to its `default`), so a gate
261
+ that failed to match simply hands control to the router after it, which then
262
+ matches on its own. Chaining reads as AND and behaves as "whatever the last router
263
+ says".
201
264
 
202
265
  `maxNodeVisits` (default 50) bounds every loop as a safety backstop. Budget loops
203
266
  with `visits_lt`.
@@ -209,7 +272,7 @@ with `visits_lt`.
209
272
  "name": "solve",
210
273
  "cwd": "/repo",
211
274
  "maxNodeVisits": 6,
212
- "contract": { "checks": [{ "type": "command", "cmd": "npm", "args": ["test"] }] },
275
+ "contract": { "checks": [{ "spec": { "type": "command", "cmd": "npm", "args": ["test"] } }] },
213
276
  "nodes": [
214
277
  {
215
278
  "id": "triage",
@@ -243,6 +306,13 @@ with `visits_lt`.
243
306
  }
244
307
  ```
245
308
 
309
+ A contract written inside a spec — the run-level `contract`, or a verifier's
310
+ inline one — is used as written, so each check goes in the `{ "spec": { … } }`
311
+ wrapper shown here, with `id` and `required` beside `spec`. The `contract` argument
312
+ of `workflow_start` and `POST /workflow/new` is normalized first and also accepts
313
+ bare checks (`{ "type": "command", … }`); when given, it replaces the spec's own.
314
+ See [`verification.md`](./verification.md#the-one-rule-about-where-contracts-come-from).
315
+
246
316
  When the router sends control back to `implement`, the agent is told what the
247
317
  last verification found: each failing required check and the lines of its output
248
318
  that say why, framed as data, ahead of the node's own prompt. This applies to any
@@ -254,32 +324,48 @@ test suite would double the most expensive part of the run to learn nothing new.
254
324
 
255
325
  ## Built-in templates
256
326
 
257
- `workflow_start` accepts `template` instead of `spec`:
258
-
259
- - **`solve`** — the shape above: triage → (optional human gate) → implement →
260
- (optional review) → verify → repair-until-green.
261
- - **`council`** — one council node, plus the implicit verifier when a contract is
262
- declared.
263
- - **`fanout`** — one fan-out node.
264
- - **`goal`** — a `goal` node → verify → repair on the same thread; `task` is the
265
- objective. See [below](#goal).
266
-
267
- These are ordinary specs, not privileged paths.
327
+ `workflow_start` accepts `template` and `task` instead of `spec` (`template`
328
+ defaults to `solve`); `clawo solve`, `clawo fanout` and `clawo goal` build the same
329
+ specs and post them.
330
+
331
+ | Template | Nodes | Arguments |
332
+ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
333
+ | `solve` | `triage` (fan-out) → `approve` (human gate, only with `humanGate`) → `implement` (agent) → `review` (fan-out, only with `reviewers`) → `verify` → `repair-gate` (router) | `agents` (the triage scouts; default one agent on the default engine), `implementer` (`engine`, `model`), `reviewers`, `humanGate`, `maxRepairs` (default 3), `contract` |
334
+ | `council` | `council`, then `__verify` when a contract is declared | `agents`, `maxRounds` (default 15), `contract` |
335
+ | `fanout` | `fanout`, then `__verify` when a contract is declared | `agents`, `contract` |
336
+ | `goal` | `goal` → `verify` → `repair-gate` (router); `task` is the objective. See [below](#goal) | `engine` (`codex` default, or `claude`), `model`, `tokenBudget`, `launcher`, `maxRepairs` (default 1), `contract` |
337
+
338
+ `solve` is the shape above: the triage and review fan-outs add a synthesis pass
339
+ when they have two or more agents, `implement` and `review` continue on failure so
340
+ the verifier always judges the tree, `verify` continues so a red result reaches
341
+ `repair-gate`, and `maxNodeVisits` is `maxRepairs + 3`. Through `workflow_start` a `fanout` template synthesizes when it
342
+ has two or more agents; `clawo fanout` synthesizes only with `--synthesize`.
343
+ `clawo goal` also takes `--effort`, `--sandbox`, `--thread`, `--goal-timeout`,
344
+ `--check-timeout` and `--objective-file`.
345
+
346
+ These are ordinary specs, not privileged paths. UltraApp's builds run one more
347
+ built-in, `ultraapp` (`synth` → `build`, a verifier → `deploy`, when there is
348
+ somewhere to deploy to), which only UltraApp starts.
268
349
 
269
350
  ## Goal
270
351
 
271
- A `goal` node runs one Codex `/goal` on one `codex-app` thread. Codex's
272
- app-server runs the goal itself: `thread/goal/set` starts its turn loop, which
273
- keeps going until the goal is `complete`, out of budget, `blocked`, or stopped
274
- by a usage limit. The node adds what an unattended run needs around that loop.
352
+ A `goal` node runs one `/goal` on one thread: a Codex `codex-app` thread by
353
+ default, or a Claude Code session with `"engine": "claude"` (see
354
+ [On Claude Code](#on-claude-code)). Codex's app-server runs the goal itself:
355
+ `thread/goal/set` starts its turn loop, which keeps going until the goal is
356
+ `complete`, out of budget, `blocked`, or stopped by a usage limit. The node adds
357
+ what an unattended run needs around that loop.
275
358
 
276
359
  ```jsonc
277
360
  {
278
361
  "id": "goal",
279
362
  "kind": "goal",
280
363
  "objective": "Reach the goal described in GOAL.md …",
364
+ "engine": "codex", // default; or "claude"
281
365
  "cwd": "/work/task/ws",
282
366
  "model": "gpt-6.1-sol", // optional; codex's own default otherwise
367
+ "effort": "high", // optional reasoning effort
368
+ "sandboxMode": "workspace-write", // default; or "read-only" / "danger-full-access"
283
369
  "launcher": ["/work/goal/sandbox/sandbox.sh", "task", "0 1"], // optional argv prefix
284
370
  "threadId": "019c…", // optional: start on this existing thread, replacing its goal
285
371
  "tokenBudget": 50000000, // optional; the goal ends as budgetLimited past it
@@ -290,27 +376,33 @@ by a usage limit. The node adds what an unattended run needs around that loop.
290
376
  ```
291
377
 
292
378
  - **The objective fits Codex's limit.** Codex accepts at most 4,000 characters;
293
- a longer objective fails the node before a session opens. Put a long brief in
294
- a file in the working directory and name the file in the objective.
295
- - **The thread is checkpointed.** Node data holds `threadId`, `status`,
296
- `tokensUsed`, `timeUsedSeconds`, `resumes`, `lastError`, the agent's last reply
297
- (`lastMessage`, at most 4,000 characters) and the thread's spend
298
- (`costUsd`), updated as the goal moves (progress at most every 10 s, status
299
- changes at once). `workflow_status` and `clawo workflow show` read it while the
300
- goal runs.
379
+ a longer objective fails the node before a session opens (and `clawo goal`
380
+ refuses it before starting a run). Put a long brief in a file in the working
381
+ directory and name the file in the objective.
382
+ - **The thread is checkpointed.** Node data holds `threadId`, the `objective`
383
+ currently set, `status`, `tokensUsed`, `timeUsedSeconds`, `resumes`,
384
+ `lastError`, the agent's last reply (`lastMessage`, at most 4,000 characters)
385
+ and the thread's spend (`costUsd`), updated as the goal moves (progress at most
386
+ every 10 s, status changes at once). `workflow_status` and `clawo workflow show`
387
+ read it while the goal runs. The spend is added to the run's `costUsd`.
301
388
  - **Resume reattaches, it does not restart.** A resumed run hands the stored
302
389
  thread id to `thread/resume`; a goal that had not finished continues where it
303
390
  was. If the resume lands on a different thread the node fails rather than
304
391
  starting the objective over. An app-server that exits mid-goal is reopened on
305
- the same thread the same way.
392
+ the same thread the same way. A goal that had already ended (`complete`,
393
+ `budgetLimited`, `blocked`) when the process died ends the node with that
394
+ result on resume; it is not set again.
306
395
  - **Stalls are resumed within a bound.** A goal that is `usageLimited` or
307
396
  `paused`, or `active` with no turn running and either no news for `stallMs` or
308
- an error the server will not retry (`serverOverloaded`, `usageLimitExceeded`,
309
- `rateLimitExceeded`, a dropped connection), is set back to `active` after a
310
- backoff that starts at `capacityRetry.backoffMs` and doubles to at most 15 min.
311
- Every attempt counts before it is made, reopening a dead app-server included;
312
- past `capacityRetry.max` the node fails.
313
- - **Repair on the same thread.** Revisited after a red verifier, the node clears
397
+ a capacity error the server will not retry (`serverOverloaded`,
398
+ `usageLimitExceeded`, `rateLimitExceeded`, `internalServerError`,
399
+ `responseTooManyFailedAttempts`, or a failed or dropped connection), is set back
400
+ to `active` after a backoff that starts at `capacityRetry.backoffMs` and doubles
401
+ to at most 15 min. Every attempt counts before it is made, reopening a dead
402
+ app-server included; past `capacityRetry.max` the node fails. The count is part
403
+ of the checkpoint, so it carries across process restarts and repair visits.
404
+ - **Repair on the same thread.** Revisited after a red verifier — a red verdict
405
+ other than the one in force when its current objective was set — the node clears
314
406
  the finished goal and sets a new objective on the same thread: the failing
315
407
  checks first (as diagnostic data, like an agent node's repair brief), then the
316
408
  original objective. The agent keeps its context. The original objective is
@@ -325,14 +417,62 @@ by a usage limit. The node adds what an unattended run needs around that loop.
325
417
  user input) is answered with an error at once; the sandbox mode is the
326
418
  boundary.
327
419
  - **Ends.** `complete` succeeds. `budgetLimited` and `blocked` fail with the
328
- reason. Cancel interrupts the running turn and pauses the goal. Like
329
- `autoloop`, the node has no default timeout.
420
+ reason. Cancel interrupts the running turn and pauses the goal, and so does a
421
+ `timeoutMs` that runs out. Like `autoloop`, the node has no default timeout.
422
+
423
+ ### On Claude Code
424
+
425
+ With `"engine": "claude"` the same node drives Claude Code's `/goal` (CLI
426
+ 2.1.139 or later) through `ClaudeGoalSession`. Claude Code has no goal RPCs or
427
+ goal notifications, so the adapter reads the goal from what the CLI does in
428
+ stream-json mode:
429
+
430
+ - `/goal <objective>` sets the goal and starts working. A Stop hook — a separate
431
+ model judging the condition against the session — runs after every turn and
432
+ feeds the turn back while the condition does not hold, all inside one send;
433
+ one `result` arrives when the goal is met, and the goal clears itself.
434
+ - A bare `/goal` answers at once, without a model turn — `Goal active: …` or
435
+ `No goal set.` A failed turn clears the goal too, so a gone goal is not proof:
436
+ the goal is `complete` only when a turn that started with it set ended without
437
+ an error and it is gone after. A goal still set with nothing driving it, a
438
+ failed turn, and a reopened session whose goal is gone are `paused`, which the
439
+ node resumes within its bound. Resuming sends a short "continue" message when
440
+ the goal is still set and sets it again (`/goal <objective>`) when it is not,
441
+ so a goal that was already met costs one turn to confirm.
442
+ - A failed turn is read from its text: usage limits become `usageLimited`,
443
+ overloaded and 5xx errors the matching transient codes, so the node backs off
444
+ and resumes as it does for Codex. A failed login is `blocked` at once.
445
+ - When the Stop hook blocks the agent's stop nine times running, the CLI ends
446
+ the turn anyway and says so; the goal is then `blocked` (the agent keeps
447
+ asking to stop, and continuing only repeats that). The template's verifier
448
+ still judges the tree. Write the objective as a condition the agent can show
449
+ it met from inside the session; one that depends on something after the
450
+ agent stops can never be met.
451
+ - The session id is the thread id. A new session is started with
452
+ `--session-id` so it is known before the goal is set, and the session is kept
453
+ out of the registry with `skipPersistence` (not `noSessionPersistence`, which
454
+ would stop the CLI writing the transcript `--resume` needs). A reopened
455
+ process resumes it with `--resume`; the goal is still set there, so the node
456
+ continues it at once.
457
+ - Steers are written into the running turn, which the CLI folds in. Cancel and
458
+ pause send stream-json's `interrupt` control request; the goal stays set.
459
+ - `tokensUsed` counts the session's input, output and cache-read tokens, and is
460
+ carried across reopened processes. `tokenBudget` counts from when the current
461
+ objective was set; past it the turn is interrupted and the goal ends
462
+ `budgetLimited`. `sandboxMode` does not apply; put the boundary in `launcher`.
330
463
 
331
464
  The `goal` template wraps this in `goal → verify → repair-gate`. The goal node
332
465
  continues on failure, so a goal that ran out of budget is still judged. The
333
- verifier's node timeout is sized from the contract (each check's timeout, at
334
- least 30 min), so a judge that takes hours is not cut off at the 30-minute node
335
- default. `maxRepairs` defaults to 1.
466
+ verifier's node timeout is sized from the contract — the sum of every check's
467
+ timeout (its default when unset), times one plus `fixOnFailureRounds`, plus five
468
+ minutes, and never under 30 min — so a judge that takes hours is not cut off at
469
+ the 30-minute node default. `maxRepairs` defaults to 1, and `maxNodeVisits` is
470
+ `maxRepairs + 3`.
471
+
472
+ ```bash
473
+ clawo goal "Make the failing tests in tests/ pass; the brief is in GOAL.md" \
474
+ -c "npm test" --check-timeout 20 --token-budget 50000000 --max-repairs 2 --wait
475
+ ```
336
476
 
337
477
  ## The verifier is a terminal barrier
338
478
 
@@ -344,7 +484,9 @@ may follow the verifier" would be the wrong rule anyway: what matters is not tha
344
484
  a node ran, but that the tree moved. So the kernel measures. Each evidence bundle
345
485
  records a content digest of the working tree (HEAD, the full `git diff HEAD`,
346
486
  and the path and bytes of every untracked file), and when the run ends, if any
347
- workspace-touching node ran after the verdict, the digest is recomputed.
487
+ workspace-touching node ran after the verdict, the digest is recomputed in the
488
+ directory the checks ran in. Every kind except `verifier`, `router` and
489
+ `human_gate` counts as workspace-touching.
348
490
 
349
491
  If it moved, the outcome drops from `verified` to `unverified` with the reason
350
492
  recorded on the run. Not `refuted` — no check failed; we simply stopped knowing,
@@ -361,26 +503,57 @@ so anything the reviewers change is covered by the verdict.
361
503
  ## Completion
362
504
 
363
505
  `RunState` is `pending | running | awaiting_human | verifying | completed |
364
- failed | cancelled`. **`completed` is reachable only from `verifying`.**
506
+ failed | cancelled`. `verifying` is the state while a verifier node runs; the run
507
+ returns to `running` when it finishes, and ends in one of the last three.
365
508
 
366
509
  `RunOutcome` is `verified | unverified | refuted` and answers a different
367
- question: not "did it stop" but "did anything check it". A run with no contract
368
- completes as `unverified` — it says it does not know, which is not the same as
369
- success.
510
+ question: not "did it stop" but "did anything check it". The outcome is the last
511
+ verdict the run produced — each verifier visit replaces it, so a repair loop is
512
+ judged by its last check — and a `refuted` run ends `failed`, never `completed`.
513
+ A run with no contract completes as `unverified` — it says it does not know,
514
+ which is not the same as success.
515
+
516
+ The implicit `__verify` is the last node, and a run only ends `completed` after
517
+ its last node has run, so every such path is checked. A spec that places its own
518
+ `"contract": "run"` verifier gets no `__verify`, and must route every path through
519
+ that node: a run whose path skips it ends `completed` / `unverified`, with no
520
+ check run.
521
+
522
+ A run whose spec declares a run-level `contract` cannot complete without having
523
+ run it. The kernel appends a terminal verifier unless the spec places its own
524
+ `contract: "run"` verifier, but a router can still send control past that
525
+ verifier; a run that ends with no verdict from a `contract: "run"` verifier ends
526
+ `failed`, with outcome `unverified` (nothing ran, so nothing was refuted). A
527
+ pass from a verifier with an inline contract does not stand in for the declared
528
+ one.
370
529
 
371
530
  ## Control
372
531
 
373
- | Action | Tool | HTTP | CLI |
374
- | ------------- | ------------------ | --------------------------------- | -------------------------------------- |
375
- | Start | `workflow_start` | `POST /workflow/new` | `clawo solve` / `fanout` / `goal` |
376
- | Poll | `workflow_status` | `GET /workflow/<id>/state` | `clawo workflow show <id>` |
377
- | List | `workflow_list` | `GET /workflow/list` | `clawo workflow list` |
378
- | Resume | `workflow_resume` | `POST /workflow/<id>/resume` | `clawo workflow resume <id>` |
379
- | Cancel | `workflow_cancel` | `POST /workflow/<id>/cancel` | `clawo workflow cancel <id>` |
380
- | Steer | `workflow_steer` | `POST /workflow/<id>/steer` | `clawo workflow steer <id> "<text>"` |
381
- | Answer a gate | `workflow_approve` | `POST /workflow/<id>/approve` | `clawo workflow approve <id> [reject]` |
382
- | Evidence | — | `GET /workflow/<id>/evidence` | `clawo verify <id>` |
383
- | Live events | — | `GET /workflow/<id>/events` (SSE) | — |
532
+ | Action | Tool | HTTP | CLI |
533
+ | ------------- | ------------------ | --------------------------------- | --------------------------------------- |
534
+ | Start | `workflow_start` | `POST /workflow/new` | `clawo solve` / `fanout` / `goal` |
535
+ | Poll | `workflow_status` | `GET /workflow/<id>/state` | `clawo workflow show <id>` |
536
+ | List | `workflow_list` | `GET /workflow/list` | `clawo workflow list` |
537
+ | Resume | `workflow_resume` | `POST /workflow/<id>/resume` | `clawo workflow resume <id>` |
538
+ | Cancel | `workflow_cancel` | `POST /workflow/<id>/cancel` | `clawo workflow cancel <id>` |
539
+ | Steer | `workflow_steer` | `POST /workflow/<id>/steer` | `clawo workflow steer <id> "<text>"` |
540
+ | Answer a gate | `workflow_approve` | `POST /workflow/<id>/approve` | `clawo workflow approve <id> [reject]` |
541
+ | Evidence | — | `GET /workflow/<id>/evidence` | `clawo verify <id>` |
542
+ | Live events | — | `GET /workflow/<id>/events` (SSE) | `--wait` on `solve` / `fanout` / `goal` |
543
+
544
+ `POST /workflow/new` takes `{ spec, cwd?, runId?, contract? }`; the templates are
545
+ built by the tool and by the CLI, which posts the spec it built. `workflow_status`
546
+ returns the run's state, outcome, `outcomeReason`, verdict, current node, evidence
547
+ id, cost, error, node records and consensus votes, plus its last N events with
548
+ `events: N`; `GET /workflow/<id>/state` and `clawo workflow show <id> --json` return
549
+ the whole record. The event stream opens with a `snapshot` (the
550
+ run's `workflow_list` entry), then sends each kernel event as a `workflow-event`. `GET /workflow/list` and
551
+ `workflow_list` filter by `workflow` and `state`, newest first, 25 by default.
552
+
553
+ Cancel and steer reach a run only while it executes in this process. Answering a
554
+ gate does not need that: a run parked by a process that has since exited is
555
+ resumed with the answer attached. On the CLI, `reject`, `false` or `no` as the
556
+ last argument of `clawo workflow approve` rejects; anything else approves.
384
557
 
385
558
  In a running `goal` node, steer text goes into the current turn (`turn/steer`).
386
559
  Otherwise, steer text is **prepended** to the next agent node's prompt: an instruction that
@@ -393,9 +566,12 @@ belong before the task.
393
566
  in-flight agent turn is owned by the session layer and finishes on its own
394
567
  schedule; the kernel does not pretend to kill it. The abandoned attempt keeps
395
568
  running, so agent nodes name their session per attempt
396
- (`<runId>-<nodeId>-a<n>`) — otherwise the dying attempt's teardown would stop
397
- the retry's session. A node that writes to a fixed path outside the run
398
- directory can still race its own retry; scope such writes per attempt.
569
+ (`<runId>-<nodeId>-a<n>`, unless `sessionName` pins one), and a verifier names
570
+ its fixer sessions per visit and attempt (`<runId>-<nodeId>-v<visit>a<n>-fix-<round>`)
571
+ — otherwise the dying attempt's teardown would stop the retry's session. A
572
+ `goal` node is the exception: it watches its signal, so a timeout interrupts its
573
+ turn. A node that writes to a fixed path outside the run directory can still race
574
+ its own retry; scope such writes per attempt.
399
575
 
400
576
  Because such an attempt can still write, a run about to report `verified`
401
577
  waits briefly for outstanding attempts to settle, and reports `unverified`
@@ -405,9 +581,11 @@ belong before the task.
405
581
  - Cancel and a node timeout are different things. A timeout is a node failure
406
582
  (it still gets its retries and still honours `onFailure`); cancelling ends the
407
583
  run.
408
- - Nothing prunes run directories. Delete them yourself
584
+ - Nothing prunes run directories. Delete a run that is not executing yourself
409
585
  (`rm -rf ~/.claw-orchestrator/wf/<runId>`), or programmatically with
410
- `SessionManager.workflowDelete(runId)`.
586
+ `SessionManager.workflowDelete(runId)`, which claims the run first. No tool or
587
+ CLI command deletes a workflow run; `POST /autoloop/<id>/delete` removes an
588
+ autoloop's.
411
589
 
412
590
  ## Related
413
591
 
package/assets/banner.jpg DELETED
Binary file