@chrok/braid 0.1.2 → 0.2.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.
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,8 +96,8 @@ 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
@@ -80,7 +109,9 @@ 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 0.2 before publication, 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,28 +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
- | { type: "decision"; id: string; prompt: string;
169
- choices: readonly string[]; model?: string }
170
- | { type: "merge"; id: string; prompt?: string; model?: string };
171
-
172
- type Edge = { from: string; to: string; choice?: string };
173
- 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
+ };
174
220
  ```
175
221
 
176
- IDs are unique, non-empty strings. Prompts, goals, models, and choices must be
177
- non-empty strings when present. Decision choices must be non-empty and unique.
178
- Unknown fields, unsupported node types, missing references, duplicate exact
179
- edges, and cycles are rejected. Cycles are rejected even if a decision might
180
- make them inactive. Disconnected components are allowed; every root runs.
181
- Distinct choices may connect the same node pair. Choices need not all have
182
- outgoing edges, and decisions may themselves be terminal.
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
+ }
273
+ ```
183
274
 
184
- `validateGraph(input)` is also exported for validation without execution. It and
185
- `braid` throw `GraphValidationError` for invalid graphs, before invoking a model.
186
- Invalid runtime options throw `TypeError`. Execution failures return a result
187
- 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)).
188
302
 
189
303
  ### Options
190
304
 
@@ -193,9 +307,10 @@ with `status: "failed"` instead of discarding the run's successful outputs.
193
307
  | `runner` | Required | A fresh, isolated invocation for each call |
194
308
  | `defaultModel` | Adapter default | Overridden by each node's `model` |
195
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 |
196
311
  | `maxConcurrency` | `4` | Maximum simultaneous runtime-managed node invocations; positive integer |
197
312
  | `nodeTimeoutMs` | `60_000` | Separate deadline for each node, starting when it runs (not while queued) |
198
- | `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 |
199
314
  | `signal` | None | Caller cancellation signal; aborts running nodes and marks queued nodes cancelled |
200
315
  | `onEvent` | None | Live observer for graph/node creation, readiness, starts, handoffs, completions, skips, failures, and graph completion |
201
316
 
@@ -242,9 +357,16 @@ A pending node waits until **all incoming edges are resolved**. Then:
242
357
  Failures propagate as context through unconditional edges, allowing successors
243
358
  and merge agents to inspect errors and recover partial work. Failed decisions
244
359
  cannot activate choice-labelled edges; their unconditional successors can run.
245
- Skip propagation uses topological order. There are no automatic retries. The
246
- graph still reports failure if any node fails, even if a later node recovers its
247
- 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.
248
370
 
249
371
  ## Execution events
250
372
 
@@ -254,35 +376,22 @@ timestamped. The log is diagnostic data and does not alter scheduling; observer
254
376
  exceptions and rejected promises are ignored. Event payloads are frozen before
255
377
  being retained and delivered.
256
378
 
257
- The event sequence includes:
258
-
259
- - `graph_created`, `node_created`, and `edge_created` when the submitted DAG is
260
- admitted; an appended final merge emits its own node/edge creation events.
261
- - `node_runnable` and `node_started` when scheduling admits a node.
262
- - `workspace_updated` for Git workspace preparation, checkpointing, and cleanup.
263
- - `handoff` for every direct predecessor output passed to a downstream node,
264
- including the upstream decision when present.
265
- - `node_completed`, `node_skipped`, and `node_failed`, including output,
266
- decision, model, usage, latency, skip reason, or error where applicable.
267
- A node start/completion/failure event is emitted only once the corresponding
268
- transition is admitted; a provider call that expires before admission has no
269
- `node_started` event.
270
- - `graph_completed` with execution terminal IDs, or `graph_failed` with the
271
- representative error and any successful terminal IDs.
272
-
273
- `node_completed` and `node_failed` include node latency; `node_completed` also
274
- includes the selected decision and reported usage when available. Event output
275
- is diagnostic context and may be previewed by an adapter; `BraidResult.events`
276
- retains the complete event payloads.
277
-
278
- The core event stream is intentionally a log, not a second control API. It does
279
- not permit graph mutation or runtime intervention. A Pi adapter can use it to
280
- render live topology, handoffs, failures, and active nodes without reconstructing
281
- scheduler state from final results. Tool selection remains the responsibility of
282
- the host agent; the optional Pi adapter supplies explicit proactive-use guidance
283
- so Braid is considered for complex multi-branch reasoning without forcing it for
284
- every prompt. In the Pi adapter, Git nodes can inspect and edit individual
285
- worktrees; nodes outside Git stay read-only. Merge agents 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.
286
395
 
287
396
  ## Context isolation and model runners
288
397
 
@@ -308,7 +417,8 @@ merge agents. Adapters must enforce workspace capabilities and wrap mutating
308
417
  file tools in `request.withWorkspaceWrite(operation)`, so cleanup waits for
309
418
  in-flight writes and rejects later writes. Core Git mutations use this barrier.
310
419
  The included Pi adapter provides guarded `write`/`edit` alongside its read tools.
311
- Outside Git, adapters must provide read-only capabilities. Pi never provides
420
+ For read-only workspaces, adapters must omit mutating tools; the core write
421
+ barrier also rejects writes. This includes all nodes outside Git. Pi never provides
312
422
  `bash`, `powershell`, or a test runner to nodes.
313
423
 
314
424
  `request.predecessors` contains direct active predecessors in incoming-edge
@@ -327,91 +437,66 @@ Read tools follow the host filesystem permissions and are not a security sandbox
327
437
 
328
438
  ### Worktrees and merge agents
329
439
 
330
- In a Git checkout, execute and decision nodes receive detached worktrees under
331
- `os.tmpdir()/braid-workspaces-*/<unique-id>`. The first node captures tracked
332
- staged/unstaged changes, deletions, and non-ignored untracked files with a temporary
333
- index. Snapshot creation preserves the source index, branch, and files. Ignored
334
- files are not copied, and submodules are not initialized or recursively captured.
335
- Pi rejects writes inside submodules. If a custom runner populates one, core
336
- reports cleanup failure and retains the worktree rather than losing those files.
337
- Empty repositories are supported. Nodes share this baseline until a merge ends;
338
- subsequent nodes snapshot the current source checkout. Relative working directories
339
- are preserved. Code changes do not implicitly flow into successor worktrees.
340
-
341
- Add `{ type: "merge", id: "integrate" }` with incoming edges from any number of
342
- sources. The merge agent receives predecessor errors, workspace paths, and Git
343
- checkpoint refs and operates directly in the invoking checkout. **Core does not
344
- run merge, cherry-pick, or apply automatically.** The agent reviews each source,
345
- chooses which changes to integrate and how, resolves conflicts, then calls the
346
- `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:
347
452
 
