---
title: "Workflows — from a Draft to a result"
description: "Create, validate, publish, and run a Workflow; read typed results, answer decisions, and recover failed Steps."
---

# Workflows — from a Draft to a result

Use a Workflow when the same sequence of agent work, human decisions, and typed handoffs will run more than once. Dependencies determine when Steps become ready. For a one-off request, start a normal [Session](/en/docs/sessions). To start a published Workflow at a future time or on a document condition, use a [Trigger](/en/docs/triggers).

## Before you begin

Choose an active [Project](/en/docs/projects), a collaborator or admin who can change it, and active collaborator/admin agents for the Session Steps. The execution owner's machine needs an available aachat runtime and the selected coding-agent Runtime configured. A valid definition alone does not make an offline runtime available. See [Setup](/en/docs/setup) and [Environment](/en/docs/environment).

The `chat` commands below are for an agent inside a running aachat Session with access to the Project. Ask that agent to author the Workflow if you are using WebUI. They are not commands for an unauthenticated terminal. Replace `<team>/<project>`, agent names, and returned IDs with your actual values. A Project viewer can read content but cannot author or start work; mutations also recheck current Project and execution authority.

## What is saved

| Term | Meaning |
|---|---|
| Draft | Editable files in `aachat/projects/<team>/<project>/workflows/<slug>/`, synchronized as Project state. |
| Revision | An immutable snapshot of the definition and supporting files. |
| Published | The pointer to the Revision used for new published Runs. Publication is separate from a Draft Run. |
| Run | One execution with fixed Revision and input. Later Draft edits and publication do not change it. |
| Attempt | One execution attempt of a Step. Retrying creates another Attempt in the same Run. |

An Attempt has the exact read-only Revision Bundle at `$AA_WORKFLOW_DIR`. Read it when doing the assigned Step; never edit it or copy it over the Draft. There is no `_published` directory. Discover published definitions through `list` and `show`.

## Create a small, complete Workflow

This example takes a reader name and returns a greeting. First inspect the available agents and published names, then create the Draft identity:

```bash
chat project members <team>/<project> --runtime-profiles
chat workflow list --project <team>/<project>
chat workflow init greeting --project <team>/<project>
```

Use a new slug if `greeting` is already in use. In the generated Draft, replace `workflow.yaml` with the following and replace `writer.yourname` with the exact member name. Choose a Runtime supported by that agent. `session.runtime` and its `kind` are required; the example explicitly uses an empty configuration.

```yaml
schema_version: 1
name: greeting
description: Write a greeting for one named reader.
inputs:
  type: object
  additionalProperties: false
  properties:
    reader:
      type: string
      minLength: 1
  required: [reader]
outputs:
  greeting: "{{ steps.write.outputs.greeting }}"
steps:
  write:
    needs: []
    session:
      agent: writer.yourname
      runtime:
        kind: codex-acp
        config: {}
      prompt:
        file: prompts/write.md
    outputs:
      type: object
      additionalProperties: false
      properties:
        greeting:
          type: string
          minLength: 1
      required: [greeting]
```

Create `prompts/write.md` within the same Draft:

```text
Write one welcoming sentence for {{ inputs.reader }}.
Return the sentence in the required greeting output.

{{ aachat.step_completion }}
```

The final placeholder inserts the completion instructions for this Attempt. It must appear exactly once at the end of a Session prompt. Remove unused starter files if they are no longer needed. The schema is a closed object: `reader` is required, and undeclared input keys are rejected. The Step must return `greeting`, and the top-level mapping makes that value available as the Run result.

## Validate, try, and publish

From the workspace root, validate in the Draft's Project directory, then optionally run the Draft:

```bash
(cd aachat/projects/<team>/<project> && chat workflow validate workflows/greeting)
chat workflow run --draft aachat/projects/<team>/<project>/workflows/greeting --stdin <<'JSON'
{"reader":"Alex"}
JSON
```

Validation contacts the server to check Project agents and any published child Revision pins; it is not an offline YAML check. A validation error identifies the field or file to correct. A Draft Run starts real agent work, so use suitable input and inspect its returned Run ID with `status` before moving on. A successful Draft Run is useful evidence, but is not required for publication.

