zig-core 0.10.0

Core library for zig — workflow orchestration engine for AI coding agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# The .zwf / .zwfz Format

A `.zwf` file is a plain TOML file that describes a workflow — a DAG of AI agent
steps with shared variables, conditional routing, and data flow. Zig compiles
this into zag orchestration commands at execution time.

A `.zwfz` file is a **zip archive** containing one workflow file plus
associated resource files (prompt files, schemas, etc.). Use
`zig workflow pack` to create distributable zip archives.

## File Structure

```toml
[workflow]
name = "my-workflow"           # Required: unique workflow name
description = "What it does"   # Optional: human-readable description
tags = ["tag1", "tag2"]        # Optional: discovery/filtering tags
version = "1.0.0"              # Optional: workflow version
provider = "claude"            # Optional: default provider for all steps
model = "sonnet"               # Optional: default model for all steps

[roles.analyst]                # Reusable role definitions
system_prompt = "You are an analyst."

[vars.target]                  # Variable declarations
type = "string"
default = "."
description = "Path to analyze"

[[step]]                       # Step definitions (one or more)
name = "analyze"
prompt = "Analyze ${target}"
role = "analyst"
```

## Sections

### `[workflow]` — Metadata

| Field         | Required | Description                                                             |
|---------------|----------|-------------------------------------------------------------------------|
| `name`        | Yes      | Unique workflow name                                                    |
| `description` | No       | Human-readable description                                              |
| `tags`        | No       | Tags for discovery and filtering                                        |
| `version`     | No       | Workflow version string (e.g., "1.0.0")                                 |
| `provider`    | No       | Default provider for all steps (claude, codex, gemini, copilot, ollama) |
| `model`       | No       | Default model for all steps (steps can override)                        |
| `resources`   | No       | Reference files advertised to every step (see Resources below)          |
| `memory`      | No       | Memory injection mode: `all` (default), `global`, or `none` (see `zig docs memory`) |

When `provider` or `model` is set on the workflow, every step inherits it as a
default. A step can override the workflow-level value by setting its own
`provider` or `model` field. The workflow-level `memory` mode can similarly be
overridden per step.

### `[roles.<name>]` — Roles

Roles define reusable system prompts that shape agent behavior. Each role
provides its prompt inline or loads it from an external file. Steps reference
roles by name via the `role` field.

| Field               | Required | Description                                    |
|---------------------|----------|------------------------------------------------|
| `system_prompt`     | No*      | Inline system prompt (`${var}` refs allowed)   |
| `system_prompt_file`| No*      | Path to file containing the system prompt      |

\* One of `system_prompt` or `system_prompt_file` should be set. They are
mutually exclusive.

```toml
[roles.doctor]
system_prompt = "You are a board-certified physician."

[roles.nurse]
system_prompt_file = "prompts/nurse.md"       # Loaded relative to the .zwf file
```

Steps reference roles statically or dynamically:

```toml
[[step]]
name = "examine"
prompt = "Examine the patient"
role = "doctor"                               # Static reference

[[step]]
name = "specialist"
prompt = "Specialist examination"
role = "${specialist_type}"                   # Dynamic — resolved at runtime
```

### `[vars.<name>]` — Variables

Variables are shared state between steps. They can be referenced in prompts
and system prompts via `${var_name}` and updated by steps via the `saves` field.

| Field            | Required | Description                                         |
|------------------|----------|-----------------------------------------------------|
| `type`           | Yes      | `string`, `number`, `bool`, or `json`               |
| `default`        | No       | Default value (TOML literal)                        |
| `default_file`   | No       | Path to file whose contents become the default      |
| `description`    | No       | Human-readable purpose                              |
| `from`           | No       | Input source binding (`"prompt"`)                   |
| `required`       | No       | Must be non-empty before execution                  |
| `min_length`     | No       | Minimum string length (string vars only)            |
| `max_length`     | No       | Maximum string length (string vars only)            |
| `min`            | No       | Minimum numeric value (number vars only)            |
| `max`            | No       | Maximum numeric value (number vars only)            |
| `pattern`        | No       | Regex pattern to match (string vars only)           |
| `allowed_values` | No       | Restrict to specific values (TOML array)            |

