Definition Authoring
Author a definition when a repeated process should become portable, reviewable, and executable across projects. The YAML records its inputs, dependencies, decisions, and published results as one versioned contract.
File placement
Pack-authored workflows are standalone .workflow.yaml or .workflow.yml files under a pack.yaml include root:
example-workflows/
pack.yaml
workflows/
task-delivery-review.workflow.yamlMinimal definition
schemaVersion: 1.0
workflow:
key: example.task.delivery-review
name: Example Task Delivery Review
version: 0.1.0
pack: example-workflows
source:
kind: capability
capabilityKey: example.workflow.task-delivery-review
start:
mode: manual
inputs:
taskKey:
type: string
required: true
outputs:
taskKey:
type: string
required: true
outputMappings:
taskKey: "${workflow.inputs.taskKey}"
steps:
- key: load_task
name: Load task
kind: tool
tool:
id: pm_task
action: get
inputs:
taskKey: "${workflow.inputs.taskKey}"
outputs:
status: "$.normalized.task.status"Every workflow needs a schema version, workflow key, name, version, start configuration, inputs, and steps. Add only the outputs the completed run should publish.
Definition fields
| Field | Required | Purpose |
|---|---|---|
schemaVersion | Yes | Definition format; use 1.0 |
workflow.key | Yes | Stable workflow identity |
workflow.name | Yes | Name shown in Valdr UI |
workflow.description | No | Short explanation of the workflow outcome |
workflow.version | Yes | Exact version selected by runs and parent workflows |
workflow.pack | Pack source | Owning pack key |
workflow.source | Pack source | Capability that owns the workflow source |
workflow.start.mode | Yes | Use manual |
workflow.start.suggestedWhen | No | Catalog hints for when the workflow is useful |
workflow.inputs | Yes | Values callers may or must provide |
workflow.outputs | No | Results the completed run publishes |
workflow.outputMappings | For required outputs | Maps each required result from an input or step output |
workflow.steps | Yes | Executable steps and their dependencies |
Input and output descriptors support string, boolean, number, integer, object, and array:
inputs:
taskKey:
type: string
required: true
description: Task to deliver
includeDraft:
type: boolean
default: trueUse description to make Builder launch forms self-explanatory. A default makes an optional input usable without a supplied value. Output names must match [A-Za-z_][A-Za-z0-9_]*. Every required workflow output needs an outputMappings entry unless its descriptor supplies a type-correct default.
Version changes deliberately
Treat the workflow key and version as a published identity. When behavior changes, create a new version so existing runs and parent workflows keep the behavior they selected.
Prefer updating pack workflows through their source pack. A guarded same-version overwrite is supported for local and pack-managed definitions when the caller preserves the exact provenance, sets overwrite: true, and supplies the current content hash. Existing runs keep their frozen routing and child revisions, and reruns reuse those frozen revisions. An eligible same-run manual retry of a saved Command step is the deliberate exception: its next attempt can snapshot the active saved definition’s command, cwd, and timeoutSeconds; prior attempts remain unchanged. For a one-off variation, use Use as starting point in Valdr UI and save it under a new identity.
Step kinds
| Kind | Use it to |
|---|---|
tool | Call an action-based Valdr MCP tool, bounded Git or GitHub action, or trusted shell command |
session | Launch or continue an agent session |
review | Launch an independent reviewer |
await_condition | Wait for an expected result or evidence |
human_gate | Require an operator decision |
condition | Outcome route: evaluate a value for branching or repeat a bounded correction loop |
subworkflow | Run an exact version of another workflow |
Expressions and outputs
Use expressions to connect declared data:
| Syntax | Meaning |
|---|---|
${workflow.inputs.taskKey} | A workflow input |
${steps.load_task.outputs.taskStatus} | A named output from an upstream step |
${runtime.actor} | The operator recorded for the run |
${runtime.loop.currentPass} | The current pass inside a bounded loop |
TASK-${workflow.inputs.taskKey} | Text interpolation |
Other runtime references include runUlid, rootRunUlid, taskKey, projectKey, contextRef, createdFromSessionUlid, stepKey, attempt, startedAt, clientRequestId, and actorHandle. Inside a loop corridor, runtime.loop also exposes targetStepKey, sourceStepKey, and previous; loop references outside a corridor are invalid.
Map only values later steps or the final result need:
- key: load_task
name: Load Task
kind: tool
tool:
id: pm_task
action: get
inputs:
taskKey: "${workflow.inputs.taskKey}"
outputs:
taskStatus: "$.normalized.task.status"Later steps use ${steps.load_task.outputs.taskStatus}. The Workflow Builder offers supported output choices for the selected action.
Dependencies
Use needs whenever a step depends on another step or consumes its outputs:
- key: mark_in_progress
name: Mark Task In Progress
kind: tool
needs: [load_task]
tool:
id: pm_task
action: change_status
inputs:
taskKey: "${workflow.inputs.taskKey}"
to: in_progress
actorHandle: "${runtime.actor}"Compose another workflow
A subworkflow step selects an exact saved child version and binds its declared inputs:
- key: verify_delivery
name: Verify Delivery
kind: subworkflow
workflow:
key: example.task.verify-delivery
version: 1.0.0
inputs:
taskKey: "${workflow.inputs.taskKey}"
outputs:
status: "$.normalized.child.outputs.status"The child must declare every bound input and selected output. Publishing a newer child version does not update the parent automatically; change the parent’s selected version when you want the new behavior.
Wait for agent or review evidence
An await_condition step lists the outcomes it accepts and the agent or session allowed to provide them:
- key: await_executor
name: Await Executor
kind: await_condition
needs: [launch_executor]
waitsFor:
kind: workflow_input
expected: [executor_completed]
authorizedHandles: ["${workflow.inputs.executorHandle}"]
sourceSessionUlid: "${steps.launch_executor.outputs.executorSessionUlid}"
outputs:
outcome: "$.normalized.input.outcome"Keep the accepted outcomes narrow and bind the wait to the session, review, or task that owns the evidence.
Add a human gate
- key: approve_release
name: Approve Release
kind: human_gate
needs: [prepare_release]
humanGate:
prompt: "Approve ${steps.prepare_release.outputs.releaseName}?"
approvalText: Ship
rejectionText: Hold
waitsFor:
kind: workflow_input
expected: [approved, rejected]
authorizedHandles: ["@release-operator"]
outputs:
outcome: "$.normalized.input.outcome"Human gates may also present one to five reviewed documents and send feedback to the configured writer session. Feedback keeps the gate open; the operator approves or rejects only after reviewing the refreshed result.
Add a bounded correction loop
Use an Outcome route with loop_back when requested changes should return to an earlier step:
- key: route_review
name: Route Review
kind: condition
needs: [await_review]
checks:
- kind: value_equals
value: "${steps.await_review.outputs.outcome}"
equals: review_approved
domain: [review_approved, review_changes_requested]
outputs:
outcome: "${steps.await_review.outputs.outcome}"
onFailure:
action: loop_back
to: implement
max: 3
exhausted: blockmax is from 1 to 10 and counts the first pass and every correction pass. The domain must be a non-empty, unique list containing equals and every possible observed value. Keep loops narrow and bounded: corridor steps cannot declare retry, and corridor subworkflows cannot use forEach or detached.
Validate and import
Validate the pack source:
valdr validate-pack example-workflowsGenerate an archive:
valdr generate-valdr-pack example-workflows \
--output scratch/example-workflows.valdr-pack.tar.gzOpen Settings in Valdr UI to preview and import the pack. Fix validation errors at the reported workflow and step before importing.
Next step
Open the Workflow Steps reference for per-kind examples, or use the Workflow Builder to compose the same definition visually.