Workflow Stages
Workflow stages define the lifecycle of a workflow-created agent: entry instructions, done signals, issue transitions, PR events, and terminal stages.
What are stages?
Stages are the state machine that drives a workflow-created agent through its lifecycle. Each stage has entry conditions, actions, and transitions. ElasticClaw Server injects instructions into the agent as messages at each stage transition.
Stage structure
stages:
- id: working
label: "Working"
entry: true
on_enter:
inject: |
Read your CONTEXT.md and start working on the issue.
Narrate your progress as you go. Keep me updated.
- id: pr_opened
label: "PR Opened"
triggers:
- message_contains: "[DONE]"
on_enter:
move_issue: "In Review"
inject: |
PR created. Watch for CI results and review comments.
- id: merged
label: "Merged"
triggers:
- pr_merged:
terminal: true
- id: closed_no_merge
label: "Closed Without Merge"
triggers:
- pr_closed:
on_enter:
inject: |
PR was closed without merging. Decide: reopen, new PR, or ask the user.Tool gates
Stages can run a command and evaluate its structured output before deciding what happens next. The command runs in the agent workspace, and the named output is available to later templates as{{ .Outputs.<name>.<field> }}.
stages:
- id: validate
label: Validate
triggers:
- message_contains: "[DONE]"
on_enter:
run:
command: python3 scripts/check.py
output: check
timeout: 10m
gate:
output: check
pass:
path: status
values: [passed, skipped]
fail:
path: status
values: [failed, error]
required: true
- id: create_pr
triggers:
- gate_result:
stage: validate
verdict: pass
on_enter:
inject: |
Validation passed with status {{ .Outputs.check.status }}.
- id: fix
triggers:
- gate_result:
stage: validate
verdict: fail
on_enter:
inject: |
Validation failed: {{ .Outputs.check.reason }}Plan approval gates
Issue and workflow agents normally get a freeform hub prompt to write a plan in chat before implementing. To replace that with a deterministic gate, mark a stage with plan_gate: true and configure a gate: on that stage.
- When any stage has
plan_gate: truewith agate, the hub skips freeform plan approval for that pipeline (no keyword scoring, no double proceed). - Validation-only gates without
plan_gateleave freeform plan approval in place (existing workflows stay compatible). - On plan-gate
pass, the hub records plan accepted so freeform cannot re-fire; the next stage'sinjectis the proceed signal.
stages:
- id: plan
entry: true
on_enter:
inject: |
Write .elasticclaw/plan.json (understanding, area, steps, verification),
then say [PLAN_READY]. Do not implement until proceed is injected.
- id: plan_validate
plan_gate: true
triggers:
- message_contains: "[PLAN_READY]"
on_enter:
run:
command: python3 scripts/validate_plan.py
output: plan
gate:
output: plan
pass:
path: status
values: [ok]
fail:
path: status
values: [incomplete]
required: true
- id: implement
triggers:
- gate_result:
stage: plan_validate
verdict: pass
on_enter:
inject: Plan accepted. Proceed with implementation.See the full worked example under Workflows → Plan approval.
Stage fields
id — Unique stage identifier (kebab-case)
label — Human-readable label shown in UI
entry: true — Marks the initial stage when an agent is created
terminal: true — Marks a terminal stage (the agent will be terminated)
triggers — Conditions that transition into this stage
on_enter — Actions to run when entering this stage
gate — Optional deterministic pass/fail evaluation over a named run output
plan_gate: true — Marks this stage's gate as plan approval. When present with a gate block, freeform hub plan approval is skipped for the workflow. Ordinary gates without this flag do not change plan behavior.
Triggers
Each trigger defines a condition. Exactly one field should be set per trigger:
message_contains: "[DONE]" — Matches when an agent message contains this substring (case-insensitive)
gate_result — Matches a previous gate verdict, such as pass or fail
judge_verdict — Matches a model review verdict from a judge stage
output_matches — Matches a persisted output path against one or more expected values
pr_merged: — Triggers when the tracked PR is merged (key presence alone activates it)
pr_closed: — Triggers when the tracked PR is closed without merging
pr_conditions: — Compound conditions that must all pass:
ci: "passing"— All check runs are success or skippedreviews: "clean"— No CHANGES_REQUESTED reviewsquiet_for: "1h"— No new comments in the last hour
Stage skip rules
A stage can be skipped before its on_enter actions run based on the source issue labels. skip_if jumps when the issue has any of the listed labels; skip_unless jumps when the issue has none of them. The target stage is given by go_to.
stages:
- id: working
entry: true
skip_if:
issue_labels:
labels:
- skip-agent
go_to: skipped
on_enter:
inject: Read CONTEXT.md and start working.
- id: skipped
label: Skipped
terminal: trueOn-enter actions
inject — Sends a user message to the agent
run — Runs a command in the agent workspace and can persist stdout as structured output. Supports command, output, timeout, and continue_on_error.
dependency_updates — Updates Go and npm dependencies with native tooling and persists structured output
judge — Runs a model-backed review over bounded inputs and persists a verdict. Supports model, inputs, instructions, output, require.verdict, max_tokens, timeout, and continue_on_error.
move_issue — Moves the associated Linear, Jira, Shortcut, or GitHub issue. It accepts a status string or { status, issue_id }.
close_issue: true — Closes the associated GitHub issue
add_labels — Adds labels to the associated GitHub issue
remove_labels — Removes labels from the associated GitHub issue
merge_pr: true — Attempts to merge the tracked PR through ElasticClaw Server's GitHub PR merge path
Template variables
inject messages and mapped move_issue.issue_id values can use Go template variables. Automatic issue-triggered workflows expose {{.Issue.Identifier}}, {{.Issue.Title}}, {{.Issue.URL}}, and{{.Issue.Description}}. Manual triggers expose{{.Inputs.name}} values from the workflow inputs.
Default stages
A typical issue workflow has four stages:
- working — The agent starts here. Injected with "read CONTEXT.md and start working."
- pr_opened — Triggered by
[DONE]message. Moves issue to done status. - merged — Triggered by PR merge. Terminal — the agent terminates.
- closed_no_merge — Triggered by PR close without merge. Injected with guidance.
elasticclaw workflow push --workspace <workspace> <file-or-dir>.