#### Input Binding

Use `from = "prompt"` to bind the CLI user prompt to a variable. Only one
variable may use this. When set, the value from `zig run <workflow> "content"`
is assigned to the variable instead of being prepended as "User context:".

#### Constraints

Constraints are validated before step execution. If a constraint fails, zig
prints a clear error and aborts. Default values are also validated at parse
time (`zig validate`).

```toml
[vars.content]
type = "string"
from = "prompt"
required = true
min_length = 10
max_length = 5000
pattern = "^[A-Z]"
description = "Content to process (must start with uppercase)"

[vars.priority]
type = "string"
default = "medium"
allowed_values = ["low", "medium", "high"]

[vars.score]
type = "number"
min = 0.0
max = 100.0
```

### `[[step]]` — Steps

Each step is one zag agent invocation.

#### Core Fields

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `name`           | Yes      |         | Unique step identifier                   |
| `prompt`         | Yes      |         | Prompt template (`${var}` refs allowed)  |
| `description`    | No       | `""`    | Human-readable description of this step  |
| `provider`       | No       |         | Zag provider — overrides workflow-level default |
| `model`          | No       |         | Model name or size alias — overrides workflow-level default |
| `depends_on`     | No       | `[]`    | Steps that must complete first           |
| `inject_context` | No       | `false` | Inject dependency outputs into prompt    |
| `condition`      | No       |         | Only run if expression evaluates to true |
| `system_prompt`  | No       |         | Agent system prompt override (`${var}` refs allowed) |
| `role`           | No       |         | Role name or `${var}` reference (mutually exclusive with `system_prompt`) |
| `max_turns`      | No       |         | Maximum agentic turns                    |

#### Output and Data Flow

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `json`           | No       | `false` | Request structured JSON output           |
| `json_schema`    | No       |         | JSON schema to validate output           |
| `output`         | No       |         | Output format: `text`, `json`, `json-pretty`, `stream-json`, `native-json` |
| `saves`          | No       | `{}`    | Extract values from output into variables|

#### Failure and Retry

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `on_failure`     | No       | `fail`  | `fail`, `continue`, or `retry`           |
| `max_retries`    | No       |         | Retry limit (when `on_failure = "retry"`)|
| `retry_model`    | No       |         | Model to escalate to on retry            |
| `next`           | No       |         | Explicit next step (enables loops)       |
| `timeout`        | No       |         | Step timeout (e.g., `5m`, `30s`, `1h`)   |
| `tags`           | No       | `[]`    | Zag session tags                         |

#### Execution Environment

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `interactive`    | No       | `false` | Hand the terminal to the agent for this step (see below) |
| `auto_approve`   | No       | `false` | Auto-approve all agent actions           |
| `root`           | No       |         | Working directory override (`~/` and `$HOME` are expanded) |
| `add_dirs`       | No       | `[]`    | Additional directories in agent scope (`~/` and `$HOME` are expanded) |
| `env`            | No       | `{}`    | Per-step environment variables           |
| `files`          | No       | `[]`    | Files to attach to the agent prompt (`~/` and `$HOME` are expanded) |
| `resources`      | No       | `[]`    | Reference files advertised to this step's agent (see Resources below) |
| `storage`        | No       |         | Storage entries in scope for this step (omit = all; `[]` = none; list = named — see `zig docs storage`) |
| `memory`         | No       |         | Memory injection override: `all`, `global`, or `none` (inherits workflow default) |

##### Interactive Steps

Setting `interactive = true` hands the terminal to the agent for that step:
stdin, stdout, and stderr are all inherited, so the agent's TUI can render
normally and you can chat with it. The workflow pauses until you exit the
session (e.g. `/exit` or `Ctrl+D`), then resumes with downstream steps.

