Skip to content

Human-Gate Steps

Sovereign
Sovereign tier required. Human gates keep final authority with the operator while the surrounding process remains automated.

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"
FieldRequiredPurpose
humanGate.enabledNoSet false only when this version intentionally bypasses the gate
humanGate.promptNoDecision shown to the operator
humanGate.approvalTextNoApproval button label
humanGate.rejectionTextNoRejection button label
humanGate.referencesNoUp to eight labeled task, session, run, or URL links
humanGate.previewNoOne to five documents to review before deciding
humanGate.feedbackNoWriter session, task, and review that receive requested corrections; requires at least one preview document
humanGate.pullRequestProposalNoEditable pull-request proposal backed by frozen branch evidence
waitsFor.expectedYesOne or both of approved, rejected
waitsFor.authorizedHandlesYesOperators allowed to decide
outputsNoNamed 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 run

Each 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: approved

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