Human-Gate Steps
Use a human_gate when the workflow must stop for an explicit human decision. The run inspector presents the authored prompt and enables the decision only for an authorized operator.
Configuration
- 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"| Field | Required | Purpose |
|---|---|---|
humanGate.enabled | No | Set false only when this version intentionally bypasses the gate |
humanGate.prompt | No | Decision shown to the operator |
humanGate.approvalText | No | Approval button label |
humanGate.rejectionText | No | Rejection button label |
humanGate.references | No | Up to eight labeled task, session, run, or URL links |
humanGate.preview | No | One to five documents to review before deciding |
humanGate.feedback | No | Writer session, task, and review that receive requested corrections; requires at least one preview document |
humanGate.pullRequestProposal | No | Editable pull-request proposal backed by frozen branch evidence |
waitsFor.expected | Yes | One or both of approved, rejected |
waitsFor.authorizedHandles | Yes | Operators allowed to decide |
outputs | No | Named decision for downstream routing |
When copy is omitted, Valdr uses the step name, Approve, and Reject.
humanGate.enabled: false intentionally bypasses the decision and records the step as approved without waiting for human input. The step must still include approved in waitsFor.expected. Use a new workflow version when changing this setting. A run that already froze an enabled gate remains waiting for its operator decision.Link supporting records
References give the operator direct context without changing the decision contract:
humanGate:
references:
- kind: task
target: "${workflow.inputs.taskKey}"
label: Open task
- kind: run
target: "${runtime.runUlid}"
label: Open runEach reference needs kind, target, and label. Supported kinds are task, session, run, and url.
Preview reviewed documents
A gate can present one to five documents before the operator decides:
humanGate:
prompt: "Approve the reviewed specification?"
approvalText: Approve spec
rejectionText: Reject
preview:
documents:
- key: spec
label: Specification
path: "${steps.create_spec.outputs.path}"
sourceSessionUlid: "${steps.create_spec.outputs.writerSessionUlid}"Each document needs a unique key, a visible label, its path, and the session that created it.
Send feedback before deciding
humanGate:
feedback:
sourceSessionUlid: "${steps.create_spec.outputs.writerSessionUlid}"
taskKey: "${workflow.inputs.taskKey}"
reviewId: "${steps.create_spec.outputs.reviewId}"Feedback returns a correction request to the configured writer session and refreshes the preview. It does not approve or reject the gate. The operator reviews the updated document, then makes the final decision.
The Builder exposes structured Preview documents and Feedback controls. Use its YAML dialog when you need to inspect or edit the full definition directly.
Approve a pull-request proposal
A pull-request gate lets the operator edit the title, body, and draft state before approving publication. Bind it to frozen repository evidence:
humanGate:
prompt: Review and approve the pull request proposal.
approvalText: Publish pull request
rejectionText: Reject
pullRequestProposal:
repository: "${steps.observe_remote.outputs.repositoryOwner}/${steps.observe_remote.outputs.repositoryName}"
remoteName: "${workflow.inputs.remoteName}"
baseBranch: "${workflow.inputs.baseBranch}"
headBranch: "${workflow.inputs.headBranch}"
headSha: "${steps.resolve_head.outputs.resolvedSha}"
writerSessionUlid: "${steps.draft_proposal.outputs.sessionUlid}"
evidenceDigest: "${steps.read_diff.outputs.digest}"
initial:
title: "${steps.draft_proposal.outputs.title}"
body: "${steps.draft_proposal.outputs.body}"
draftFrom: "${workflow.inputs.initialDraft}"draftFrom must be an exact reference; the referenced value must resolve to a Boolean at runtime. You may use a literal Boolean in initial.draft instead. Initial and edited titles must be one line and at most 256 characters; bodies must use LF line endings and be at most 65,536 bytes. Map $.normalized.input.payload.title, .body, and .draft as step outputs when the later GitHub step should publish the operator-approved values.
Route the decision
- key: release
name: Release
kind: tool
needs: [approve_release]
when:
- kind: value_equals
value: "${steps.approve_release.outputs.outcome}"
equals: approvedA rejection is recorded as the rejected workflow value. Without a block or fail policy, route it to the appropriate follow-up step. Set onFailure.action to block or fail when rejection should stop the run at the gate.
Next step
Continue with Outcome route to route the decision explicitly.