An interactive step **must be alone in its tier**. Because the step occupies
the terminal and waits for human input, it cannot share a tier with sibling
steps, run in a `race_group`, be retried on failure, or produce captured
output.

The following combinations are rejected at validation time:

- `race_group = "..."` — one human, one tty
- `saves = { ... }` — interactive output streams to the terminal, not a buffer
- `on_failure = "retry"` / `max_retries = N` — human input can't be replayed
- `json = true`, `output = "..."`, `json_schema = "..."` — these force
  non-interactive mode in `zag`
- `command = "review" | "plan" | "pipe" | "collect" | "summary"` — only the
  default `run` command has an interactive TUI

If another step shares the same tier (i.e. the same dependency set), add a
`depends_on` chain so it runs before or after the interactive step.

```toml
[[step]]
name = "plan"
prompt = "Draft a plan for ${goal}"

[[step]]
name = "review"
prompt = "Let's talk through the plan before executing"
depends_on = ["plan"]
interactive = true

[[step]]
name = "execute"
prompt = "Carry out the plan"
depends_on = ["review"]
```

#### Context Injection

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `context`        | No       | `[]`    | Session IDs to inject as context         |
| `plan`           | No       |         | Path to a plan file to prepend as context (`~/` and `$HOME` are expanded) |
| `mcp_config`     | No       |         | MCP configuration, path to config file (`~/` and `$HOME` are expanded) |

#### Isolation

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `worktree`       | No       | `false` | Run in an isolated git worktree          |
| `sandbox`        | No       |         | Docker sandbox name                      |
| `race_group`     | No       |         | Race group — first to finish wins, rest cancelled |

#### Command Step Types

| Field            | Required | Default | Description                              |
|------------------|----------|---------|------------------------------------------|
| `command`        | No       |         | Zag command: `review`, `plan`, `pipe`, `collect`, `summary` |
| `uncommitted`    | No       | `false` | Review uncommitted changes (`command = "review"`) |
| `base`           | No       |         | Base branch for review diff (`command = "review"`) |
| `commit`         | No       |         | Specific commit to review (`command = "review"`) |
| `title`          | No       |         | Review title (`command = "review"`)      |
| `plan_output`    | No       |         | Output path for plan (`command = "plan"`)|
| `instructions`   | No       |         | Additional plan instructions (`command = "plan"`) |

### `[storage.<name>]` — Storage

Storage declares **writable, structured working data** for the run — the
place where steps accumulate character sheets, summaries, bible documents,
or whatever else the workflow author wants to carry between steps. Paths
resolve against `<cwd>/.zig/`; absolute paths pass through. See
`zig docs storage` for the full model.

| Field         | Required | Description                                                  |
|---------------|----------|--------------------------------------------------------------|
| `type`        | No       | `"folder"` (default) or `"file"`                             |
| `path`        | Yes      | Relative to `<cwd>/.zig/`; absolute paths allowed            |
| `description` | No       | One-line description shown to the agent                      |
| `hint`        | No       | Free-form guidance on what should live in this storage       |
| `files`       | No       | Expected-file hints (folder-typed storage only)              |

```toml
[storage.characters]
type = "folder"
path = "./characters"
description = "Character profiles, one file per character"
hint = "Filename: <slug>.md. Include name, age, background, relationships."

[[storage.characters.files]]
name = "README.md"
description = "Character index"

[storage.bible]
type = "file"
path = "./bible.md"
description = "Single source of truth"
```

Steps narrow their view with `storage = ["name", ...]`. Omitting the field
exposes every declared entry; `storage = []` suppresses the block entirely.

## Resources

`resources` is a list of reference files that the workflow tells the agent
about — paths only, never inlined content. The agent reads them on demand with
its file tools when the user's request touches them. Use this for things like
CVs, style guides, and reference docs that you want available *if needed*
without burning context up front.

