| 1 | # Workflow Authoring |
| 2 | |
| 3 | > **Ordinary multi-agent work does not require this file.** In Operate, send |
| 4 | > normal messages. Small work stays direct; multiple delegated steps use a |
| 5 | > compact Workflow plan with dependencies, bounded scopes, and completion |
| 6 | > evidence. Fleet manages the same sub-agents and roles. One bounded, |
| 7 | > independent task can use a direct agent, with `followup` for continued work. |
| 8 | > Act/Agent may also use optional soft-auto launch. See |
| 9 | > [Automatic Workflows](AUTOMATIC_WORKFLOWS.md). |
| 10 | |
| 11 | Workflow has one runtime boundary: authored source lowers to typed |
| 12 | Rust `WorkflowSpec`, Rust validates the IR, and the scheduler/headless worker |
| 13 | runtime executes leaves. Authoring languages do not get hidden authority to own |
| 14 | files, shell, network, providers, cancellation, or TUI state. |
| 15 | |
| 16 | Compatibility launch paths on the `workflow` tool: |
| 17 | |
| 18 | | Input | When to use | |
| 19 | |-------|-------------| |
| 20 | | `plan` | Structured goal / phases / children (preferred agent path) | |
| 21 | | `script` | Short inline JS the model owns | |
| 22 | | `source_path` | Checked-in `.workflow.js` / `.workflow.ts` in the workspace | |
| 23 | |
| 24 | Use `agent(action="roster")` to inspect the saved Fleet models and roles before |
| 25 | assigning children. Native plan children accept `model` for a saved shortlist |
| 26 | selector, or `role`/`profile` for a saved assignment. Named Exact Fleets keep |
| 27 | their member routes fixed and reject per-step model overrides. Plan children |
| 28 | also accept `cwd`, a repository-relative working directory — required in |
| 29 | multi-repository workspaces so the child (and worktree isolation) resolves the |
| 30 | right repository, mirroring `task({cwd})`. |
| 31 | |
| 32 | For a guided walkthrough from fleet task specs to Workflow authoring and |
| 33 | monitoring, see [fleet + Workflow Tutorial](FLEET_WORKFLOW_TUTORIAL.md). |
| 34 | |
| 35 | |
| 36 | ## Access model |
| 37 | |
| 38 | The Workflow script is a **coordinator only**. It has no filesystem or shell of |
| 39 | its own. Real work happens in sub-agents the script launches. |
| 40 | |
| 41 | | Layer | What it can access | |
| 42 | |-------|--------------------| |
| 43 | | Workflow script (JS VM) | Script variables, branching/loops, `task()` / `parallel()` / `pipeline()`, `phase` / `log`, `budget` / `args`. **No** direct FS, shell, network, env, imports, clock, or randomness. | |
| 44 | | Workflow-spawned sub-agents | Normal tool surface (read/search/edit/write, shell, web, MCP) subject to role posture, allowlists, and parent policy. File edits for write-capable roles auto-accept under Workflow; shell / web / MCP still require parent auto-approve or fail closed. | |
| 45 | | Parent session | Working directory, configured tools/MCP, permission mode, sandbox/network rules. | |
| 46 | |
| 47 | ### Scale |
| 48 | |
| 49 | - Up to **16 concurrent** live agents in one run (additional spawns wait for a slot). |
| 50 | - Up to **1_000 agents per run** (VM lifetime spawn cap). |
| 51 | - Configured `max_children` and `max_concurrent` can narrow these limits. |
| 52 | - Automatic launch is model-judged on scope; the host enforces only the hard `max_children` / `max_depth` ceilings. |
| 53 | - Plan the population the work needs and let the host queue and clamp it. |
| 54 | These ceilings are enforcement, not a reason to pre-shrink a valid plan. |
| 55 | |
| 56 | See the Workflow JS sandbox tests for the fail-closed host surface inventory. |
| 57 | |
| 58 | ## Language Choice |
| 59 | |
| 60 | | Surface | Strength | Tradeoff | v0.8.60 stance | |
| 61 | |---|---|---|---| |
| 62 | | YAML / JSON IR | Simple, reviewable, no runtime | Verbose for generated workflows | Keep as interchange/debug format | |
| 63 | | JavaScript | Familiar object syntax and easy agent generation | Unsafe if executed as a general runtime | First-class authoring through declarative compile-only subset | |
| 64 | | TypeScript | Best editor/types story for workflow SDK | Needs stripping/typechecking if full TS is supported | Same compile-only subset for now; richer SDK later | |
| 65 | |
| 66 | The default high-capability path is TypeScript/JavaScript authoring, but only as |
| 67 | a compile step. The compiler accepts a JSON-compatible object inside |
| 68 | `workflow({...})` from `.workflow.js` or `.workflow.ts`, lowers it to |
| 69 | `WorkflowSpec`, and runs the Rust validation gate. (Starlark authoring was a |
| 70 | bootstrap reference and has been removed; Workflow authoring is JS-only.) |
| 71 | |
| 72 | ## Contract |
| 73 | |
| 74 | Accepted source shape: |
| 75 | |
| 76 | ```js |
| 77 | export default workflow({ |
| 78 | "id": "issue-audit-js", |
| 79 | "goal": "Audit an issue fix with parallel agents", |
| 80 | "nodes": [ |
| 81 | { |
| 82 | "branch": { |
| 83 | "id": "parallel-audit", |
| 84 | "children": [ |
| 85 | { "agent": { "id": "code-audit", "prompt": "Review code", "agent_type": "review" } }, |
| 86 | { "agent": { "id": "test-audit", "prompt": "Review tests", "agent_type": "verifier" } } |
| 87 | ] |
| 88 | } |
| 89 | }, |
| 90 | { "reduce": { "id": "summary", "inputs": ["code-audit", "test-audit"], "prompt": "Summarize" } } |
| 91 | ] |
| 92 | }); |
| 93 | ``` |
| 94 | |
| 95 | Supported node wrappers: `agent`, `branch`, `sequence`, `reduce`, |
| 96 | `teacher_review`, `loop_until`, `cond`, and `expand`. Raw `WorkflowNode` JSON IR |
| 97 | with `kind` / `spec` also remains valid. |
| 98 | |
| 99 | An `agent` node may declare `"profile": "reviewer"` to run as a named fleet |
| 100 | roster profile. The name is trimmed and lowercased at compile time and must be |
| 101 | a single token (no whitespace, quotes, or `=`); the saved roster is resolved at |
| 102 | dispatch time, and explicit fields on the agent override profile defaults. |
| 103 | |
| 104 | The runtime `task()` surface also accepts `cwd` for an existing repository- |
| 105 | relative working directory. This is required when a workflow is launched from |
| 106 | a multi-repository workspace and the child needs shell or file access. `cwd` |
| 107 | is validated by the host, does not grant mutation authority, and should be |
| 108 | paired with `worktree: true` when the child needs an isolated checkout. |
| 109 | |
| 110 | The compiler rejects effectful constructs such as `import`, `require`, `fetch`, |
| 111 | `process`, `Deno`, `Bun`, `child_process`, file reads/writes, `eval`, `async`, |
| 112 | and `await`. This is intentionally stricter than JavaScript: workflow source is |
| 113 | a familiar declaration format, not a second execution runtime. The denied |
| 114 | effects are not denied to the run — put them in a child worker, which has |
| 115 | the full tool surface, and keep the script to coordination. |
| 116 | |
| 117 | ## Verification |
| 118 | |
| 119 | - `cargo test -p codewhale-workflow --locked javascript` |
| 120 | |
| 121 | Current example: `workflows/issue_audit.workflow.js`. |
| 122 | |
| 123 | ## Agent-Written fleet Workflows |
| 124 | |
| 125 | The primary product flow is not "ask the user to write a script." The main |
| 126 | agent should decide when a task deserves workflow orchestration, draft the |
| 127 | Workflow source, show the plan for the current permission mode, and then let |
| 128 | the runtime compile and monitor it. |
| 129 | |
| 130 | Workflow owns the plan: phases, branches, loops, reducers, and intermediate |
| 131 | results. fleet owns the durable roster, member identity, semantic role, and |
| 132 | saved provider/model pins or inheritance. Runtime owns tool posture, launch |
| 133 | concurrency, leases, heartbeats, logs, receipts, and resume/stop/restart |
| 134 | controls. In other words, a workflow selects fleet members and monitors their |
| 135 | Runtime runs; it isn't an executor, because the script has no shell or |
| 136 | filesystem of its own — effects live in the workers. |
| 137 | |
| 138 | Workflow-to-Runtime launch validation applies a conservative default shape |
| 139 | before any Workflow IR is lowered to selected workers: |
| 140 | |
| 141 | - up to 1,000 total worker agents per Workflow run; |
| 142 | - up to 16 live worker agents at once; larger populations queue (block) on the |
| 143 | host's per-run concurrency gate until a live slot frees, then select through |
| 144 | fleet and execute through Runtime; |
| 145 | - Workflow IR structural nesting no deeper than 5; |
| 146 | - Runtime child delegation defaults to 3 levels and has an opt-in hard ceiling |
| 147 | of 8; that execution budget is independent of Workflow IR shape; |
| 148 | - loops require `max_iterations`; |
| 149 | - dynamic `expand` nodes require `max_children` and a template. |
| 150 | |
| 151 | Those limits distinguish population from instantaneous launch concurrency. A |
| 152 | valid 1,000-agent Workflow can still drain through a smaller Runtime worker |
| 153 | pool. Model selection stays per member: a DeepSeek preset can suggest |
| 154 | `deepseek-v4-pro` for the orchestrator and `deepseek-v4-flash` for nearby |
| 155 | workers, but users and agents may override any slot when the task calls for it. |
| 156 | |
| 157 | ## Experimental search is a Workflow option |
| 158 | |
| 159 | Experimental search generalizes the existing best-of-N recipe without adding a |
| 160 | new product mode, scheduler, or sub-agent API. A provider-neutral |
| 161 | `WorkflowSearchSpec` freezes the objective, baseline, model request and resolved |
| 162 | version, public evidence, evaluator hash, hard gates, scoring rule, budgets, |
| 163 | write scope, rounds, and review-only integration policy before admission. |
| 164 | |
| 165 | The current JS starter supports structured generation and read-only review with |
| 166 | `strategy: "search"`. Runtime-owned command gates, hidden evaluation, benchmark |
| 167 | scoring, and clean-baseline replay are an explicit host seam still to wire; a |
| 168 | candidate's self-verdict must never be promoted into evaluator truth. See |
| 169 | [Workflow Experimental Search](WORKFLOW_EXPERIMENTAL_SEARCH.md). |
| 170 |