Publish the Draft and inspect its input/output contract before starting the published version:

```bash
chat workflow publish aachat/projects/<team>/<project>/workflows/greeting
chat workflow show greeting --project <team>/<project>
```

Alongside the published Revision's inputs and outputs, inspect each entry in `steps`: `key`, `kind`, and `needs`. Check `agent_name` and `runtime_kind` for Session Steps and `workflow_slug` for child Workflows. The list follows dependency order; it does not guarantee serial execution of independent Steps. Show does not start a Run or expose prompt bodies, secrets, or every internal Step of a child Workflow. Start the published version after this check.

```bash
chat workflow run greeting --project <team>/<project> --stdin --wait <<'JSON'
{"reader":"Alex"}
JSON
```

Publication snapshots the files and changes the Published pointer; it does not modify the Draft files or `draft_version`. `run --wait` starts the Run and registers a durable notification for the source Session. The command returns immediately. After acceptance, the source agent ends its turn and reads the result on continuation instead of polling.

```bash
chat workflow status <run-id> --project <team>/<project>
chat workflow runs greeting --project <team>/<project>
```

A succeeded example Run returns `outputs` such as `{"greeting":"Welcome, Alex!"}`. The wording is generated, not guaranteed to be that exact sentence. Before success, `outputs` is absent. Without top-level output declarations, a succeeded Run returns `{}`. Check both the Run state and the actual result or linked artifact.

## Complete a Session Step

The agent executing the Step follows the generated completion block. In the example Attempt, after producing the actual greeting, it submits:

```bash
chat workflow complete --stdin <<'JSON'
{"outcome":"succeeded","outputs":{"greeting":"Welcome, Alex!"}}
JSON
```

If it cannot finish, it submits the cause and next action:

```bash
chat workflow complete --stdin <<'JSON'
{"outcome":"failed","error":"Required source is unavailable; restore access before retrying."}
JSON
```

Only a JSON response containing `"accepted": true` confirms acceptance of the completion intent. A timeout, terminal echo, or final chat response does not. After a transport error, retry the **exact same JSON payload** using the same non-interactive form. Once accepted, start no further work and finish the current response normally. Acceptance is not yet proof that the whole Run succeeded. Do not use `chat session finish` as a substitute.

If a Session Step needs an unplanned human decision, create a Project Ask and register `chat wait --all --ask <ask-id> --project <team>/<project>`, then end the turn without completing the Step. On continuation, read `chat ask show <team>/<project> <ask-id>`, resolve the answer or cancellation, and finish the Step. A Workflow Attempt can wait on same-Project Asks, not on Session or Run targets.

## Human decisions and reusable children

A planned Decision Step creates a Project Ask for the human who started the Run, or the human owner of the agent that started it. There is no assignee field in the Decision definition. Answer it in the Project's Ask interface. The Run uses a pinned answer revision as `{"answer": "..."}`; a later edit to the Ask does not rewrite that output. `waiting_for_decision` is an intermediate state, not a failure. This is a business decision, distinct from a coding Runtime asking permission to use a tool. An answer supplies data to downstream Steps. Branching requires explicit `when` conditions as below; an answer does not grant deployment authority.

A Workflow Step can call a published Workflow in the same Project. Its child Revision is pinned, and only one level of composition is supported. See the complete [definition reference](/en/docs/workflow-reference) for Decision and child examples, typed bindings, and limits.

## Choose a branch and collect its result

Use this example only after confirming the compatible API/server, WebUI, and CLI described in the [version prerequisite and definition rules](/en/docs/workflow-reference). Availability in your environment is not established by this guide or by a successful syntax check. If validation rejects `when`/`join`, or the Run view lacks condition/skip details, stop and check the deployed version with your administrator.

This Workflow asks which note to prepare, runs one branch, and exports the join's summary. It does not publish or deploy anything. As with `greeting`, inspect Project members and Runtime profiles, then run `chat workflow init choose-note --project <team>/<project>` (choose another slug if occupied). Replace the Draft's `workflow.yaml` with the following. Replace all three `writer.yourname` entries and Runtime settings with the actual supported member/profile.

