
PART 2Organize and carry out work2.6
Workflow definition reference
Bundle paths, Step kinds, Runtime selection, typed schemas, conditions and joins, prompt references, child Workflows, commands and responses, and execution limits.
Use this reference while editing workflow.yaml. The Workflow guide provides a complete Session Step example, the create-to-result procedure, and the WebUI operations. Definitions use YAML with schema_version: 2 or 3; unsupported fields are rejected. YAML anchors, aliases, merge keys, and tags are not supported. Validate the actual Draft before publishing or running it.
Ordered Session messages
session.prompt.file is the initial task. Add follow_ups in the order they should run in the same Session:
session:
agent: writer.yourname
prompt: { file: prompts/title.md }
follow_ups:
- { file: prompts/article.md }For example, title.md asks for a title; article.md asks the agent to write the article using that title. Each file contains only its current task. The conversation carries earlier work; there are no per-turn outputs or new reference variables. The Workflow sends completion instructions separately after the final task. All task files must be below prompts/, and all references are validated before launch. Omitted or empty follow_ups means one task; null is rejected. At most 128 task messages are allowed, including the initial message and repeated uses of the same file. Each task and completion message is limited to 100,000 UTF-8 bytes; the full rendered plan is limited to 1 MiB. A later invalid or oversized message prevents the Attempt from starting.
In the Draft, select Initial message, Follow-up 1, and subsequent messages to edit their files. Add, remove, or reorder entries in workflow.yaml. Attempt details also show Completion report; unsent messages are the accepted plan, not delivered conversation history. The accepted text remains fixed even if the Draft changes. A missing execution-state record does not imply that a message succeeded.
New Draft validation, publication, and Draft Runs accept schema 2 or 3. Change an existing Draft to schema_version: 2, remove completion placeholders, then validate and publish. Retired schema 1 Revisions and Runs are read-only history. They cannot start, retry, or cancel work. Start a new Run from the migrated published Workflow, whose definition may have changed. Historical prompts and results are preserved.
A Run is also read-only when its composed parent/child operation includes a retired Revision, even if its own Revision uses schema 2. The server reports this as execution_retired; start a new Run from the migrated published Workflow.
Definition fields
| Field | Contract |
|---|---|
schema_version | Required integer 2 or 3; for_each requires 3. |
name | Required non-empty display name. The Draft directory slug identifies the Workflow. |
description | Required non-empty description string. |
inputs | Required closed object schema for Run input. Use properties: {} when there is no input. |
steps | Required map of 1–64 Steps. Keys match ^[a-z][a-z0-9_-]{0,63}$. |
outputs | Optional map from exported output names to complete Step output references. Not a JSON Schema. When omitted, nothing is exported. |
steps.<key>.needs | Required list of direct dependencies. Roots use []. Up to 32 per Step and 256 edges in total. Unknown or duplicate dependencies and cycles are rejected. |
steps.<key>.when | Optional condition on a direct dependency's output. Allowed on every Step kind. Give value and exactly one of equals or otherwise: true. |
steps.<key>.join | Optional boolean, default false. true is allowed only on a Session with non-empty needs and no when. |
steps.<key>.session | One Step kind. agent and prompt.file are required; runtime and ordered follow_ups are optional. A Session Step also declares an outputs schema. |
steps.<key>.decision | One Step kind. A static non-empty question and body.file are required; options is optional. Do not declare Step outputs. |
steps.<key>.workflow | One Step kind. The published child's slug is required; inputs binds the child's fields. Do not declare Step outputs. |
needs expresses execution dependencies, not conditions. A Step cannot combine Session, Decision, and child Workflow. Files in scripts/ are supporting material agents can use. Placing them there does not create a shell Step or run commands automatically.
Session Step Runtime
When session.runtime is omitted, the Step starts with the agent's default Runtime and settings (mode, model, effort), resolved when each Attempt starts. The agent's human owner saves that default from the Runtime settings in a Project composer with Save as agent default (Agents). Writing a model or effort into the Step prompt does not change settings.
Set runtime only on a Step that needs different settings.
session:
agent: writer.yourname
runtime:
kind: codex-acp
config: {}
prompt:
file: prompts/write.mdWhen you write runtime, kind is required. config maps option IDs to strings or booleans and may be omitted or {}. Strings are ACP option value IDs, not arbitrary display names. Numbers are not accepted. Find available kinds and options with chat project members <team>/<project> --runtime-profiles. Execution also needs the agent's membership, the owner's availability, and a compatible Runtime.
Conditions, skipped Steps, and joins
needs still expresses dependencies. To choose work by a result, add an explicit when. when.value names one complete reference {{ steps.<direct-need>.outputs.<path> }} to a required boolean or string enum output of a direct dependency. The only free-text string exception is a Decision's answer. equals is an exact match of type and value: boolean true differs from string "true", and strings are not trimmed or interpreted. For enums, only declared values can be compared. Run inputs, optional output paths, arbitrary expressions, type conversion, loops, and error-recovery branches are not supported. See also the complete branch example.
otherwise: true applies only when the value matches none of the equals comparisons that use the same normalized reference anywhere in the Revision. That reference needs at least one equals, and there may be at most one otherwise. When several Steps match the same value, all of them run. With booleans and enums you can leave uncovered values that run no branch.
For Decision answers, equals must match a published option. Branching on an answer requires an otherwise Session whose only needs is that Decision and whose prompt references its answer directly. That Session receives free-text answers and uncovered options. It preserves the human's conditions and authority and uses an Ask when a further decision is needed. A matching answer grants no new authority, and editing the Ask later does not change the pinned answer revision.
A nonmatching condition derives skipped, and ordinary Steps depending on a skipped Step also skip. The reason is recorded in skip_reason as condition_not_matched (with the reference and actual value) or dependency_skipped (with the earlier Step). Skipped Steps create no Attempt, Session, Ask, or child Run and cannot be retried. Failed, cancelled, blocked, and unresolved dependencies are never treated as successful skips.
A join: true Session waits until every direct dependency has succeeded or skipped, and runs even when every branch skipped. It does not run through a failure or cancellation. Its prompt must not reference steps.*.outputs.* directly. Instead it reads # Workflow dependency results, which the server appends once at the end of the initial task message: a canonical JSON array in Step-key order, with each dependency's step, state, and either accepted outputs or the skip reason. The attachment counts toward the 100,000-byte prompt limit and is preserved across retry and past-prompt reconstruction.
Run outputs accept a single Step reference or a nonempty array of references in priority order. Only skipped candidates are skipped; the first succeeded Step supplies the value. Failed, cancelled, unfinished, missing or invalid selected outputs never fall back to an older candidate. A single reference and the last array candidate must target a Step that cannot be skipped, including skip propagation through ordinary dependencies. Every candidate must reference a required output path with exactly the same schema, including descriptions and array order. Selection uses the fixed Revision and latest Attempt, and outputs become available only after the Run succeeds and all executed Steps have accepted successful completions.
outputs:
verdict:
- '{{ steps.recheck.outputs.verdict }}'
- '{{ steps.check.outputs.verdict }}'If check passes and recheck skips, check supplies the result. After recheck executes, its result is returned whether its verdict is pass or fail. Both verdict paths must have the same required schema, and check must not be skippable. Candidate arrays apply only to top-level Run outputs, not conditions, child Workflow input bindings or prompt references.
A Run can succeed when every Step succeeded or skipped, but Run outputs require accepted successful completions from every executed Step. A child Run's success alone does not prove the parent Step accepted its output. Check the parent Run and the actual outputs before using the result. The WebUI Run detail and Draft diagram show each Step's condition, join, and skip reason.
Bundle files and paths
The root you edit is aachat/teams/<team>/projects/<project>/workflows/<slug>/ for a Project Workflow and aachat/teams/<team>/workflows/<slug>/ for a Team Workflow. A Bundle contains the required workflow.yaml, an optional root README.md, and text files below prompts/ or scripts/. A Session's prompt.file and a Decision's body.file point to existing files below prompts/ in the Bundle.
Use case-sensitive relative paths and ordinary UTF-8 text. Absolute paths, backslashes, empty or dot path segments, dotfiles, _published, duplicate paths, and U+0000 are rejected. If any file contains unresolved merge conflict markers, validate, publish, and run --draft stop before sending. A Bundle is not a snapshot of the whole repository, so keep unrelated files outside it. A running Attempt reads the pinned read-only Bundle at $AA_WORKFLOW_DIR.
A Step Session works only in the Workflow's own Project. When a prompt names another Project's aachat/teams/<team>/projects/<project>/… path, validate, publish, and run --draft return the non-blocking warning project_path_not_available_here. In a Team Bundle, every Project path triggers this warning. Keep the needed text in the Bundle and read it from $AA_WORKFLOW_DIR, or pass it as Run input.
Validation, publication, and Draft Runs return a non-blocking bundle_reference_missing warning when an initial Session prompt or follow-up names a literal $AA_WORKFLOW_DIR/<path> missing from the Bundle. The warning names the task file and missing path; add the file or correct the reference. Project and Team Bundles use the same check. Variable, placeholder, glob, and space-containing paths are not checked, nor are references inside scripts or other supporting files. Existing published Revisions and Runs are unchanged.
Supported input and output schemas
The root of Run inputs and of Session Step outputs is an object. Every object, including nested ones, needs additionalProperties: false and properties. Use properties: {} when there are no fields. List required fields in required. Optional fields may be omitted, but do not pass null in place of absence.
| Structure | Values and keywords |
|---|---|
| Types | object, string, number, integer, boolean, array |
| object | properties, required, additionalProperties |
| array | items, minItems, maxItems |
| string | minLength, maxLength, pattern, format |
| number | minimum, maximum |
| Common | type, enum, description |
format supports only uri. Arbitrary JSON Schema is not available: $ref, allOf, oneOf, anyOf, nullable, and type-union arrays are unsupported. Define small schemas inline. A document path can be an ordinary string. format: uri checks URI shape; it does not guarantee that the artifact exists or that access is appropriate. The WebUI Run dialog shows form fields only when every top-level input is a string, number, integer, or boolean (including enums); if any input is an object or array, the whole input is entered as one JSON object.
Prompt references and exported values
Session prompts can embed {{ inputs.reader }} or {{ steps.write.outputs.greeting }} in text. To reference a Step output, that Step must be a direct dependency listed in needs. Depending on an intermediate Step does not let you reference all its ancestors. Referenced schema paths must exist. Do not put {{ aachat.step_completion }} in task files. The Workflow sends the output schema and completion instructions after all task messages, in a separate turn.
Decision bodies can also reference inputs and direct dependencies, but must not contain the completion marker. The question and options are static. The server creates the Ask and accepts the human answer, so agents do not send a Decision completion payload.
In top-level outputs, name a Step output path that is required in its source schema, for example greeting: "{{ steps.write.outputs.greeting }}". Run input cannot be referenced directly. The server derives a closed output schema from the mapping. When the Run is succeeded, these values appear as the Run's outputs in status and runs. Before success there is no outputs key; without declarations it is {}. A child's successful output is written once to the parent Step before downstream work starts.
Complete child and Decision example
First publish the guide's greeting in the same Project, or in the Team so that team:greeting resolves. Then create a Draft with chat workflow init approve-greeting --project <team>/<project> and write this workflow.yaml.
schema_version: 2
name: approve-greeting
description: Ask a human to review a generated greeting.
inputs:
type: object
additionalProperties: false
properties:
reader:
type: string
minLength: 1
required: [reader]
outputs:
greeting: "{{ steps.compose.outputs.greeting }}"
answer: "{{ steps.review.outputs.answer }}"
steps:
compose:
needs: []
workflow:
slug: greeting
inputs:
reader: "{{ inputs.reader }}"
review:
needs: [compose]
decision:
question: Is this greeting ready to use?
body:
file: prompts/review.md
options: [Use it, Revise it]Create prompts/review.md.
Please review this greeting for {{ inputs.reader }}:
{{ steps.compose.outputs.greeting }}
Choose whether it is ready, or explain the revision needed.Follow the guide's procedure with slug approve-greeting: validate, optionally try a Draft Run with {"reader":"Alex"}, and publish. The child creates greeting, then an Ask goes to the human who started the Run (or that agent's human owner when an agent started it). There is no field to choose the Decision's recipient. A successful Run exports greeting and answer. Choosing Revise it still completes the Decision; this definition has no revision Step. To interpret the answer, add a later Session Step; to choose a branch, define when and the required otherwise Session.
For Decision options, omit them or leave them empty for free text, or give 2–5 distinct options. Do not add outputs to a Decision; an object schema with a required string answer is derived automatically. The server pins the adopted answer revision, so editing the Ask later does not change this Run.
Each value in a child's workflow.inputs is either a literal JSON value or one complete reference to an input or a direct dependency. Do not interpolate text around a reference. Bind every required child input and pass only fields its schema accepts. Even for an optional target input, the source path must be required. The source schema, including its constraints, must be a subset of the target contract; matching the basic type alone is not enough. For example, you can write the literal reader: Alex instead of reader: "{{ inputs.reader }}".
The server pins the child's current published Revision when it validates, publishes, or accepts a Run. The child must be a published Workflow with the same owner, or a Team published Workflow referenced as team:<slug>, and a pinned child may contain only Session and Decision Steps. Nested child Workflows are rejected. An unpublished child fails validation. Publishing a new child later does not change existing parent Revisions or Runs; to adopt it, revalidate and publish the parent Draft. Recover inner Steps by opening the failed child Run, and cancel a child from its parent Run.
Command reference
These chat workflow commands are for agents inside a Session. Replace identifiers with real values. Square brackets mark optional arguments and are not typed. Project-scoped operations accept --project <team>/<project> (a bare Project slug also works); without it, the cwd's Project is used. validate, publish, and run --draft derive the owner (Project or Team) from the Bundle path, so you need not change cwd. run --draft of a Team Bundle takes the executing Project from --project or the cwd. complete targets the current Attempt and does not accept a Run ID.
| Operation | Syntax |
|---|---|
| Create a Draft | chat workflow init <slug> [--project <team>/<project>] [--client-nonce <uuid>]; for a Team, chat workflow init team:<slug> |
| Validate | chat workflow validate <bundle-path> (aachat/teams/<team>/projects/<project>/workflows/<slug> or aachat/teams/<team>/workflows/<slug>) |
| Publish | chat workflow publish <bundle-path> |
| List published | chat workflow list [--project <team>/<project>] [--last 50] [--before <cursor>] [--archived] |
| Published contract and Steps | chat workflow show <slug> [--project <team>/<project>] |
| History | chat workflow runs [<slug>] [--project <team>/<project>] [--last 4] [--before <cursor>] [--state <state>] |
| Draft Run | chat workflow run --draft <bundle-path> (--stdin | --input-file <path>) [--client-nonce <id>] [--wait] |
| Published Run | chat workflow run <slug> [--project <team>/<project>] (--stdin | --input-file <path>) [--client-nonce <id>] [--wait] |
| Run detail | chat workflow status <run-id> [--project <team>/<project>] [--detail] |
| Compare two Runs | chat workflow compare <left-run-id> <right-run-id> [--project <team>/<project>] [--step <key>] [--detail] |
| Attempt history | chat workflow attempts <run-id> --step <key> [--project <team>/<project>] [--last 50] [--before <cursor>] |
| Attempt detail | chat workflow attempt <attempt-id> [--project <team>/<project>] |
| Cancel | chat workflow cancel <run-id> [--project <team>/<project>] [--reason <reason>] |
| Retry | chat workflow retry <run-id> --step <key> [--project <team>/<project>] [--client-nonce <uuid>] |
| Complete an Attempt | chat workflow complete --stdin |
| Attempt feedback | chat workflow feedback --stdin (current Attempt), chat workflow feedback <run-id> --step <key> [--attempt N] [--project <team>/<project>] --stdin |
| Archive / restore | chat workflow archive|restore <slug> [--project <team>/<project>] [--preview]; for a Team, team:<slug> |
Notes:
runtakes exactly one of--stdinor--input-file <path>, and the input is a JSON object. Page sizes forlist,runs, andattemptsare 1–100; defaults are 50 forlistandattemptsand 4 forruns. For the next page, use the command in the response'snext_pageas is.- Project-wide
runsshows root Runs; with a Workflow slug it shows that Workflow's root and child Runs with parent identity.--stateis one ofrunning,waiting_for_decision,cancelling,attention_required,succeeded, orcancelled. Each item includesinputand, after success,outputs; free-text strings become 100-character previews markedtruncated: true. Read full values with thestatuscommand indetail_read. listreturns published, unarchived Workflows of the Project and its Team. Team Workflows haveslugset toteam:<slug>.--archivedreturns archived Workflows, including unpublished Drafts.statusreturns each Step's summary,feedback_count, andavailable_actions.--detailreturns the complete record, including the definition, inputs, prompts, launch facts, and feedback bodies.- When
runomits--client-nonce, the request identity is derived from the Session, Project, Workflow, and input, so rerunning the same command returns the same Run (created: false). A new UUID starts another Run with the same input. Forinitandretry, reuse--client-nonceonly when retrying exactly the same request. Never reuse a nonce with changed input. --waitis a durable wait registration and does not block the CLI. It cannot be used inside a Workflow Attempt Session.- For
archiveandrestore,--previewshows the effect; passing the preview's values as--expected-lifecycle-version <N> --confirmation-token <token>applies exactly what you previewed. - The
aachat workflowcommands operate Workflows from an ordinary terminal outside a Session. See CLI for their syntax.
Reading responses
Output uses the shared {"ok": true, "data": {...}} shape, or {"ok": false, "error": ...} on failure. When extracting values with jq, start from .data.
validate:valid,warnings,workflow_steps,content_hash. Whenvalidis false, the error isworkflow_validation_failed.init:bundle_ready, the Bundle path inbundle.path, andclient_nonce. If the Bundle is not ready locally within one minute, the error isworkflow_bundle_not_readywith a recovery command using the same nonce.run:accepted,created,run,available_actions,detail_read, andstartups[]for the Step Sessions it started.startups[].startup.stateisstarted,failed,preparing, orruntime_offline; the server waits up to 40 seconds before returning (guide). If a later stage ofrun --wait(the wait registration) fails,ok: falsestill keeps the confirmed Run ID, nonce, and failed stage indata. Check the existing Run before creating another.complete: onlyaccepted: truemeans that Attempt's completion intent was accepted. It does not mean the whole Run succeeded.archive/restore:triggers(paused Triggers), existing Runs that continue, andtriggers_resumed: false.
chat workflow status <run-id> --detail returns the latest Attempt's saved Session token_usage for each Step/item and its launch-time model settings in observed_launch. Read earlier Attempts with attempts (follow next_page) and one Attempt with attempt. The five counters are input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, and turn_count. Ordinary status keeps usage but omits observed_launch.
These are Session totals at read time, not Step or Run totals: they include only saved completed turns, can grow after an Attempt ends, and do not add child Runs or separate Sessions. Omission means no Session value is available; zero is a saved value, not proof of zero consumption or complete measurement. Runtime-internal subagent coverage is unverified. Launch settings do not establish the model used for every turn: match the model option's id to applied_config_options[].config_id; proof: observed means observed, ack means acknowledged only, and an option without an application record is a launch snapshot. Keep aliases as returned and do not substitute requested settings for missing observations.
Limits and how to recover
| Target | Limit | When exceeded |
|---|---|---|
| One Bundle file | 1 MiB | Shorten or split supporting text. |
| Whole Bundle | 4 MiB, 128 files | Remove unrelated assets. |
| Relative path | 256 UTF-8 bytes | Shorten the path. |
| Dependency graph | 64 Steps, 32 needs per Step, 256 edges total | Shrink the graph while keeping one-level composition. |
| Rendered Session prompt | 100,000 UTF-8 bytes per task or completion message; 1 MiB for the full plan | Shorten the prompt, input, or upstream text. |
| Decision question / each option | Non-empty, at most 2,000 / 200 bytes | Shorten fixed text; use 2–5 distinct options or free text. |
| Rendered Decision body | 100,000 bytes | Fix the immutable input or definition and use a new Run. |
| Run input | 128 KiB, JSON depth 32 | Store large content as a Project resource and pass a reference; flatten deep nesting. |
| Completion JSON / Run outputs | 128 KiB, JSON depth 32 | Return short artifact references. |
Failed completion error | Non-empty, at most 4 KiB | Keep the cause and next action, shorten the diagnostic, and resend. |
| Attempt feedback | Non-empty after trimming, at most 8,192 bytes | Describe the problem concretely and briefly. |
KiB and MiB are binary units. Bytes are not characters. These are Workflow limits, separate from Database SQL limits and Session follow-up schedule constraints. Passing schema validation does not guarantee that a Runtime can execute or that results are correct. For retry, cancellation, and completion acceptance by state, see the Run recovery guide; for scheduled start failures, see the Trigger guide.
Session Step starting branch
Optional session.working_branch selects a remote branch in the execution Project's workspace repository. Use a literal string, a required string Run input ("{{ inputs.start_branch }}"), or a required string output of a direct needs dependency. Every parent object on the reference path must also be required. Child outputs are checked against the pinned Revision's schema. A join: true Step accepts only literals or Run inputs.
Omission keeps the Project/Team default; it never inherits the previous Step's override. An explicit empty string (including whitespace after trimming) selects the repository default branch. Null, non-string values, partial expressions, origin/..., refs/..., and HEAD are rejected. Resolved values are not evaluated again as expressions.
Each Session starts on its own managed branch from the remote tip fetched at startup. This setting does not select a push/PR destination or automatically commit, push, or merge. The preceding Step must commit and push before returning its branch name. If that branch advances, the next starting point advances too. Retry fetches the current tip of the same branch; resuming or following up in the same Session preserves its existing workspace. A missing remote branch fails startup without falling back to the default.
Save these files as a Bundle, replace maker.owner with a Project Agent, and run chat workflow validate <Bundle path>. workflow.yaml:
schema_version: 2
name: Branch handoff
description: Continue from the previous Step's pushed files
inputs: {type: object, additionalProperties: false, properties: {}}
steps:
prepare:
needs: []
session:
agent: maker.owner
prompt: {file: prompts/prepare.md}
outputs:
type: object
additionalProperties: false
required: [branch]
properties:
branch: {type: string}
continue:
needs: [prepare]
session:
agent: maker.owner
working_branch: "{{ steps.prepare.outputs.branch }}"
prompt: {file: prompts/continue.md}
outputs: {type: object, additionalProperties: false, properties: {}}prompts/prepare.md:
Create the files in your workspace. Commit and push them to your Session branch,
then return that branch name in outputs.branch.prompts/continue.md:
Read the previous Step's files directly from the workspace and continue the work.Run a Step for each item
Add for_each: '{{ inputs.candidates }}' to a schema 3 session, decision, or workflow Step. Its source must be a required Run input or direct dependency output whose array schema declares items and maxItems at most 100. Prompts, follow-ups, decision bodies, and child input bindings can use {{ item }} or {{ item.name }}. when selects the entire Step.
Each input position runs independently, with up to four active items per Run Step; human waits and child Runs occupy slots. An empty list succeeds without Attempts. Define outputs for one item. After every item succeeds, downstream Steps receive steps.<key>.outputs.items in input order. Combined results must fit the 128 KiB/depth 32 completion envelope, including wrappers; return saved artifact references for large results.
Other items continue after one fails. History, retry, and external feedback require zero-based --item-index N. Retry only entries in available_actions.retry_items; successful items are not rerun. Use a child Workflow for multiple tasks within an item. Schema 2 parents cannot pin schema 3 children.