348
453
  ```json
349
454
  { "dispositions": [
350
- { "nodeId": "implementation", "disposition": "integrated", "reason": "Cherry-picked the reviewed checkpoint" },
351
- { "nodeId": "alternative", "disposition": "discarded", "reason": "The selected implementation supersedes this alternative" }
455
+ { "executionId": "<source-execution-id>", "disposition": "integrated", "reason": "Applied the reviewed checkpoint" }
352
456
  ] }
353
457
  ```
354
458
 
355
- Each merge request includes bounded changed-file lists, diff statistics and diff
356
- previews in `mergeSources[].changes`, plus the invoking checkout's dirty status.
357
- These are inspection aids; the agent still chooses every integration operation.
358
- The `finish_merge` schema lists only the current source IDs and requires exactly
359
- one decision per source. Invalid calls report missing, unexpected and duplicate
360
- IDs so the agent can correct the call.
361
-
362
- The model-facing `git` tool takes a `command` from the node's allowed command
363
- enum and a separate `args` array. For example, `{"command":"show","args":["REF:path"]}`.
364
- The programmatic `ModelRequest.git` API continues to accept the complete argument
365
- array. Rejected commands include relevant supported alternatives; Braid never
366
- silently substitutes a different Git operation. A first argument identical to
367
- `command` is rejected before execution: `{ "command": "status", "args": ["status"] }`
368
- would otherwise silently query a path named `status`. Use `args: ["--short"]`
369
- for the full status, or `args: ["--", "status"]` for an intentional path filter;
370
- same-named branches can use a full ref such as `refs/heads/diff`.
371
-
372
- `archived` means integration failed. A missing finish call, unresolved conflicts,
373
- or any archived source fails the merge node. Once the agent ends, core removes
374
- its predecessor worktrees. Every removed source retains a checkpoint ref,
375
- including intentionally discarded changes and ignored node output files. A
376
- later consumer can inspect a removed source through `git show <checkpointRef>`.
377
- Core also records `backupRef` for the source checkout before each merge agent.
378
-
379
- When declared nodes settle and worktrees remain, core appends an ordinary merge
380
- agent named `__braid_merge__` (with a suffix if needed), using the run's default
381
- model. It appears in results, events, usage, and terminal outputs. Merge nodes
382
- are exclusive within a graph and serialized per source checkout across runs in
383
- the same process. Avoid concurrent external edits to that checkout while merging;
384
- this lock does not coordinate other processes or the parent editor.
385
-
386
- Cancellation or graph timeout prevents new merge agents from starting. Core waits
387
- for tracked writes, archives remaining work, and removes its worktrees. Merge
388
- agent failure follows the same archive/cleanup path. It does not reset the source
389
- checkout: partial integration or Git conflict state may remain for review, with
390
- `backupRef` available for recovery. Filesystem/Git cleanup errors are reported as
391
- `CLEANUP_FAILED` with retained workspace paths; a process crash cannot run cleanup.
392
-
393
- `result.workspaces` and `node.workspace` report paths, states, reasons, and refs.
394
- A cleaned worktree path is historical; use `checkpointRef` to recover its contents:
395
-
396
- ```sh
397
- git show <checkpointRef>:path/to/file
398
- git diff <snapshotCommit> <checkpointRef>
399
- ```
400
-
401
- Recovery refs live under `refs/braid/checkpoints/` and `refs/braid/merge-backups/`.
402
- After reviewing them, remove a particular ref with `git update-ref -d <ref>`.
403
- 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).
404
489
 
