2.3 Waits and gates — the policy way
Included from Basic tier
After this you can: pause an action with a wait step, and know that approval gates are imposed by policy on the path a change runs — not written into the action.
Before you start — tick each one:
- Lesson 2.2 — Namespaces, composition and inputs (
opschain learn 2.2)
In the GUI: a held run sits in the Reviews & approvals inbox with its approval context, and the sidebar entry wears a red bubble with the count waiting on you.
Some steps need a human: a maintenance window to pick, a sign-off before production moves. This lesson separates the two cleanly. A wait step is authoring — it belongs in your action file. An approval is governance — it belongs in a policy on the server, and nothing in the action file names an approver.
1. Wait steps
A wait: step pauses the run for a human. Locally it prompts right in
your terminal; declared inputs are prompted one by one with their
defaults:
action: pick
steps:
- name: pick_window
wait:
inputs:
- name: maintenance_window
default: "02:00-04:00"
description: Deployment window
- name: after
run: echo proceeding
opschain runs create change pick --env dev --plan --local
· pick
├─ · pick_window
└─ · after
Run it from a terminal and the prompt appears at pick_window; answer
it and after runs. Run it where no terminal can answer — a script, CI —
and the run cancels loudly rather than hanging:
2026-11-07T00:26:25Z pick/pick_window pending -> waiting
2026-11-07T00:26:25Z pick/pick_window waiting -> cancelled
2026-11-07T00:26:25Z pick/after pending -> aborted
2026-11-07T00:26:25Z pick running -> cancelled
Error: prompter failed for step pick/pick_window: step pick/pick_window is waiting for input but stdin is not an interactive terminal — re-run with --approve-all, or run from a terminal
The exit code is 3 — cancelled, the same code a run you interrupt
exits with — and the refusal names the switch. --approve-all answers
every wait step with its defaults — the switch for unattended runs:
opschain runs create change pick --env dev --local --approve-all
2026-11-07T00:25:50Z pick/after queued -> running
[pick/after] proceeding
2026-11-07T00:25:50Z pick/after running -> success
2026-11-07T00:25:50Z pick running -> success
run 01a1b365-b6aa-73f0-94ad-f9ce78b156bc success (0.1s)
On a server the same step waits in the record, and whoever may act on
the node answers it with opschain runs approve <run-id> --step <step-id>
(lesson 3.2). That is a wait, not an approval: it asks a person for
input, it does not say which person must sign off.
2. Approval gates live in policies
To require a governed sign-off — named approvers, quorum, an audited decision — a change manager writes an approval policy on the server. The policy names a path pattern; every change created under that path is held for approval before it starts, whatever action file it runs:
opschain approval-policies create --name 'Production change control' \
--path-pattern '/projects/acme/environments/prod/**' \
--approver-group release_managers --quorum 1
From then on a change created under prod is created normally — its
step tree freezes and a fingerprint is taken — and then held:
pending_approval in every listing, a checklist opened in the approvers'
inbox, nothing driven, no runner, no queue slot. The approvers answer
with opschain approvals approve|reject <run-id> and read the inbox with
opschain approvals list; the decision history is
opschain approvals history.
The point for you as an author: the action file does not change.
Who signs off is governance, not authoring — the same deploy action
runs ungated in dev and held in prod, decided by the path it runs
on. Writing policies needs the packs:change-governance entitlement and
the policy-writer role; opschain learn governance-approvals walks
the whole funnel from the change manager's seat.
3. A note on the legacy key
Repositories written before approval policies may carry
requires_approval_from: (user_names:/ldap_groups:) on a wait
step — an in-YAML approval gate only the named users or directory groups
may answer mid-run. It still works and is still enforced, but it is the
superseded form: do not write it in new actions. For stored workflows,
opschain approval-policies convert-step-gates inventories every such
gate and creates the equivalent policies without touching the YAML; for
repository actions, create the policy at the owning path and delete the
key at your own pace.
Check yourself
- Where does an approval requirement live for a production change, and what does the legacy in-YAML key do today?
What you learned
- A
wait:step pauses a run for human input; locally it prompts in your terminal, and--approve-allanswers it with the defaults. - A wait asks a person for input; an approval names who must sign off — and that lives in an approval policy on the server, matched by path.
- A change under a policy's path is created, then held
pending_approvaluntil the approvers decide, with the action file unchanged. requires_approval_from:is the superseded in-YAML form;convert-step-gatesturns stored workflows' gates into policies.
Next: lesson 2.4 — Using packs (opschain learn 2.4), or back to your track (opschain learn).