@chrok/braid 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,45 @@
1
1
  # Braid
2
2
 
3
- Braid is a small execution runtime for dynamically constructed graphs of isolated
4
- model invocations. A parent submits a complete DAG in one call; Braid resolves
5
- routing and dependencies, runs independent nodes concurrently, and returns the
6
- successful execution-terminal outputs. It is an agent primitive, not a workflow
7
- builder.
3
+ Braid is a small TypeScript runtime for LLM agent graphs with bounded loops,
4
+ live updates, and isolated Git worktrees. A parent submits a graph, runs
5
+ independent invocations concurrently, and can revise the graph or pause and
6
+ resume handoffs while it runs. Each execution retains its own outputs and
7
+ checkpoints; explicit `integrate` nodes apply selected work to the source checkout.
8
8
 
9
9
  [![CI](https://github.com/Epsirom/braid/actions/workflows/ci.yml/badge.svg)](https://github.com/Epsirom/braid/actions/workflows/ci.yml)
10
+ [![npm core](https://img.shields.io/npm/v/%40chrok%2Fbraid?label=%40chrok%2Fbraid)](https://www.npmjs.com/package/@chrok/braid)
11
+ [![npm Pi](https://img.shields.io/npm/v/%40chrok%2Fpi-braid?label=%40chrok%2Fpi-braid)](https://www.npmjs.com/package/@chrok/pi-braid)
10
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
11
13
 
12
- **v0.1:** Experimental, TypeScript, Node.js 22+, ESM, no runtime dependencies.
13
- The core has no Pi, provider SDK, or framework dependency.
14
+ **0.2 API:** Experimental, Node.js 22+, ESM. The framework-agnostic core
15
+ has no runtime dependencies; the OpenAI-compatible runner and Pi extension are
16
+ optional integrations. The npm badges show published versions; see
17
+ [GitHub releases](https://github.com/Epsirom/braid/releases) for release notes.
18
+ Read the [0.1 → 0.2 migration guide](docs/compatibility.md#migrating-from-01-to-02)
19
+ before upgrading. For the previous API, use the
20
+ [0.1.3 documentation](https://github.com/Epsirom/braid/tree/v0.1.3).
21
+
22
+ | Package | Purpose |
23
+ | --- | --- |
24
+ | [@chrok/braid](https://www.npmjs.com/package/@chrok/braid) | Core runtime and optional OpenAI-compatible runner |
25
+ | [@chrok/pi-braid](https://www.npmjs.com/package/@chrok/pi-braid) | Pi background jobs, execution controls, reminders, and live flow panel; installs the matching core dependency |
26
+
27
+ ## What changed in 0.2?
28
+
29
+ - **Editable graphs, captured executions.** `startBraid` exposes revision-checked
30
+ updates and pause/resume; `executionId` identifies a particular invocation,
31
+ while `nodeId` identifies its reusable definition.
32
+ - **Bounded refinement loops.** Explicit feedback edges revisit nodes with fresh
33
+ workspaces. Each loop has a finite iteration limit, and `maxExecutions` caps
34
+ materialized executions across the whole run, including live updates.
35
+ - **Explicit workspace integration.** Read-only workers inspect isolated
36
+ predecessor snapshots. `merge` combines changes in a new worktree;
37
+ `integrate` writes to the source checkout. Final integration is never implicit.
38
+ - **Per-execution failure policy.** Optional failures keep their errors and
39
+ partial work for recovery. Set `requireSuccess: true` to fail the whole run.
40
+
41
+ See [execution control](docs/execution-control.md) for the full contract and
42
+ [ROADMAP.md](ROADMAP.md) for the current scope and remaining work.
14
43
 
15
44
  ## Why Braid?
16
45
 
@@ -67,20 +96,22 @@ try {
67
96
 
68
97
  For a live provider, use the adapter in the API example below. Installation and
69
98
  running the example above do not make model requests. In a Git checkout, Braid
70
- creates node worktrees and may append a merge agent that can integrate changes
71
- into the source checkout. Read [workspace behavior](#worktrees-and-merge-agents)
99
+ creates a fresh worktree for each execution. Only explicit `integrate` nodes
100
+ write results back to the source checkout. Read [workspace behavior](#worktrees-and-merge-agents)
72
101
  before running a custom adapter against a repository.
73
102
 
74
103
  ## Install in Pi
75
104
 
76
- With Pi 0.87.1 and Node.js 22.19+:
105
+ With Pi 1.0.1 and Node.js 22.19+:
77
106
 
78
107
  ```sh
79
108
  pi install npm:@chrok/pi-braid
80
109
  ```
81
110
 
82
111
  Run `/reload`, ask Pi to analyze a task with Braid, and open `/braid` to inspect
83
- the job. The extension includes the matching core runtime; no checkout is needed.
112
+ the job. npm installs the exact matching core dependency; no checkout is needed
113
+ for published versions. To try the current source checkout, follow the
114
+ [local Pi installation guide](integrations/pi/README.md#install-this-local-checkout-in-pi).
84
115
 
85
116
  ![Braid Pi flow panel with an offline example](docs/assets/pi-panel.svg)
86
117
 
@@ -163,34 +194,111 @@ output. The graph has no special fork, branch, or join nodes.
163
194
  ### Graph schema
164
195
 
165
196
  ```ts
166
- type BraidNode =
167
- | { type: "execute"; id: string; prompt: string; model?: string;
168
- workspace?: "read-only" | "worktree" }
169
- | { type: "decision"; id: string; prompt: string;
170
- choices: readonly string[]; model?: string; workspace?: "read-only" | "worktree" }
171
- | { type: "merge"; id: string; prompt?: string; model?: string };
172
-
173
- type Edge = { from: string; to: string; choice?: string };
174
- type BraidInput = { goal: string; nodes: readonly BraidNode[]; edges: readonly Edge[] };
197
+ type NodePrompt = string | {
198
+ template: string;
199
+ variables: Readonly<Record<string, string>>;
200
+ };
201
+ type BraidInputNode =
202
+ | { type: "execute"; id: string; prompt: NodePrompt; model?: string;
203
+ workspace?: "read-only" | "worktree"; notifyOnCompletion?: boolean;
204
+ requireSuccess?: boolean; pauseAfter?: boolean }
205
+ | { type: "decision"; id: string; prompt: NodePrompt;
206
+ choices: readonly string[]; model?: string; workspace?: "read-only" | "worktree"; notifyOnCompletion?: boolean;
207
+ requireSuccess?: boolean; pauseAfter?: boolean }
208
+ | { type: "merge" | "integrate"; id: string; prompt?: NodePrompt; model?: string;
209
+ notifyOnCompletion?: boolean;
210
+ requireSuccess?: boolean; pauseAfter?: boolean };
211
+
212
+ type Edge = { from: string; to: string; choice?: string; feedback?: string; executionId?: string };
213
+ type BraidInput = {
214
+ goal: string;
215
+ nodes: readonly BraidInputNode[];
216
+ edges: readonly Edge[];
217
+ loops?: readonly { id: string; entry: string; maxIterations: number }[];
218
+ promptTemplates?: Readonly<Record<string, string>>;
219
+ };
220
+ ```
221
+
222
+ IDs are unique, non-empty strings. Goals, models, choices, plain-string prompts,
223
+ and rendered prompts must be non-empty strings when present. Decision choices
224
+ must be non-empty and unique.
225
+ `workspace` is optional on execute/decision nodes and forbidden on merge/integrate
226
+ nodes. Both read-only and writable Git executions get isolated predecessor
227
+ snapshots; read-only disables write capabilities. Outside Git all filesystem
228
+ access is read-only. `notifyOnCompletion` requests host reminders, `pauseAfter`
229
+ holds outgoing scheduling, and `requireSuccess` makes failure abort the whole run.
230
+ All three booleans default to false.
231
+
232
+ Unknown fields, missing references, duplicate exact edges, and undeclared cycles
233
+ are rejected. Declared structured loops require a finite iteration limit and an
234
+ explicit feedback edge. Disconnected components are allowed; every root runs.
235
+ Only decision nodes may have choice edges, and choices need not all have exits.
236
+
237
+ `validateGraph(input)` validates without execution. Invalid graphs raise
238
+ `GraphValidationError`; invalid options raise `TypeError`. Optional invocation
239
+ failures remain in execution history; required or infrastructure failures make
240
+ the run fail. See [execution control](docs/execution-control.md) for the complete
241
+ loop, live update, pause/resume, and failure contract.
242
+
243
+ ### Reusable prompt templates
244
+
245
+ Define shared instructions once in `promptTemplates`, then give each node a
246
+ template name and explicit string variables. The same representation is accepted
247
+ by the core API and Pi's `braid` tool:
248
+
249
+ ```json
250
+ {
251
+ "goal": "Review the runtime and validation code",
252
+ "promptTemplates": {
253
+ "review": "Review {{target}} for {{focus}}. Inspect source and tests, then report findings with file references and supporting evidence."
254
+ },
255
+ "nodes": [
256
+ {
257
+ "type": "execute", "id": "runtime", "workspace": "read-only",
258
+ "prompt": {
259
+ "template": "review",
260
+ "variables": { "target": "src/runtime.ts", "focus": "scheduling and cancellation" }
261
+ }
262
+ },
263
+ {
264
+ "type": "execute", "id": "validation", "workspace": "read-only",
265
+ "prompt": {
266
+ "template": "review",
267
+ "variables": { "target": "src/validate.ts", "focus": "input validation" }
268
+ }
269
+ }
270
+ ],
271
+ "edges": []
272
+ }
175
273
  ```
176
274
 
177
- IDs are unique, non-empty strings. Prompts, goals, models, and choices must be
178
- non-empty strings when present. Decision choices must be non-empty and unique.
179
- `workspace` is optional on execute/decision nodes and forbidden on merge nodes.
180
- Use `"read-only"` for analysis, review, routing, and synthesis. Omit it or use
181
- `"worktree"` for the existing behavior: an isolated writable worktree in Git,
182
- read-only access outside Git. Explicit read-only nodes read the live source
183
- directory, not a fixed snapshot; see [workspace semantics](#worktrees-and-merge-agents).
184
- Unknown fields, unsupported node types, missing references, duplicate exact
185
- edges, and cycles are rejected. Cycles are rejected even if a decision might
186
- make them inactive. Disconnected components are allowed; every root runs.
187
- Distinct choices may connect the same node pair. Choices need not all have
188
- outgoing edges, and decisions may themselves be terminal.
189
-
190
- `validateGraph(input)` is also exported for validation without execution. It and
191
- `braid` throw `GraphValidationError` for invalid graphs, before invoking a model.
192
- Invalid runtime options throw `TypeError`. Execution failures return a result
193
- with `status: "failed"` instead of discarding the run's successful outputs.
275
+ - Placeholders use `{{name}}`, with optional surrounding whitespace inside the
276
+ braces. Names match `[A-Za-z_][A-Za-z0-9_]*`; repeated placeholders reuse the
277
+ same value. Template names are any non-empty strings.
278
+ - `variables` is required, including `{}` for a constant template. Values must
279
+ be strings and must match the template's variables exactly. Empty values are
280
+ allowed if the complete rendered prompt is still non-empty.
281
+ - Values are inserted literally once: no expressions, recursive expansion,
282
+ escaping, environment lookup, or access to other nodes' outputs. To insert
283
+ literal double braces into a template, pass them as a variable value.
284
+ - Unknown templates, missing/unused variables, invalid template syntax, and
285
+ blank rendered prompts throw `GraphValidationError` before any model call.
286
+ All declared templates are syntax-checked, including unused ones. Errors
287
+ identify the template and, for node references/rendering, the affected node.
288
+ - Templates work on execute, decision, merge, and integrate prompts. Omitted
289
+ merge/integrate prompts retain their defaults. Plain-string prompts are never rendered,
290
+ even when they contain `{{...}}`.
291
+
292
+ Templates reduce duplicate text in graph/tool-call arguments; every worker still
293
+ receives its fully rendered prompt. Rendering and input snapshotting happen
294
+ before asynchronous execution. Templates are local to one submission, with no
295
+ saved registry or new runtime dependencies.
296
+
297
+ `BraidInputNode`, `NodePrompt`, and `PromptTemplateReference` describe compact
298
+ inputs. `BraidNode`, `ExecuteNode`, `DecisionNode`, `MergeNode`, `IntegrateNode`,
299
+ and `ModelRequest.node` keep their string-prompt types for runners. Core applies the
300
+ same non-empty prompt validation after rendering; it does not impose a prompt
301
+ length or token cap (see [resource limits](docs/resource-limits.md)).
194
302
 
195
303
  ### Options
196
304
 
@@ -199,9 +307,10 @@ with `status: "failed"` instead of discarding the run's successful outputs.
199
307
  | `runner` | Required | A fresh, isolated invocation for each call |
200
308
  | `defaultModel` | Adapter default | Overridden by each node's `model` |
201
309
  | `cwd` | `process.cwd()` | Source checkout for core-managed worktrees; non-Git directories grant read-only capabilities |
310
+ | `maxExecutions` | `1000` | Total materialized executions, including skipped branches, across all rounds and updates; positive safe integer |
202
311
  | `maxConcurrency` | `4` | Maximum simultaneous runtime-managed node invocations; positive integer |
203
312
  | `nodeTimeoutMs` | `60_000` | Separate deadline for each node, starting when it runs (not while queued) |
204
- | `graphTimeoutMs` | `300_000` | Whole execution deadline, including node queueing; starts after validation |
313
+ | `graphTimeoutMs` | `300_000` | Whole execution deadline, including node queueing and paused gates; starts after validation |
205
314
  | `signal` | None | Caller cancellation signal; aborts running nodes and marks queued nodes cancelled |
206
315
  | `onEvent` | None | Live observer for graph/node creation, readiness, starts, handoffs, completions, skips, failures, and graph completion |
207
316
 
@@ -248,9 +357,16 @@ A pending node waits until **all incoming edges are resolved**. Then:
248
357
  Failures propagate as context through unconditional edges, allowing successors
249
358
  and merge agents to inspect errors and recover partial work. Failed decisions
250
359
  cannot activate choice-labelled edges; their unconditional successors can run.
251
- Skip propagation uses topological order. There are no automatic retries. The
252
- graph still reports failure if any node fails, even if a later node recovers its
253
- work successfully.
360
+ There are no automatic retries. Failures are optional by default; a
361
+ `requireSuccess: true` execution failure aborts the whole run, cancels siblings,
362
+ and drains writes and cleanup. The policy is captured when the instance is admitted.
363
+ Graph deadlines and execution limits remain active while paused.
364
+
365
+ Nodes are mutable definitions; execution instances retain captured prompts and
366
+ inputs. `startBraid` exposes revision-checked `update`, `resume`, `snapshot`, and
367
+ `cancel`. Updates can change running nodes, completed nodes, edges, templates, and
368
+ loop topology at any time before finalization. Completion uses the latest graph.
369
+ See [loops and live updates](docs/execution-control.md) for schemas and examples.
254
370
 
255
371
  ## Execution events
256
372
 
@@ -260,37 +376,22 @@ timestamped. The log is diagnostic data and does not alter scheduling; observer
260
376
  exceptions and rejected promises are ignored. Event payloads are frozen before
261
377
  being retained and delivered.
262
378
 
263
- The event sequence includes:
264
-
265
- - `graph_created`, `node_created`, and `edge_created` when the submitted DAG is
266
- admitted; an appended final merge emits its own node/edge creation events.
267
- - `node_runnable` and `node_started` when scheduling admits a node.
268
- - `workspace_updated` for Git workspace preparation, checkpointing, cleanup, and
269
- explicitly requested read-only workspaces.
270
- - `handoff` for every direct predecessor output passed to a downstream node,
271
- including the upstream decision when present.
272
- - `node_completed`, `node_skipped`, and `node_failed`, including output,
273
- decision, model, usage, latency, skip reason, or error where applicable.
274
- A node start/completion/failure event is emitted only once the corresponding
275
- transition is admitted; a provider call that expires before admission has no
276
- `node_started` event.
277
- - `graph_completed` with execution terminal IDs, or `graph_failed` with the
278
- representative error and any successful terminal IDs.
279
-
280
- `node_completed` and `node_failed` include node latency; `node_completed` also
281
- includes the selected decision and reported usage when available. Event output
282
- is diagnostic context and may be previewed by an adapter; `BraidResult.events`
283
- retains the complete event payloads.
284
-
285
- The core event stream is intentionally a log, not a second control API. It does
286
- not permit graph mutation or runtime intervention. A Pi adapter can use it to
287
- render live topology, handoffs, failures, and active nodes without reconstructing
288
- scheduler state from final results. Tool selection remains the responsibility of
289
- the host agent; the optional Pi adapter supplies explicit proactive-use guidance
290
- so Braid is considered for complex multi-branch reasoning without forcing it for
291
- every prompt. In the Pi adapter, nodes can inspect the live source read-only or
292
- edit individual Git worktrees; nodes outside Git stay read-only. Merge agents
293
- handle integration; the parent reviews results and runs shell commands and tests.
379
+ The event sequence includes initial `graph_created`, `node_created`, and
380
+ `edge_created`; revision-bearing `graph_updated`; and per-instance
381
+ `node_runnable`, `node_started`, `workspace_updated`, `handoff`, `node_completed`,
382
+ `node_failed`, or `node_skipped`. Instance events carry `executionId`, admission
383
+ `revision`, and optional `loopId`/`iteration`. Handoffs also identify their exact
384
+ `fromExecutionId`.
385
+
386
+ `loop_started`/`loop_completed` report rounds. `execution_paused` and
387
+ `execution_resumed` report gates. `graph_completed` or `graph_failed` terminates
388
+ the log. Completion/failure is published after checkpointing. Node events repeat
389
+ with distinct execution IDs when the same definition runs again.
390
+
391
+ Use `startBraid` methods to control a run. Event callbacks are observers; throwing
392
+ or rejecting does not change scheduler behavior. The Pi panel displays current
393
+ topology, iterations, pause gates, and a bounded event preview; complete history
394
+ remains available in the result.
294
395
 
295
396
  ## Context isolation and model runners
296
397
 
@@ -336,124 +437,66 @@ Read tools follow the host filesystem permissions and are not a security sandbox
336
437
 
337
438
  ### Worktrees and merge agents
338
439
 
339
- By default, execute and decision nodes in a Git checkout receive detached worktrees
340
- under `os.tmpdir()/braid-workspaces-*/<unique-id>`. The first writable node captures tracked
341
- staged/unstaged changes, deletions, and non-ignored untracked files with a temporary
342
- index. Snapshot creation preserves the source index, branch, and files. Ignored
343
- files are not copied, and submodules are not initialized or recursively captured.
344
- Pi rejects writes inside submodules. If a custom runner populates one, core
345
- reports cleanup failure and retains the worktree rather than losing those files.
346
- Empty repositories are supported. Worktree nodes share this baseline until a merge
347
- ends; subsequent worktree nodes snapshot the current source checkout. Relative working directories
348
- are preserved. Code changes do not implicitly flow into successor worktrees.
349
-
350
- Set `workspace: "read-only"` on execute/decision nodes that only inspect or reason
351
- about files. They read the original `cwd`, including ignored files accessible to
352
- the adapter, without creating a snapshot, worktree, checkpoint, or merge source.
353
- In Git they retain inspection commands (`status`, `diff`, `show`, `log`,
354
- `ls-files`, `rev-parse`), with optional Git index writes disabled. Relative paths
355
- use the original `cwd`, including when it is a subdirectory of the repository.
356
- These reads observe the live checkout: parent edits or concurrent merge nodes
357
- may change files during execution. Use worktree mode when a fixed snapshot is
358
- needed. Predecessor edits are visible in the source only after integration;
359
- their worktrees/checkpoints can still be inspected explicitly.
360
-
361
- Explicit read-only allocations appear in `workspace_updated`, node/predecessor
362
- workspace metadata, and `result.workspaces` with mode `read-only` and state
363
- `ready`. They have no cleanup lifecycle or recovery refs. Implicit non-Git
364
- read-only runs retain their existing event/result behavior. A custom runner is
365
- trusted code and must honor the workspace capability; this is not an OS sandbox.
366
-
367
- For a mixed graph, give review branches `workspace: "read-only"` and leave
368
- implementation branches in worktree mode. Use a read-only execute node to
369
- summarize findings; a merge node integrates file changes and does not accept
370
- the `workspace` field.
371
-
372
- Add `{ type: "merge", id: "integrate" }` with incoming edges from any number of
373
- sources. The merge agent receives predecessor errors, workspace paths, and Git
374
- checkpoint refs and operates directly in the invoking checkout. **Core does not
375
- run merge, cherry-pick, or apply automatically.** The agent reviews each source,
376
- chooses which changes to integrate and how, resolves conflicts, then calls the
377
- `finish_merge` tool with exactly one disposition and reason per source:
440
+ Every Git execution owns a fresh worktree. Roots use the job's initial snapshot
441
+ of tracked edits and non-ignored untracked files without changing the real index.
442
+ Successors use immutable predecessor checkpoints; read-only executions use the
443
+ same snapshot rules with writes disabled. Independent code branches require an
444
+ explicit merge before an ordinary worker consumes them together.
445
+
446
+ - `merge` combines selected predecessor checkpoints in a **new isolated worktree**.
447
+ - `integrate` applies selected results to the **invoking working checkout**.
448
+
449
+ There is no automatic final integration. Add an explicit integrate node when
450
+ results should reach the working branch. Both operations accept optional prompts
451
+ and require `finish_merge` with exactly one disposition per source:
378
452
 
379
453
  ```json
380
454
  { "dispositions": [
381
- { "nodeId": "implementation", "disposition": "integrated", "reason": "Cherry-picked the reviewed checkpoint" },
382
- { "nodeId": "alternative", "disposition": "discarded", "reason": "The selected implementation supersedes this alternative" }
455
+ { "executionId": "<source-execution-id>", "disposition": "integrated", "reason": "Applied the reviewed checkpoint" }
383
456
  ] }
384
457
  ```
385
458
 
386
- Each merge request includes bounded changed-file lists, diff statistics and diff
387
- previews in `mergeSources[].changes`, plus the invoking checkout's dirty status.
388
- These are inspection aids; the agent still chooses every integration operation.
389
- The `finish_merge` schema lists only the current source IDs and requires exactly
390
- one decision per source. Invalid calls report missing, unexpected and duplicate
391
- IDs so the agent can correct the call.
392
-
393
- The model-facing `git` tool takes a `command` from the node's allowed command
394
- enum and a separate `args` array. For example, `{"command":"show","args":["REF:path"]}`.
395
- The programmatic `ModelRequest.git` API continues to accept the complete argument
396
- array. Rejected commands include relevant supported alternatives; Braid never
397
- silently substitutes a different Git operation. A first argument identical to
398
- `command` is rejected before execution: `{ "command": "status", "args": ["status"] }`
399
- would otherwise silently query a path named `status`. Use `args: ["--short"]`
400
- for the full status, or `args: ["--", "status"]` for an intentional path filter;
401
- same-named branches can use a full ref such as `refs/heads/diff`.
402
-
403
- `archived` means integration failed. A missing finish call, unresolved conflicts,
404
- or any archived source fails the merge node. Once the agent ends, core removes
405
- its predecessor worktrees. Every removed source retains a checkpoint ref,
406
- including intentionally discarded changes and ignored node output files. A
407
- later consumer can inspect a removed source through `git show <checkpointRef>`.
408
- Core also records `backupRef` for the source checkout before each merge agent.
409
-
410
- When declared nodes settle, core releases worktrees whose contents match their
411
- own snapshot, including those from failed nodes, as `discarded` with reason
412
- `No changes from snapshot`. Their checkpoint refs point to the snapshot commit
413
- without creating empty checkpoint commits; original node errors remain visible.
414
- This comparison includes ignored output files. Explicit merge nodes still run
415
- even when their sources have no changes.
416
-
417
- When worktrees with changes remain, core appends an ordinary merge
418
- agent named `__braid_merge__` (with a suffix if needed), using the run's default
419
- model. It appears in results, events, usage, and terminal outputs. Merge nodes
420
- are exclusive within a graph and serialized per source checkout across runs in
421
- the same process. Avoid concurrent external edits to that checkout while merging;
422
- this lock does not coordinate other processes or the parent editor.
423
-
424
- Analysis-only graphs therefore keep their declared terminal outputs and do not
425
- incur an automatic merge model call. Consumers should not assume that every Git
426
- run includes `__braid_merge__`; use `terminalOutputs` for the completed endpoints.
427
-
428
- Cancellation or graph timeout prevents new merge agents from starting. Core waits
429
- for tracked writes, archives remaining work, and removes its worktrees. Merge
430
- agent failure follows the same archive/cleanup path. It does not reset the source
431
- checkout: partial integration or Git conflict state may remain for review, with
432
- `backupRef` available for recovery. Filesystem/Git cleanup errors are reported as
433
- `CLEANUP_FAILED` with retained workspace paths; a process crash cannot run cleanup.
434
-
435
- `result.workspaces` and `node.workspace` report paths, states, reasons, and refs.
436
- A cleaned worktree path is historical; use `checkpointRef` to recover its contents:
437
-
438
- ```sh
439
- git show <checkpointRef>:path/to/file
440
- git diff <snapshotCommit> <checkpointRef>
441
- ```
442
-
443
- Recovery refs live under `refs/braid/checkpoints/` and `refs/braid/merge-backups/`.
444
- After reviewing them, remove a particular ref with `git update-ref -d <ref>`.
445
- They preserve recoverable Git objects without retaining worktree directories.
459
+ The agent chooses merge, cherry-pick, apply, restore, or file edits; core never
460
+ chooses for it. Sources include bounded diffs relative to the initial job
461
+ snapshot. Integrate also receives the current source checkout's dirty status and
462
+ must preserve unrelated user changes. Unresolved conflicts, a missing finish
463
+ call, or an `archived` disposition fail the operation.
464
+
465
+ The model-facing Git tool separates `command` from `args`, for example
466
+ `{ "command": "show", "args": ["REF:path"] }`. The programmatic `request.git`
467
+ accepts the complete argument array. Duplicate command prefixes, network Git,
468
+ branch switching, and filesystem-boundary overrides are rejected.
469
+
470
+ Instances checkpoint before downstream admission. All source checkpoints remain
471
+ reusable; no merge consumes or deletes a predecessor's result. Job cleanup
472
+ archives and removes owned worktrees, retaining refs under
473
+ `refs/braid/checkpoints/`. Integration additionally captures a pre-write
474
+ `backupRef` under `refs/braid/merge-backups/`. Target workspace `dispositions`
475
+ record the agent's source selections.
476
+
477
+ `result.workspaces` is keyed by execution ID; `result.nodes[id].workspace` is the
478
+ latest convenience view. Cleaned paths are historical; recover their contents
479
+ with `git show <checkpointRef>:path` or inspect changes with
480
+ `git diff <snapshotCommit> <checkpointRef>`. Remove reviewed refs explicitly with
481
+ `git update-ref -d <ref>`.
482
+
483
+ Cancellation drains tracked writes before cleanup. Checkpoint/cleanup failures
484
+ fail the run and retain unsafe-to-remove paths. Failed integration may leave
485
+ partial source edits or conflict state with its backup available for recovery;
486
+ it does not reset the checkout. Source integrations serialize within one process;
487
+ this does not coordinate parent edits or other processes. Read-only workers remain
488
+ isolated from those source edits. See [execution control](docs/execution-control.md).
446
489
 
447
490
  **The adapter is a trust boundary, not a security sandbox.** It must avoid shared
448
491
  conversation state, expose only its declared capabilities, and forward `signal` to its provider.
449
492
  The core never gives the model arbitrary code execution or a recursive Braid
450
493
  tool. An optional Pi adapter translates this same contract without changing the
451
- runtime; the core v0.1 package does not depend on Pi. See
494
+ runtime; the core package does not depend on Pi. See
452
495
  [`integrations/pi/README.md`](integrations/pi/README.md) for installation and testing.
453
496
 
454
497
  The included OpenAI-compatible adapter uses fresh Chat Completions contexts,
455
498
  a strict `decide({ choice })` tool and one tool-free continuation for decisions.
456
- Merge nodes use a local `git` / `finish_merge` tool loop; ordinary OpenAI nodes
499
+ Merge/integrate nodes use a local `git` / `finish_merge` tool loop; ordinary OpenAI nodes
457
500
  have no filesystem tools. Pi exposes read and guarded write tools plus these
458
501
  core Git/merge tools. Tool errors go back to merge agents for recovery. Both
459
502
  adapters sum usage across their model calls and forward cancellation.
@@ -481,16 +524,14 @@ remain 60 seconds per node and 5 minutes per graph.
481
524
  `BraidResult` contains:
482
525
 
483
526
  - `status`: `completed` or `failed`.
484
- - `terminalOutputs`: `{ [nodeId]: { output, decision?, model? } }` for completed
485
- nodes with **no active outgoing edges in this execution**. This includes a
486
- decision selecting a choice with no successor. A completed node does not
487
- become terminal merely because its active successor failed or was skipped.
488
- - `nodes`: all node states plus available output, decision, model, usage, error,
489
- skip reason, start/end timestamps (Unix milliseconds), and latency in ms.
490
- Skipped nodes have no start time or latency. Nodes without a valid
491
- response have no output. A failed decision may retain its text and selected
492
- choice for debugging; choice-labelled edges still remain blocked.
493
- - `workspaces`: Git workspace states and recovery refs, including cleaned sources.
527
+ - `terminalOutputs`: latest successful unconsumed outputs keyed by node ID.
528
+ - `terminalExecutionIds`: exact IDs of the successful execution endpoints.
529
+ - `nodes`: latest states/results per node ID, including output, error, usage,
530
+ timing, and workspace metadata. A pending definition has no execution ID yet.
531
+ - `executions`: all immutable invocation definitions and their final results,
532
+ keyed by execution ID, including historical rounds and deleted definitions.
533
+ - `revision`: the last committed graph revision.
534
+ - `workspaces`: execution-keyed workspace states, recovery refs, and merge choices.
494
535
  - `events`: the immutable execution log described above. `onEvent` observes live
495
536
  copies of the same state transitions while the run is in progress.
496
537
  - `metadata`: run/root identity, timestamps, monotonic latency, summed reported
@@ -516,7 +557,7 @@ uncancelled provider work after timeout can outlive a slot.
516
557
 
517
558
  - [`src/types.ts`](src/types.ts): public graph, provider, and result types.
518
559
  - [`src/validate.ts`](src/validate.ts): strict validation, graph snapshot,
519
- dependency indexes, and iterative DAG validation.
560
+ dependency indexes, and iterative validation of acyclic regions and structured loops.
520
561
  - [`src/runtime.ts`](src/runtime.ts): edge resolution, explicit state transitions,
521
562
  bounded concurrent scheduling, invocation deadlines, execution events, and result accounting.
522
563
  - [`src/workspaces.ts`](src/workspaces.ts): Git snapshots, checkpoint refs, merge
@@ -526,18 +567,18 @@ uncancelled provider work after timeout can outlive a slot.
526
567
  - [`test/`](test/): deterministic scheduling, execution-event, and intercepted HTTP/tool tests.
527
568
 
528
569
  There is one in-memory execution context per run, plus a process-local mutex
529
- per source checkout for merge agents. Worktree registration and removal are
570
+ per source checkout for integrate agents. Worktree registration and removal are
530
571
  serialized per common Git directory within the process; model calls remain
531
572
  concurrent. These locks do not coordinate other processes. `rootRunId` equals
532
- `runId` in v0.1. Centralized invocation admission and
573
+ `runId`. Centralized invocation admission and
533
574
  usage aggregation leave places to thread a shared root budget in a future
534
575
  nested-run implementation; **nested runs and shared budget enforcement are not
535
- implemented**. The current scheduler deliberately rescans a small DAG after
576
+ implemented**. The current scheduler deliberately rescans a small graph after
536
577
  completions; `onEvent` is an observer for diagnostics and visualization, not a
537
578
  scheduler event bus.
538
579
 
539
- Out of scope: loops, arbitrary code nodes, persistent workflows, saved templates,
540
- resuming saved runs, human approval, editing UI, user-directed graph mutation,
580
+ Out of scope: arbitrary/unstructured cycles, nested/overlapping loops, arbitrary
581
+ code nodes, durable workflow recovery, saved templates, a graphical editing UI,
541
582
  and recursive Braid calls from model nodes.
542
583
 
543
584
  ## Contributing and project status