```yaml
schema_version: 1
name: choose-note
description: Choose a note, then summarize the branch result.
inputs:
  type: object
  additionalProperties: false
  properties: {}
outputs:
  summary: "{{ steps.summary.outputs.text }}"
steps:
  choose:
    needs: []
    decision:
      question: Which note should be prepared?
      body: { file: prompts/choose.md }
      options: [Draft, Hold]
  draft:
    needs: [choose]
    when:
      value: "{{ steps.choose.outputs.answer }}"
      equals: Draft
    session:
      agent: writer.yourname
      runtime: { kind: codex-acp, config: {} }
      prompt: { file: prompts/draft.md }
    outputs:
      type: object
      additionalProperties: false
      properties:
        text: { type: string, minLength: 1 }
      required: [text]
  interpret:
    needs: [choose]
    when:
      value: "{{ steps.choose.outputs.answer }}"
      otherwise: true
    session:
      agent: writer.yourname
      runtime: { kind: codex-acp, config: {} }
      prompt: { file: prompts/interpret.md }
    outputs:
      type: object
      additionalProperties: false
      properties:
        text: { type: string, minLength: 1 }
      required: [text]
  summary:
    needs: [draft, interpret]
    join: true
    session:
      agent: writer.yourname
      runtime: { kind: codex-acp, config: {} }
      prompt: { file: prompts/summary.md }
    outputs:
      type: object
      additionalProperties: false
      properties:
        text: { type: string, minLength: 1 }
      required: [text]
```

Create `prompts/choose.md` in the same Draft:

```text
Choose Draft for a short planning note, or Hold to record that work is on hold.
You may also describe your conditions in your own words.
```

Create `prompts/draft.md` in the same Draft:

```text
Return a short planning note in text. Do not publish or deploy anything.

{{ aachat.step_completion }}
```

Create `prompts/interpret.md` in the same Draft:

```text
The human answered: {{ steps.choose.outputs.answer }}
Return a note in text preserving the answer and any conditions. Do not infer
approval or carry out the requested work. If another decision is necessary,
use a Project Ask and wait before completing this Step.

{{ aachat.step_completion }}
```

Create `prompts/summary.md` in the same Draft:

```text
Read the attached Workflow dependency results. Return a concise summary in
text using the succeeded outputs and explaining which branch was skipped.
Preserve any human conditions; do not treat a skipped branch as completed work.
If all dependencies were skipped, report that no branch work was performed.

{{ aachat.step_completion }}
```

From the Draft's Project directory, validate, then optionally try the Draft with the empty input object:

```bash
chat workflow validate workflows/choose-note
chat workflow run --draft workflows/choose-note --stdin <<'JSON'
{}
JSON
```

Open the returned Run in the Project's Workflows and answer its Ask. Selecting exactly `Draft` runs `draft` and skips `interpret`; selecting `Hold` or supplying free text runs `interpret` and skips `draft`. `Draft, after review` is free text, not approval for the `Draft` branch. A later Ask edit does not change the accepted answer for this Run. If `interpret` needs another decision, follow the Ask/wait procedure above before completing it.

After the selected branch succeeds, `summary` reads its accepted output and the other branch's skip reason. Each running Session submits its own schema-valid completion using the generated instructions. The summary Attempt may, for example, submit `{"outcome":"succeeded","outputs":{"text":"Work is on hold; the draft branch was skipped."}}` only if that describes the actual result. `accepted: true` acknowledges that Step's completion, not the whole Run's success.

Use `chat workflow status <run-id> --project <team>/<project>` to verify the Run is succeeded and `outputs.summary` exists and contains the expected result. Missing outputs are not an empty successful deliverable. Inspect the active Step, completion acceptance, or `output_error` before passing anything onward. When a child is involved, inspect the parent too: child success alone does not prove the parent accepted its output. After the trial, publish and run the published version using the earlier procedure with `choose-note` and `{}`.

This Decision example always selects one of the two branches after an answer. With boolean/enum conditions, an uncovered value can skip every branch: a join still runs and must report that no branch work occurred. An ordinary downstream Step would skip instead. Do not count skipped work as performed or retry it to force a branch; correct the Draft or input and use a new Run if the intended choice was different.

## Read the Run and recover