405
490
  **The adapter is a trust boundary, not a security sandbox.** It must avoid shared
406
491
  conversation state, expose only its declared capabilities, and forward `signal` to its provider.
407
492
  The core never gives the model arbitrary code execution or a recursive Braid
408
493
  tool. An optional Pi adapter translates this same contract without changing the
409
- runtime; the core v0.1 package does not depend on Pi. See
494
+ runtime; the core package does not depend on Pi. See
410
495
  [`integrations/pi/README.md`](integrations/pi/README.md) for installation and testing.
411
496
 
412
497
  The included OpenAI-compatible adapter uses fresh Chat Completions contexts,
413
498
  a strict `decide({ choice })` tool and one tool-free continuation for decisions.
414
- 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
415
500
  have no filesystem tools. Pi exposes read and guarded write tools plus these
416
501
  core Git/merge tools. Tool errors go back to merge agents for recovery. Both
417
502
  adapters sum usage across their model calls and forward cancellation.
@@ -439,16 +524,14 @@ remain 60 seconds per node and 5 minutes per graph.
439
524
  `BraidResult` contains:
440
525
 
441
526
  - `status`: `completed` or `failed`.
442
- - `terminalOutputs`: `{ [nodeId]: { output, decision?, model? } }` for completed
443
- nodes with **no active outgoing edges in this execution**. This includes a
444
- decision selecting a choice with no successor. A completed node does not
445
- become terminal merely because its active successor failed or was skipped.
446
- - `nodes`: all node states plus available output, decision, model, usage, error,
447
- skip reason, start/end timestamps (Unix milliseconds), and latency in ms.
448
- Skipped nodes have no start time or latency. Nodes without a valid
449
- response have no output. A failed decision may retain its text and selected
450
- choice for debugging; choice-labelled edges still remain blocked.
451
- - `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.
452
535
  - `events`: the immutable execution log described above. `onEvent` observes live
453
536
  copies of the same state transitions while the run is in progress.
454
537
  - `metadata`: run/root identity, timestamps, monotonic latency, summed reported
@@ -474,7 +557,7 @@ uncancelled provider work after timeout can outlive a slot.
474
557
 
475
558
  - [`src/types.ts`](src/types.ts): public graph, provider, and result types.
476
559
  - [`src/validate.ts`](src/validate.ts): strict validation, graph snapshot,
477
- dependency indexes, and iterative DAG validation.
560
+ dependency indexes, and iterative validation of acyclic regions and structured loops.
478
561
  - [`src/runtime.ts`](src/runtime.ts): edge resolution, explicit state transitions,
479
562
  bounded concurrent scheduling, invocation deadlines, execution events, and result accounting.
480
563
  - [`src/workspaces.ts`](src/workspaces.ts): Git snapshots, checkpoint refs, merge
@@ -484,18 +567,18 @@ uncancelled provider work after timeout can outlive a slot.
484
567
  - [`test/`](test/): deterministic scheduling, execution-event, and intercepted HTTP/tool tests.
485
568
 
486
569
  There is one in-memory execution context per run, plus a process-local mutex
487
- per source checkout for merge agents. Worktree registration and removal are
570
+ per source checkout for integrate agents. Worktree registration and removal are
488
571
  serialized per common Git directory within the process; model calls remain
489
572
  concurrent. These locks do not coordinate other processes. `rootRunId` equals
490
- `runId` in v0.1. Centralized invocation admission and
573
+ `runId`. Centralized invocation admission and
491
574
  usage aggregation leave places to thread a shared root budget in a future
492
575
  nested-run implementation; **nested runs and shared budget enforcement are not
493
- implemented**. The current scheduler deliberately rescans a small DAG after
576
+ implemented**. The current scheduler deliberately rescans a small graph after
494
577
  completions; `onEvent` is an observer for diagnostics and visualization, not a
495
578
  scheduler event bus.
496
579
 
497
- Out of scope: loops, arbitrary code nodes, persistent workflows, saved templates,
498
- 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,
499
582
  and recursive Braid calls from model nodes.
500
583
 
501
584
  ## Contributing and project status