Bounded review loops and serial collections¶
Use a reviewed definition when work needs finite retries, revision after human
review, or a serial body for each input record. Check astro status --json for
workflows.bounded_review_loops and workflows.serial_collections, and use a CLI
release containing this guide. Capability discovery describes the installation;
current workflow permissions and token scope still apply to every operation.
Return rejected work to its author¶
[workflow]
slug = "bounded-review"
name = "Bounded review"
pattern = "review_loop"
[[stage]]
kind = "agent_dispatch"
agent = "author"
output_key = "draft"
on_failure = "retry"
max_attempts = 2
timeout = 600
[[stage]]
kind = "human_gate"
output_key = "review"
prompt = "Approve, or explain the required revision?"
approvers = ["team:reviewers"]
timeout = 3600
[stage.back_edge]
to = "draft"
when = "gate_rejected"
max_rounds = 3
on_exhausted = "fail"
Replace the agent and approver selectors with your actual organization targets.
Both ends of a return require explicit, unique output_key values. Rejection
returns to draft, carrying the review note and predecessor context; approval
continues forward. max_rounds = 3 allows the first visit and at most two
returns. That edge's lifetime budget does not reset when another edge revisits
it. At exhaustion, fail ends the run; escalate opens an operator wait whose
clearance preserves the recorded escalation outcome.
max_attempts is an integer from 1 through 20, default 3. It includes the first
attempt. Agent and nested-workflow retry policies use that stage's cap; other
failure policies remain fail, skip and escalate. A retry cap and a review
return cap are separate bounds. Completed work under a skip/escalation policy
does not change a failed agent into a successful agent result.
Native return conditions are gate_rejected on a human gate, stage_failed on
an agent or nested workflow, and output_equals with a dotted path and an
explicit JSON scalar value. Round caps are 1 through 20. always and
on_exhausted = "continue" are control/output semantics used by the supported
Flowise import, rather than a replacement for native review approval.
The reviewed plan bounds all return visits, retry attempts, collection items and fan-out branches before dispatch: at most 1,000 stage visits and 500 execution units. A combined plan can exceed the budget even when each individual cap is valid. Dynamic/implicit fan-out reserves up to 50 workers; an explicit smaller count uses its authored bound. Use authoritative server validation and inspect the reviewed revision before starting.
Run a body once per record, in order¶
This complete no-input example uses literal records and a deterministic formatter, so it needs no agent workload to demonstrate serial scheduling:
[workflow]
slug = "bounded-records"
name = "Bounded records"
pattern = "chained"
[[stage]]
kind = "collection"
output_key = "each"
iteration_json = '{"max_items":2,"body_end":"render","items":[{"text":"first"},{"text":"second"}]}'
[[stage]]
kind = "format_record"
output_key = "render"
iteration_json = '{"source_format":"langflow_parser","pattern":"Item: {text}","separator":"\n"}'
A collection requires max_items from 1 through 50, a forward body_end
output key, and exactly one of literal items or a dotted items_path into
the predecessor output. Its records must be JSON objects. Missing or oversized
input is unavailable and dispatches no body items; an empty verified list
finishes with zero results. It never truncates an oversized list.
The body is the contiguous range after the collection through body_end.
Supported body kinds are agent_dispatch, workflow, checkpoint,
human_gate and format_record. Configure an agent stage's exact workload
binding, environment recipe, skills and prompt before dispatch. A nested stage
uses kind = "workflow" and a definition reference; review its resolved
identity using the exact-reference rules below. A native body binding does not translate an
arbitrary source-framework model/tool configuration automatically.
Each item executes in a separate durable child, with the exact parent execution and recorded zero-based item index. An item gate blocks the next item until decided. Bounded returns inside one body are supported. Nested collections, parallel body stages and returns entering or leaving a body range are refused. An outer return can repeat the collection under its edge's lifetime bound.
The parent output contains ordered results, item_execution_ids,
finished_count, complete and its frozen collection binding. A failed body
stops later items and reports an incomplete collection. complete records that
all bodies finished under the authored failure policy; inspect each item's
outcome separately. Input and collected output must be finite JSON with string
keys, at most 32 nesting levels, 16,384 nodes and 256 KiB encoded size. An
oversized aggregate fails explicitly.
An abort during a blocked item cancels and awaits that child's closure, then fails the parent and its open stage mirrors with an incomplete collection. SDK cancellation settles the parent and open mirrors as cancelled. Both stop later items. A control acknowledgement does not establish run closure or resource cleanup; inspect the terminal metadata.
Preserve an explicitly selected target¶
Use agent = "guid:<workload-guid>" or
workflow = "guid:<definition-guid>" to retain the selected target identity.
The UUID after guid: must use canonical lowercase, hyphenated spelling,
for example guid:123e4567-e89b-42d3-a456-426614174000. TOML export/re-import,
repository reconciliation and frozen reviewed plans preserve that reference.
An agent slug can exist in two apps; a GUID selects the reviewed workload.
A nested definition rename preserves its GUID. A malformed, unavailable,
deleted or foreign GUID target is refused, including when another target now
uses its former slug. No slug fallback replaces an explicit GUID.
Literal references such as agent = "author" and
workflow = "your-definition-slug" remain compatible. They keep their existing
slug-resolution behavior; they do not establish immutable target identity.
An agent slug may resolve after import when a matching workload is registered
in the run's organization. Review the resolved target before dispatch. Nested
project visibility, current trigger/dispatch permissions and token scope still
apply to GUID references. Configured stage binding overrides remain explicit
choices in the reviewed plan; a conflicting default agent mapping is refused.
A target GUID does not prove an imported framework's model/tool equivalence.
Validate, bind, review and observe¶
astro workflow init --pattern review_loop -o review.toml
astro workflow validate review.toml
astro workflow validate review.toml --server
astro workflow import review.toml --preview
astro workflow run-manifest review.toml --dry-run --bind 0=<agent-workload-guid>
Local validation checks shape and preserves bounded fields; the server owns graph budgets, collection ranges, target resolution and authorization. Review the dry-run plan before choosing the configured-workflow start path from workflow setup. For an enabled persisted definition, the reviewed-start path binds its GUID, revision, schema digest and durable request identity. Frozen starts keep the reviewed plan after a live edit. Recover a lost start reply by its existing request file, rather than dispatching a new request.
astro workflow execution-stages <execution-guid> --json
astro workflow gates --json
astro workflow gate <definition-slug> --run <run-guid> --decision reject --note "Revise the draft"
Use the original server and organization. Stage metadata includes one-based
roundNumber and attemptNumber, return cause (edge, reason, maxRounds,
edgeRound), timestamps and separate collection/fan-out parent identities.
JSON keeps zero-based collectionIndex and fanoutIndex; text item/branch
labels start at one. The CLI reads the exact execution identity across every
page. Missing metadata stays unavailable and schema/permission failures do not
select a different run. A gate decision is authorized by the server and routed
to the recorded item child.
Source imports and their boundaries¶
TOML, YAML and GraphQL preserve the same bounded native contract. Export uses
back_edge_json and iteration_json to preserve JSON null values; authoring
accepts either that string or the corresponding object, never both.
The Flowise Loop 1.2 mapping is based on source revision 9291856. Its supported terminal sequential loop becomes an explicit bounded control return, preserving max-loop-count, continuation at cap and literal fallback output. State, router, parallel and unresolved-variable semantics outside that mapping are refused.
The Langflow mapping is based on source revision f9b2832. The supported graph connects CreateList to Loop, Loop's item into a built-in Parser and Parser feedback to that same Loop; an optional done edge can feed explicit TypeConverter JSON conversion. It preserves ordered records, plain named Parser fields, separator, missing fields as empty strings and the final record table, then executes the serial body. Custom code, routers/state, nested source loops, Stringify mode and other source body component types are refused. Native agent/workflow collection bodies do not certify broader Langflow Agent or RunFlow source translation. Source importer tests do not invoke the Langflow runtime, and unavailable-target agent tests do not prove successful container execution.