In WebUI, open the Project's Workflows, select the Workflow, and open a Run. The dependency graph and Step details let you inspect inputs, outputs, Attempts, source Session, linked Ask, and child Run. Open the relevant Attempt or child before deciding how to recover. CLI `status` includes `available_actions`; these reflect current state and authority, and are rechecked when you act.

| Observed state or problem | Next action |
|---|---|
| Work is running or waiting for a decision | Inspect the active Step or answer the Ask; do not launch a duplicate to obtain a result. |
| `skipped` with `condition_not_matched` | Compare the recorded reference and actual value with the condition. No Attempt, Session, Ask, signal, or child Run was created for this Step; it has no retry action. |
| `skipped` with `dependency_skipped` | Inspect the named upstream Step's skip reason. Ordinary downstream work also skips; use an explicit join when it must collect the remaining results. |
| A join has not started or has failed | Check every dependency. Failed, cancelled, blocked, or unresolved work is not a successful skip. Repair the cause and retry only when offered; a failed join can be retried with skipped dependencies when listed in `available_actions.retry_steps`. |
| `attention_required` | Inspect the failed Step and cause. It is settled, not succeeded. |
| Retry is offered | Run `chat workflow retry <run-id> --step <step-key> --project <team>/<project>` only for a key in `available_actions.retry_steps`. |
| A Workflow Step failed in its child | Open the child Run and retry the failed inner Step when offered. |
| Cancellation is needed | Use `chat workflow cancel <run-id> --project <team>/<project>` only when `available_actions.cancel` is true. Cancel a child through its parent. |
| Definition or immutable input is wrong | Correct and validate the Draft, publish if needed, and start a new Run. Retrying does not adopt Draft edits. |
| Decision Ask was cancelled or its assignee is unavailable | Repair the cause and retry when offered; a retry creates a new Ask. An oversized rendered Decision has no retry action. |
| Runtime cannot start | Restore the execution owner's runtime/configuration, then follow the available retry action. Ordinary Session resume is not supported for Workflow Attempts. |

For several root Runs whose results must be combined, start them without individual `--wait` flags and register one explicit wait:

```bash
chat wait --all --workflow <first-run-id> --workflow <second-run-id> --project <team>/<project>
```

Child Runs belong to their parent and cannot be wait targets. When continued, inspect each result using the supplied `Read:` command. Settled results can include attention or cancellation; waiting is not an assertion of success.

To pass a successful result to another Workflow, inspect both schemas, then select only the keys accepted by the next closed input object. For a next Workflow named `save-greeting` that declares `greeting`:

```bash
chat workflow status <run-id> --project <team>/<project> | jq '.outputs | {greeting}' | chat workflow run save-greeting --project <team>/<project> --stdin
```

Run this only after confirming success and the presence of `outputs`; do not pipe a failed status into a new Run. Store large deliverables in [Shared Documents](/en/docs/shared-documents) or [Media](/en/docs/media) and return a bounded reference. Completion and Run outputs are limited to 128 KiB and JSON depth 32. `workflow_completion_limit_exceeded` requires smaller outputs and a corrected new Run; a historical succeeded Run can report `output_error` instead of oversized outputs.

## Improve the next Revision

When a prompt, input, output schema, or agent assignment caused a concrete problem, leave Attempt feedback. In the current Attempt use `chat workflow feedback --stdin`; outside it use the Run ID and Step, with optional `--attempt N`:

```bash
chat workflow feedback <run-id> --step write --project <team>/<project> --stdin <<'TEXT'
The prompt did not specify the reader's language. Add a language input before the next revision.
TEXT
```

Feedback is plain text, up to 8,192 bytes. Do not invent scores. Read it in Run status or the Run screen and use the improvement action to prepare Draft changes. Feedback alone changes no prompt. Edit the normal Draft, validate it, optionally try it, and publish the next Revision. Keep Run logs in Run history.

In WebUI, choose **Ask an agent to improve** to prepare and focus a new Session's launch draft. If another draft is already entered, review the replacement confirmation. Check the Project, Workflow slug, and normal Draft path in the prepared prompt, select the Agent and Runtime, then send to start the improvement Session. Preparing the draft is not Session-start acceptance. Open the started Session to follow its edits; an existing Run still uses its pinned Revision.
