# Workflow Authoring > 阅读简体中文版:[zh_hans/WORKFLOW_AUTHORING.md](zh_hans/WORKFLOW_AUTHORING.md)。 > **Ordinary multi-agent work does not require this file.** In Operate, send > normal messages. Small work stays direct; multiple delegated steps use a > compact Workflow plan with dependencies, bounded scopes, and completion > evidence. Fleet manages the same sub-agents and roles. One bounded, > independent task can use a direct agent, with `followup` for continued work. > Act/Agent may also use optional soft-auto launch. See > [Automatic Workflows](AUTOMATIC_WORKFLOWS.md). Workflow has one runtime boundary: authored source lowers to typed Rust `WorkflowSpec`, Rust validates the IR, and the scheduler/headless worker runtime executes leaves. Authoring languages do not get hidden authority to own files, shell, network, providers, cancellation, or TUI state. Compatibility launch paths on the `workflow` tool: | Input | When to use | |-------|-------------| | `plan` | Structured goal / phases / children (preferred agent path) | | `script` | Short inline JS the model owns | | `source_path` | Checked-in `.workflow.js` / `.workflow.ts` in the workspace | Use `agent(action="roster")` to inspect the saved Fleet models and roles before assigning children. Native plan children accept `model` for a saved shortlist selector, or `role`/`profile` for a saved assignment. Named Exact Fleets keep their member routes fixed and reject per-step model overrides. Plan children also accept `cwd`, a repository-relative working directory — required in multi-repository workspaces so the child (and worktree isolation) resolves the right repository, mirroring `task({cwd})`. For a guided walkthrough from fleet task specs to Workflow authoring and monitoring, see [fleet + Workflow Tutorial](FLEET_WORKFLOW_TUTORIAL.md). ## Access model The Workflow script is a **coordinator only**. It has no filesystem or shell of its own. Real work happens in sub-agents the script launches. | Layer | What it can access | |-------|--------------------| | 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. | | 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. | | Parent session | Working directory, configured tools/MCP, permission mode, sandbox/network rules. | ### Scale - Up to **16 concurrent** live agents in one run (additional spawns wait for a slot). - No default cumulative child limit; `[workflow] max_children = 0` leaves total work uncapped. - Owners can set a positive `max_children` limit and configure `max_concurrent`. - Automatic launch is model-judged on scope; the host enforces configured owner limits and plan validation. - Plan the population the work needs and let the host queue and clamp it. These ceilings are enforcement, not a reason to pre-shrink a valid plan. See the Workflow JS sandbox tests for the fail-closed host surface inventory. ## Language Choice | Surface | Strength | Tradeoff | Stance | |---|---|---|---| | YAML / JSON IR | Simple, reviewable, no runtime | Verbose for generated workflows | Keep as interchange/debug format | | JavaScript | Familiar object syntax and easy agent generation | Unsafe if executed as a general runtime | First-class authoring through declarative compile-only subset | | 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 | The default high-capability path is TypeScript/JavaScript authoring, but only as a compile step. The compiler accepts a JSON-compatible object inside `workflow({...})` from `.workflow.js` or `.workflow.ts`, lowers it to `WorkflowSpec`, and runs the Rust validation gate. (Starlark authoring was a bootstrap reference and has been removed; Workflow authoring is JS-only.) ## Contract Accepted source shape: ```js export default workflow({ "id": "issue-audit-js", "goal": "Audit an issue fix with parallel agents", "nodes": [ { "branch": { "id": "parallel-audit", "children": [ { "agent": { "id": "code-audit", "prompt": "Review code", "agent_type": "review" } }, { "agent": { "id": "test-audit", "prompt": "Review tests", "agent_type": "verifier" } } ] } }, { "reduce": { "id": "summary", "inputs": ["code-audit", "test-audit"], "prompt": "Summarize" } } ] }); ``` Supported node wrappers: `agent`, `branch`, `sequence`, `reduce`, `teacher_review`, `loop_until`, `cond`, and `expand`. Raw `WorkflowNode` JSON IR with `kind` / `spec` also remains valid. An `agent` node may declare `"profile": "reviewer"` to run as a named fleet roster profile. The name is trimmed and lowercased at compile time and must be a single token (no whitespace, quotes, or `=`); the saved roster is resolved at dispatch time, and explicit fields on the agent override profile defaults. The runtime `task()` surface also accepts `cwd` for an existing repository- relative working directory. This is required when a workflow is launched from a multi-repository workspace and the child needs shell or file access. `cwd` is validated by the host, does not grant mutation authority, and should be paired with `worktree: true` when the child needs an isolated checkout. The compiler rejects effectful constructs such as `import`, `require`, `fetch`, `process`, `Deno`, `Bun`, `child_process`, file reads/writes, `eval`, `async`, and `await`. This is intentionally stricter than JavaScript: workflow source is a familiar declaration format, not a second execution runtime. The denied effects are not denied to the run — put them in a child worker, which has the full tool surface, and keep the script to coordination. ## Gates A gate fires on a role's completion (`on: role_complete`) and blocks the role named in `blocks_role` until the gate passes. That makes `blocks_role` a **different** role from the one being gated — a gate that verifies phase 2's work must block phase 3, never phase 2: ```js // Correct: verify the implementer, block the next stage. gates: [{ id: "verify-fix", gate: "verify", on: "role_complete", role: "implement", blocks_role: "verify", on_fail: "escalate", max_retries: 1, require_explicit_verdict: true, }] ``` Admission rejects a gate that blocks its own role, a cycle of role gates, and a gate whose explicitly named prerequisite first occurs after its blocked role in a sequential plan. Diagnostics name the contradictory gate and roles before any child starts. Parallel structure, unknown profile roles and dynamic raw JavaScript order are not assigned a total order; runtime gates still enforce the prerequisite at dispatch. Native singleton and sequence steps use the same settled behavior as parallel steps. Ordinary failures retain partial results and let independent work continue. A step whose required input failed or is missing is skipped with a named reason; it never receives an empty substitute and never launches a child. Raw JavaScript `task()` remains fail-fast unless the author uses the existing settled combinators. Reducers can explicitly summarize partial inputs; they must disclose missing inputs instead of claiming that blocked work ran. Let workers finish their assigned outcome. Legacy model task limits are accepted for compatibility but ignored, and token hints are not spend caps. Actual owner limits belong in operator, role/Fleet, parent or saved-runtime configuration. Preserve partial findings and exact blockers for a follow-up instead of discarding completed work. Schema repair cannot invent a deadline from a legacy task field. ## Verification - `cargo test -p codewhale-workflow --locked javascript` Current example: `workflows/issue_audit.workflow.js`. ## Agent-Written fleet Workflows The primary product flow is not "ask the user to write a script." The main agent should decide when a task deserves workflow orchestration, draft the Workflow source, show the plan for the current permission mode, and then let the runtime compile and monitor it. Workflow owns the plan: phases, branches, loops, reducers, and intermediate results. fleet owns the durable roster, member identity, semantic role, and saved provider/model pins or inheritance. Runtime owns tool posture, launch concurrency, leases, heartbeats, logs, receipts, and resume/stop/restart controls. In other words, a workflow selects fleet members and monitors their Runtime runs; it isn't an executor, because the script has no shell or filesystem of its own — effects live in the workers. Workflow-to-Runtime launch validation applies a conservative default shape before any Workflow IR is lowered to selected workers: - no default total worker limit; a positive owner-configured `max_children` still applies; - up to 16 live worker agents at once; larger populations queue (block) on the host's per-run concurrency gate until a live slot frees, then select through fleet and execute through Runtime; - no default structural nesting cap; positive owner `workflow.max_depth` values apply; - no default Runtime delegation depth cap; explicit owner restrictions remain independent of Workflow IR shape; - loops require `max_iterations`; - dynamic `expand` nodes require `max_children` and a template. Those limits distinguish population from instantaneous launch concurrency. A long-running Workflow can still drain through a smaller Runtime worker pool. Model selection stays per member: a DeepSeek preset can suggest `deepseek-v4-pro` for the orchestrator and `deepseek-v4-flash` for nearby workers, but users and agents may override any slot when the task calls for it. ## Experimental search is a Workflow option Experimental search generalizes the existing best-of-N recipe without adding a new product mode, scheduler, or sub-agent API. The proposed search spec would freeze the objective, baseline, model request and resolved version, public evidence, evaluator hash, hard gates, scoring rule, budgets, write scope, rounds, and review-only integration policy before admission; it is a design, not shipped code. The current JS starter supports structured generation and read-only review with `strategy: "search"`. Runtime-owned command gates, hidden evaluation, benchmark scoring, and clean-baseline replay are an explicit host seam still to wire; a candidate's self-verdict must never be promoted into evaluator truth. See [Workflow Experimental Search](WORKFLOW_EXPERIMENTAL_SEARCH.md).