Build a workflow
Use this reference when building a flow in the visual editor, writing YAML or JSON, or asking an agent to configure a process. For choosing a starting preset, see Choose a workflow.
A definition describes the process. A preset gives a reusable definition an ID and visibility scope. A work stream resolves its selected source into a snapshot; editing the preset does not change existing work. A squad selects a default and alternatives, while its workspace, permissions, and integrations remain separate.
Start with a complete example
Section titled “Start with a complete example”This preset uses two participants with the same general agent type: one writes a brief and another reviews it independently. Review feedback returns to the writer, then comes back to the reviewer.
Pass this envelope to tau workflow create --stdin with a quoted heredoc (shown below), or use --content for short JSON/YAML. You may also save it as brief-review.yaml and use tau workflow create brief-review.yaml for reuse. In the visual editor’s complete-definition field, use only the contents beneath definition.
tau workflow create --stdin <<'TAU_PRESET'id: brief-reviewdescription: Produce a concise brief with an independent evidence review.scope: { kind: instance }definition: schemaVersion: 1 name: Brief with review participants: writer: agentTypeId: general session: reuse-within-stream reviewer: agentTypeId: general session: reuse-within-stream entry: draft routing: mode: guided returnTo: declared-only delegation: disabled limits: maxDelegations: 0 onLimit: request-owner-input steps: - id: draft kind: agent participant: writer instructions: Write the requested brief. Cite sources and distinguish evidence from assumptions. output: A concise brief with citations and unresolved questions.
outcomes: ready: { next: review } - id: review kind: agent participant: reviewer instructions: Check the brief against the request and sources. Approve it or give specific corrections. output: An evidence-backed verdict and any required corrections.
outcomes: approved: { next: finish } changes-requested: { returnTo: draft, afterRework: follow-graph } completion: mode: deliverableTAU_PRESETAttempts are unlimited by default. An optional maxStepAttempts: 3 permits the initial attempt and up to two further attempts per step. It is not three revisions after the first attempt. Workers start only when their steps become active; saving this preset starts no agents.
Definition fields
Section titled “Definition fields”| Field | Meaning |
|---|---|
schemaVersion | Must be 1. |
name | Display name, 1–200 characters. |
participants | Map of local participant IDs to agent configurations, at most 64 entries. |
entry | ID of the first step. |
steps | 1–128 step definitions. Handoffs determine execution order; array order alone does not connect steps. |
routing | Allowed returns, delegation, and adaptive changes. |
limits | Attempt, delegation, and concurrent-attempt bounds. |
completion | Delivery policy, separate from the last step’s outcome. |
subscriptions | Optional integration-event consumers, at most 32. |
Objects reject unknown fields. Local participant, step, outcome, and preset IDs are 1–100 characters, begin with a lowercase letter, and contain lowercase letters, digits, or hyphens. finish, constructor, and prototype are reserved. finish is allowed as a terminal next destination, not as a step ID. Agent-type IDs reference existing agent types and do not use this local-ID restriction.
Participants: who performs the work
Section titled “Participants: who performs the work”Each participant has these fields:
| Field | Values and behavior |
|---|---|
agentTypeId | Required existing agent-type ID. The agent type supplies agent instructions and configuration. Inspect available agent types rather than inventing an ID. |
tier | Optional enabled model-tier slug, such as deep or exhaustive. Overrides the agent type’s model settings for this participant. |
session | Required: reuse-within-stream or fresh-per-attempt. Reuse retains the participant’s session for compatible subsequent attempts; fresh creates a new session for each attempt. |
A participant is a named role in this flow. It directly selects an agent type and owns its model tier override and session policy; there is no separate profile to configure. Agent steps reference the participant ID.
A participant can keep agentTypeId: engineer while selecting tier: deep or tier: exhaustive. The editor lists enabled tiers configured on the instance. With no tier override, the agent type’s model settings apply. The workflow saves the tier choice and resolves its current model chain when each execution starts. Tier edits affect queued work and the next execution of a reused participant, while an execution already running keeps its resolved chain. Missing or disabled selected tiers block execution instead of silently falling back.
Two participant IDs can use the same agent type without becoming the same agent. Conversely, multiple sequential steps can use one participant when continuity is useful. Concurrent branches receive separate sessions even when they name the same participant. A restart or participant configuration change can also require a new session.
An agent-type reference does not grant extra permissions or integration access. The squad and caller must have the required access. See agent configuration and models and credentials.
Steps: instructions, results, and decisions
Section titled “Steps: instructions, results, and decisions”All steps have these fields:
| Field | Meaning |
|---|---|
id | Unique local step ID. Update all references when renaming it. |
name | Optional display name, 1–200 characters. Cards use this label; connections still use id. Omit it to display the ID. |
kind | agent (default when omitted) or human-approval. |
instructions | What to do and how to judge the result; nonempty, up to 64,000 characters. |
output | Expected deliverable or evidence; nonempty, up to 64,000 characters. This is guidance, not a machine-validated output schema. |
outcomes | 1–16 named outcomes, each with exactly one transition shape described below. |
The graph defines which work runs. There is no per-step required flag: to make a check mandatory, connect all successful paths through it. Finish waits for active branches and direct-return requests, then enforces the completion policy.
An agent step also requires participant. Use distinct participants for writer and reviewer when you want separate agents, even if they use the same agent type. There is no extra independence flag.
A human-approval step selects who can approve it:
reviewers(default): any human withworkstreams:reviewpermission for this squad. Admin and Operator roles include this permission; custom roles can grant it without allowing squad settings changes.assigned-reviewers: any one user assigned in the work stream’s Assigned reviewers field who also has review permission for this squad. With nobody assigned, any human with review permission can decide. Assigning reviewers on an existing work stream requiresworkstreams:update.
For example, insert this step before finish, and route the preceding step to approval:
id: approvalkind: human-approvalapprover: assigned-reviewersinstructions: Check that the brief answers your question and authorize delivery.output: Your decision and its rationale.
outcomes: approved: { next: finish } revise: { returnTo: draft, afterRework: follow-graph }A human gate waits for its designated decision. An ordinary question answer or generic unblock is not approval. You can place several gates at different points, independently of the final delivery policy.
Handoffs and rework
Section titled “Handoffs and rework”Each outcome uses exactly one of these shapes:
| Shape | Behavior |
|---|---|
{ next: step-id } | Continue to that step. |
{ next: finish } | End this path; active work, joins, direct-return requests, waits, and completion checks still apply. |
{ returnTo: earlier-step, afterRework: follow-graph-step } | Revise the target step, then follow its normal outgoing arrows. Use return-to-requester to send the result directly back to the requesting step instead. |
{ parallel: [security, qa], join: consolidate } | Start distinct branches and wait for them at their shared join. |
Outcome names such as approved are chosen by the author; they are not built-in verdicts with hidden semantics. Agents submit an outcome and evidence. Tau enforces the configured transition, not the truth of a free-text verdict.
returnTo identifies the step doing the corrections. afterRework defaults to follow-graph, which follows that step’s ordinary forward connections after revisions. With return-to-requester, completing the correction returns directly to the requesting step, even if its normal outcome would branch, run another review, or finish. For example, review.changes-requested: { returnTo: create-assets, afterRework: return-to-requester } revises assets and brings them directly back to Review. The source step determines the requester; there is no separate resume destination.
Use return transitions for revision loops. Normal forward edges cannot form cycles. Returns preserve prior review history; Tau does not invalidate every downstream review automatically. Instructions should tell reviewers which changes need another assessment.
During rework, an agent can complete with resume: true to return directly to the open obligation’s destination. Without it, the declared forward path runs, and the open return must still be satisfied before leaving that path. Inspect the current run to understand which return is open.
Parallel branches and joins
Section titled “Parallel branches and joins”A fork accepts 2–16 distinct branch entry IDs. Tau derives the first shared forward destination from their connections and stores it as join; authors can omit this field. The shared step runs once after all active branches from that fork arrive. The editor displays this on the destination card instead of inserting a separate join card. Separate tracks synchronize at finish if they share no earlier step. Alternative outcomes are considered when finding a shared destination; return edges do not introduce new branches. Use separate step IDs to keep executions independent.
For example, replace a build step’s successful outcome with:
ready: parallel: [security, qa] join: consolidateDefine both security and qa to continue to consolidate, and give consolidate its own outcomes. Their instructions can request returns to the builder when fixes are needed. Separate agent sessions share the work stream’s workspace: specify who may edit which files and how reviewers coordinate changes.
maxParallelAttempts is an optional advanced override. Leave it unset for no workflow-specific concurrency limit. When set, it queues additional starts when the flow reaches its bound. Instance and squad capacity can also limit execution. Queued starts do not create workers early.
Routing policy
Section titled “Routing policy”| Field | Values |
|---|---|
mode | guided, flexible, or adaptive. |
returnTo | declared-only or earlier-steps. |
delegation | disabled or allowed. |
Guided allows declared paths only and requires returnTo: declared-only and delegation: disabled.
Workers receive the allowed handoffs and remaining delegation budget in each step assignment. “Earlier” means a predecessor in the declared graph, regardless of the order of steps in the definition. Delegation creates a tracked specialist participant whose result returns to the requesting step.
Flexible permits the additional behavior you enable: earlier-steps allows a return to an earlier step with the current step as requester; allowed permits bounded, tracked specialist delegation. It does not let agents jump arbitrarily between forward steps.
Adaptive also lets an active participant propose a runtime revision through the flow command interface. Without management permission, it must preserve active work, existing steps and their agent types/instructions, delivery mode, and delegation policy, and cannot raise limits. More extensive changes require workstreams:revise-flow. Adaptive does not grant unrestricted configuration or publishing access.
Delegation inserts tracked specialist work with a return to the requesting step. It is separate from declaring another participant or parallel branch in the original definition. If enabled, maxDelegations must be positive.
Limits
Section titled “Limits”| Field | Allowed values and meaning |
|---|---|
maxStepAttempts | Optional integer 1–100; omitted means unlimited attempts. When set, attempts allowed for each step in the current execution pass. Rework consumes attempts too. |
maxDelegations | Required integer 0–100: maximum tracked delegations. Use 0 when disabled. |
maxParallelAttempts | Optional integer 1–32: concurrently active attempts in this flow. Omitted means no workflow cap; global agent capacity still applies. |
onLimit | Must be request-owner-input. |
Exhausting a step’s attempt budget stops further progression for owner intervention. A delegation beyond its limit is rejected and requires an authorized revision. Neither limit automatically declares success. A managed reopen starts a new bounded pass while retaining history.
Completion policy
Section titled “Completion policy”Reaching the end of the graph makes a satisfied flow completion-ready. tau workstream finish separately checks delivery conditions.
completion.mode | Delivery condition |
|---|---|
deliverable | Required work and returns are satisfied and blocking waits resolved; no intrinsic PR or additional reviewer. |
review-approval | A human explicitly approves final delivery through flow finish. |
pr-merge | The change request has been merged; Tau independently verifies it. This policy does not authorize the agent to merge it. |
pr-auto-merge | The current squad must explicitly allow automatic merge. The agent can enable it, but delivery still requires a verified merge. |
direct-merge | The squad must explicitly allow direct merge; Tau verifies the recorded commit is contained in the remote base branch. |
Code hosting (completion.followChanges: true) subscribes to linked PR and issue updates through the stream’s code-hosting adapter, including PR comments, reviews, CI, and merges, plus issue comments, edits, and assignment changes. Issues and additional pull requests are tracked with tau workstream track <ws-id> --url <issue-or-pr-url>, or recorded at creation from an integration notification with tau workstream create … --from-event <event-id>; legacy github.repo/github.pr.number metadata is still recognized for the primary delivery PR, but a stale github.repo/github.issue pair is converted automatically at startup into a tracked issue entry and is never read afterward. Add --delivery when tracking a pull request that must also be merged before completion, alongside the primary codeHost.changeRequest PR — see Delivery pull requests below. Set completion.changeEventsTo: { step: engineer } to send the entire event bundle to an existing agent step. Omit it or use delivery-owner for automatic delivery ownership. Events are retained while the recipient cannot receive them; they do not start a future step. It is optional and does not itself grant repository access or authorize delivery.
Delivery pull requests
Section titled “Delivery pull requests”A work stream has one primary delivery PR (codeHost.changeRequest) and may designate additional delivery pull requests — tracked pull requests flagged delivery: true (issues cannot be flagged). For pr-merge/pr-auto-merge, tau workstream finish verifies the primary change request is merged (its head/branch identity is what completion records), then independently verifies every flagged delivery PR is merged too; an unmerged one fails finish with 409 Delivery pull request <repo>#<n> must be merged before completion. direct-merge does not consider delivery pull requests.
Observed merge/close/reopen events for a delivery PR (pull_request.merged, pull_request.closed, or pull_request.updated with a reopened action) are recorded under the stream’s row lock in metadata.delivery.pullRequests[<key>] as { state: "open" | "merged" | "closed", at, headSha?, eventId? }, with a newer occurredAt always superseding an older one; finish re-verifies live and overwrites this with the verified result. GET /api/workstreams/:id/tracked reports this as delivery: { pullRequests: [{ key, repository, number, url?, primary, state, at?, headSha? }], complete }, where complete requires at least one delivery pull request and all of them merged. Delivery pull request subscriptions (the primary PR’s and any flagged tracked PR’s) may pass the delivery-approval wait once the flow reaches completion-ready; other tracked resources’ subscriptions never do.
GitHub is the implemented adapter today. The serialized pr-merge and pr-auto-merge names describe provider-neutral change-request verification, so future adapters do not require copies of your flow. Bind the resource in work-stream metadata, not the workflow definition:
{ "codeHost": { "integration": "github", "repository": "acme/project", "changeRequest": { "number": 42 } }, "git": { "branch": "feature", "baseBranch": "main" }}An optional codeHost.connectionId selects an authorized account; omission uses the squad default. Direct merge also needs the full git.commit SHA. See GitHub setup.
Integration subscriptions
Section titled “Integration subscriptions”Subscriptions consume events for existing work. Use tau integration outputs to inspect the installed output catalog and its typed fields. Integration IDs, output names, and versions must exist; a made-up event name cannot be saved successfully.
subscriptions: - id: pr-feedback source: integration: github output: pull_request.reviewed version: 1 match: repository: { streamMetadata: github.repo } pullRequest.number: { streamMetadata: github.pr.number } deliver: to: { participant: reviewer } whenInactive: retain| Field | Meaning |
|---|---|
id | Unique subscription ID, 1–100 characters; lowercase start, then lowercase letters, digits, _, ., or -. The code-host- prefix is reserved when followChanges is enabled. |
source.integration, source.output, source.version | Exact versioned output from the catalog. |
source.connectionId | Optional authorized integration account UUID; otherwise use the squad default. |
match | 1–16 equality conditions. Every field must match, with the catalog’s declared type and normalization. |
deliver.to | { participant: id }, { step: id } for an agent step, active, or delivery-owner. |
deliver.whenInactive | retain (default) queues for the intended consumer; manager routes to the squad manager when that consumer is inactive. |
A match binds an event field either to { streamMetadata: path } or to a literal { value: ... }. Literals can be strings, numbers, or booleans. A PR number must be a number, not a numeric string. Data paths use dot-separated object fields, not array indexes or wildcard expressions.
Missing metadata leaves a subscription unbound. Adding a binding does not replay historical events. Events do not create future workers merely to deliver a notification. Pauses retain events; updates do not resolve waits or approve steps.
Squad triggers are separate configuration for creating new work from an event. Put integrationTriggers in squad metadata, with id, source, literal match conditions, and create containing workflow, optional titlePrefix, and metadata mappings from event fields:
integrationTriggers: - id: assigned-issues source: { integration: github, output: issue.assigned, version: 1 } match: repository: { value: acme/project } assignee: { value: tau-bot } create: workflow: { kind: preset, id: solo } titlePrefix: 'Investigate: ' metadata: github.repo: { event: repository }When the triggering event names an issue or pull request, Tau records it in
metadata.tracked[0] for connection-authority events (all GitHub events) with a
server-stamped origin automatically; an instance-authority event has no squad
connection to pin on the entry, so it records none. Do not map
github.issue/github.pr.number into create.metadata for that — it is
never read for tracked-resource identity. github.repo above still gives the
new stream a resolvable repository identity. Triggers deduplicate work for their resource. They do not grant access to a repository. The resulting style can subscribe to later updates. Trigger metadata mappings accept up to 16 entries and cannot write reserved completion, sources, or sourceWarnings roots.
Questions, waits, and pauses
Section titled “Questions, waits, and pauses”These are runtime controls, not extra definition fields. A blocking question from a flow agent normally waits on its current attempt. For example, security can wait for a threat-model answer while QA continues; the join remains held until security finishes. Use a whole-stream wait for a shared blocker.
Answers provide input, not approval. Relevant open waits prevent advancement and automatic continuance for the waiting attempt. Pause requests cancellation of active execution and requires explicit resume; parking separately releases the stream’s capacity slot. See Review and intervene for operating controls.
Presets, sources, and customizations
Section titled “Presets, sources, and customizations”A preset file wraps a definition with:
| Field | Meaning |
|---|---|
id | Stable preset ID. |
description | Optional catalog description, at most 4,000 characters. |
scope | Optional visibility: { kind: instance }, { kind: squad, squadId: UUID }, or { kind: user, userId: UUID }. Access is still permission-checked. |
definition | Complete definition described above. |
A source chooses what a new stream should resolve. It is not the same file shape as a preset:
kind: presetid: brief-review# Optional: pin the revision you inspected to detect a changed preset.# revision: REVISION_FROM_GETcustomizations: - op: set-name name: Brief for the board - op: set-limits limits: maxStepAttempts: 2 maxDelegations: 0 onLimit: request-owner-inputFor a one-off flow, use kind: inline and definition containing the complete definition. It is stored with the stream without adding a catalog entry.
Preset sources accept up to 512 ordered customizations. The whole resulting definition is validated after applying them:
op | Other fields |
|---|---|
set-name | name |
set-entry | entry |
put-participant | id, complete participant |
remove-participant | id |
put-step | Complete step; inserts or replaces by its ID |
remove-step | id |
set-step-order | ids containing every step exactly once |
set-routing | Complete routing |
set-limits | Complete limits |
set-completion | Complete completion |
set-subscriptions | Complete subscriptions array |
Removing an entry does not automatically repair references. A disabled preset cannot be resolved for new work. Existing stream snapshots are preserved.
Structured CLI input
Section titled “Structured CLI input”Choose one explicit source. Prefer single-quoted --content JSON for short
payloads and a quoted heredoc or pipe for longer JSON/YAML. YAML is a shell
string, not a native shell object; quote it rather than letting the shell expand
it. Do not create a temporary file just to pass a payload.
| Commands | Inline | Stdin | Optional saved file |
|---|---|---|---|
workflow create, workflow update ID, workflow resolve | --content '<JSON/YAML>' | --stdin | positional file or --file FILE |
workstream advance ID | --content '<JSON/YAML>' | --stdin | --file FILE |
workstream create TITLE, schedule create, schedule update ID | --flow-content '<JSON/YAML>' | --flow-stdin | --flow FILE |
Creation/scheduling also accept --workflow ID instead of a structured source;
omitting all workflow selectors preserves inheritance (or leaves an update’s
workflow unchanged). resolve accepts a source envelope; workstream/schedule
inputs additionally accept a raw definition. Create/update presets use an envelope
with id and definition, not a source. Update still requires the inspected
--revision; advance still requires the inspected version/attempt and unchanged
retry --request-id. --json only controls output.
tau workflow resolve --squad SQUAD_ID --content '{"kind":"preset","id":"solo"}'tau workflow resolve --squad SQUAD_ID --stdin <<'TAU_FLOW'kind: presetid: solocustomizations: - op: set-name name: Daily auditTAU_FLOW
printf '%s\n' '{"kind":"preset","id":"solo"}' | tau schedule update SCHEDULE_ID --flow-stdinAll sources are bounded to 1 MiB of UTF-8 and must contain one JSON/YAML object.
Empty input, conflicting/repeated sources, duplicate or non-string mapping keys,
unsupported tags/fields, malformed documents, cyclic aliases, and excessive
nesting/alias expansion fail locally without sending an API request. Parsing is
bounded to 100 levels, 100,000 expanded values, and an alias expansion factor of 100. Diagnostics omit source values. --stdin/--flow-stdin require a pipe or
redirection and reject interactive terminals rather than waiting for typing.
File arguments are always filenames, including -; stdin is never selected
implicitly. Saved files remain useful for reusable or large authored definitions
within the same bounds.
Agent authoring workflow
Section titled “Agent authoring workflow”- Inspect
tau workflow list, thentau workflow get PRESET_IDorexportto start from a known definition. Inspect agent types andtau integration outputsbefore selecting agent types or events. - Identify the deliverable, participants, approval authority, rework destinations, and budget. Use the smallest process that meets those requirements. Discuss uncertain choices before publishing.
- Author a preset for reusable configuration or a source for one-off work. Prefer
--content '<JSON>'for short payloads and--stdinwith a quoted heredoc for longer JSON/YAML instead of creating temporary files. Validate withtau workflow resolve --content '<JSON source>' --squad SQUAD_ID(or--stdin). This checks resolution in the target squad without creating a stream or workers. - Publish only when requested:
tau workflow create --content '<JSON preset>', ortau workflow update PRESET_ID --content '<JSON preset>' --revision REVISION_FROM_GET; both also accept--stdin. A revision conflict requires re-reading and reconciling changes. - Create requested work with
tau workstream create "Title" --squad SQUAD_ID --workflow PRESET_ID, or--flow-content '<JSON source>'/--flow-stdinfor an inline/customized source.
Catalog access uses workflows:read, workflows:create, workflows:update, and workflows:delete permissions with scope checks. Runtime flow management uses workstreams:revise-flow; normal active participants submit their own allowed outcomes.
While executing, inspect tau workstream flow STREAM_ID. Advance using the current expectedVersion, your current attemptId, an allowed action, and evidence. For example:
tau workstream advance STREAM_ID --stdin --request-id REQUEST_UUID <<'TAU_COMMAND'{ "expectedVersion": 1, "attemptId": 1, "action": "complete", "outcome": "ready", "evidence": "Brief written; each factual claim has a source."}TAU_COMMANDThe numbers above are examples; always use the current run’s values. For short commands you can also use single-quoted JSON with --content. Saved command files remain supported via --file. Reuse the request ID only when retrying that exact command. Parallel branches can change the run version; re-read on conflict and proceed only if your attempt remains current. Do not advance by changing the legacy assignee or status directly.
In the editor conversation, the assistant’s tools are deliberately narrower: read the current draft and apply targeted edits based on its revision. The edit tool accepts an atomic operations array for adding or updating participants and steps, changing connections, and setting flow options. It preserves everything not mentioned. A full documentJson replacement is available for complete redesigns; supply exactly one edit format. The assistant can also call edit with historyAction: "undo" or "redo", using the same undo history as the preview buttons. read.history reports whether each action is available. It can brainstorm freely, but it cannot publish or start work. Valid edits apply automatically in the page and can be undone. Save explicitly to publish the preset. Use the interactive graph for direct edits. Participants and Settings in its header toggle their panels; selecting a card or arrow opens its inspector. Make separate copies a participant’s settings for one step without changing other steps. This is different from authorizing an operator agent to publish through the CLI.
Validate before using a flow
Section titled “Validate before using a flow”Check that every step is reachable from entry and has a path to finish; all participant, step, and subscription targets exist; forward edges have no cycles; rework targets differ from their requesters; parallel branches are distinct and converge correctly; and every successful path includes any mandatory checks. Schema validation catches structural errors, but clear instructions and an appropriate review process still need human judgment.
For implementation details, contributors can read the repository’s workflow runtime notes and the canonical definition schema. This page is the public authoring reference; use the schema matching your deployed version when building tooling.
Upgrading older definitions
Section titled “Upgrading older definitions”Rename each participant’s profile field to agentTypeId in custom YAML or API payloads. Tau migrates saved styles, squad defaults, scheduled work, editor drafts, and running flows automatically. Pinned agent settings and existing revision references are preserved.
Targeted editor edits
Section titled “Targeted editor edits”The editor assistant uses read, edit, and (with Realtime) delegate. After read, send the returned revision as baseRevision and a short summary with edit. For example:
{ "baseRevision": 3, "summary": "Ask the writer to include sources", "operations": [ { "op": "update-step", "id": "write", "changes": { "instructions": "Write the report and cite every factual claim." } } ]}Use put-participant (id, participant) and put-step (step) to add or replace those objects. Use update-step (id, changes) to preserve unmentioned step fields. Step IDs and kinds are changed by replacement, not by update-step. Use set-outcome (id, outcome, transition) to add or change a connection, and remove-outcome (id, outcome) to disconnect it. A transition can use next, parallel and join, or returnTo and afterRework.
Other operations are remove-step, remove-participant, set-name, set-entry, set-step-order, set-routing, set-limits, set-completion, and set-subscriptions. Set operations replace the named value. Related changes belong in one batch: for example, add a reviewer participant, add its step, and redirect the preceding step’s outcome together. Deleting a step also requires fixing connections that reference it. Tau validates the complete resulting flow before applying anything; an invalid or stale batch changes nothing. Each successful batch is one undoable edit.