Skip to content
Definition Authoring

Definition Authoring

Sovereign
Sovereign tier required. Workflow authoring turns repeatable delivery practice into validated, versioned automation.
This is the advanced path. Start with the Workflow Builder if you prefer visual 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.yaml

Minimal 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

FieldRequiredPurpose
schemaVersionYesDefinition format; use 1.0
workflow.keyYesStable workflow identity
workflow.nameYesName shown in Valdr UI
workflow.descriptionNoShort explanation of the workflow outcome
workflow.versionYesExact version selected by runs and parent workflows
workflow.packPack sourceOwning pack key
workflow.sourcePack sourceCapability that owns the workflow source
workflow.start.modeYesUse manual
workflow.start.suggestedWhenNoCatalog hints for when the workflow is useful
workflow.inputsYesValues callers may or must provide
workflow.outputsNoResults the completed run publishes
workflow.outputMappingsFor required outputsMaps each required result from an input or step output
workflow.stepsYesExecutable 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: true

Use 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

KindUse it to
toolCall an action-based Valdr MCP tool, bounded Git or GitHub action, or trusted shell command
sessionLaunch or continue an agent session
reviewLaunch an independent reviewer
await_conditionWait for an expected result or evidence
human_gateRequire an operator decision
conditionOutcome route: evaluate a value for branching or repeat a bounded correction loop
subworkflowRun an exact version of another workflow

Expressions and outputs

Use expressions to connect declared data:

SyntaxMeaning
${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: block

max 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-workflows

Generate an archive:

valdr generate-valdr-pack example-workflows \
  --output scratch/example-workflows.valdr-pack.tar.gz

Open 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.