Each entry is either a bare path string or a detailed table. Paths are resolved
relative to the `.zwf` file. `~/` and `$HOME` are expanded at runtime, so
paths like `~/.zig/resources/my-cv.pdf` work correctly:

```toml
[workflow]
name = "cover-letter"
resources = [
  "./style-guide.md",                                # bare form
  { path = "./cv.md", name = "cv", description = "Candidate CV", required = true },
]

[[step]]
name = "draft"
prompt = "Write a cover letter for the attached job posting."
resources = [{ path = "./templates/cover-letter.md", description = "House template" }]
```

| Field         | Required | Description                                                                          |
|---------------|----------|--------------------------------------------------------------------------------------|
| `path`        | Yes      | Path relative to the `.zwf` file                                                     |
| `name`        | No       | Display name (defaults to the file's basename)                                       |
| `description` | No       | Description rendered next to the path in the system prompt                           |
| `required`    | No       | When true, missing files cause the run to fail (instead of being skipped + warning)  |

Inline `resources` are merged with files discovered in the global tiers
(`~/.zig/resources/_shared/`, `~/.zig/resources/<workflow-name>/`) and the
project tier (`<git-root>/.zig/resources/`). See `zig man resources` for the
full collection model and the `zig resources` management commands.

## Saves Selectors

The `saves` field maps variable names to JSONPath-like selectors:

| Selector         | Description                    |
|------------------|--------------------------------|
| `"$"`            | The entire output              |
| `"$.field"`      | A top-level JSON field         |
| `"$.nested.field"` | A nested JSON field         |

## Minimal Example

```toml
[workflow]
name = "hello"

[[step]]
name = "greet"
prompt = "Say hello to the user"
```

## Full Example

```toml
[workflow]
name = "code-review"
description = "Multi-perspective code review with synthesis"
tags = ["review", "quality"]

[vars.target]
type = "string"
default = "."
description = "Path to review"

[vars.score]
type = "number"

[vars.threshold]
type = "number"
default = 8

[vars.feedback]
type = "string"
default = ""

[[step]]
name = "analyze"
prompt = "Analyze the code structure of ${target}"
provider = "claude"
model = "sonnet"

[[step]]
name = "security-review"
prompt = "Review for security vulnerabilities"
depends_on = ["analyze"]
inject_context = true

[[step]]
name = "perf-review"
prompt = "Review for performance issues"
depends_on = ["analyze"]
inject_context = true

[[step]]
name = "synthesize"
prompt = "Create a unified code review report"
depends_on = ["security-review", "perf-review"]
inject_context = true

[[step]]
name = "quality-gate"
prompt = "Score this report 1-10"
depends_on = ["synthesize"]
inject_context = true
json = true
saves = { score = "$.score", feedback = "$.suggestions" }

[[step]]
name = "refine"
prompt = "Improve based on: ${feedback}"
depends_on = ["quality-gate"]
condition = "score < threshold"
next = "quality-gate"
on_failure = "retry"
max_retries = 2
```

## Zip Archives

A `.zwfz` bundle is a zip archive containing a workflow file and associated
files (prompt files, schemas, defaults). This enables distributing self-contained
workflow packages.

### Creating Archives

```bash
# Pack a directory into a .zwfz zip archive
zig workflow pack examples/healthcare/ -o healthcare.zwfz
```

The directory must contain exactly one TOML workflow file. All files in the
directory are included in the archive.

### Using Archives

Archives work transparently with `zig run` and `zig validate`:

```bash
zig validate healthcare.zwfz
zig run healthcare.zwfz "I have chest pain"
```

File paths (`system_prompt_file`, `default_file`) are resolved relative to the
TOML file — this works identically for both plain files and zip archives.

## See Also

- `zig docs variables` — variable references and data flow
- `zig docs conditions` — condition expression syntax
- `zig docs patterns` — common orchestration patterns
- `zig docs memory` — memory scratch pad and the `<memory>` block
- `zig man run` — executing workflows