3.2 Gates, retry, cancel
Included from Basic tier
After this you can: answer an approval gate, retry a failed run, and cancel a running one — and read what each leaves in the record.
Before you start — tick each one:
- Lesson 3.1 — Your first change (
opschain learn 3.1)
In the GUI: the run page lesson 3.1 followed shows the run's status, and the Reviews & approvals inbox lists every subject held for your approval.
Lesson 3.1 ran a change that went straight to success. Real changes
park on a wait step, fail halfway, or need stopping — and each of those
moments is a verb with an audit record. This lesson walks the four verbs
an operator answers a run with: continue/approve/reject on a gate,
cancel in flight, retry after a failure, and pause as the gentler hold.
Every step below uses the pick action from lesson 2.3 — its wait step
is what gives you a run to hold, cancel and retry.
1. Answer the gates
A wait: step (lesson 2.3) in a server-side change parks on the server —
the run holds at waiting until someone answers:
opschain runs create change acme/environments/dev --action pick
created change 01a13f20-…-example (pick on acme/dev) — pending
Find the waiting step's id in the run's step tree, then answer it:
opschain runs show <run-id> -o json | grep -B4 '"waiting"'
"id": "01a13f20-…-example",
"path": "pick/pick_window",
"name": "pick_window",
"kind": "wait",
"status": "waiting",
opschain runs continue <run-id> --step 01a13f20-…-example --message "window confirmed"
continued step 01a13f20-…-example of run <run-id> — now success
continue answers a plain wait step; approve and reject answer an
approval-gated one, and only the required approvers may:
opschain runs approve <run-id> --step <step-id> --message "LGTM"
opschain runs reject <run-id> --step <step-id> --message "not tonight"
approved step <step-id> of run <run-id> — now running
The --message is recorded in the audit events. A step that is not
waiting for that answer is refused by the server with a named error — you
cannot approve the wrong thing by accident. Who counts as a required
approver is the access model's territory (lesson 7.2); until then, the
login from lesson 0.1 can answer every gate. Governed sign-off — named
approvers, quorum, the approvals inbox — is an approval policy that
holds the whole run before it starts: lesson 10.1
(opschain learn governance-approvals).
2. Cancel a run in flight
Create a second pick change and let it park on its wait step; this
time, instead of answering, cancel it:
opschain runs cancel <run-id> --reason "change window closed"
cancelled run <run-id>
The optional --reason (up to 1000 characters; the GUI's cancel dialog
sends the same field) is recorded on the cancel audit event and appended
to each cancelled step's audit trail as cancelled via API: <reason>.
The run finalises cancelled, and an executing step is aborted on its
runner within one lease-renewal round-trip, freeing that runner for the
next run. An already-finished run is refused with the server's named
error:
Error: the server answered failed_precondition: Cannot cancel because it is in the "cancelled" state
3. Retry a failed run
A retry needs a run that did not succeed: the server retries a change in
error, cancelled, aborted or rejected state and refuses a
successful one by name (Change must be in error, cancelled, aborted or rejected state to be retried). The change you just cancelled is the
subject:
opschain runs retry <run-id>
created change 01a13f31-…-example (pick on acme/dev) — queued
A retry is a new run duplicating the failed one — the original stays
untouched as evidence (still cancelled in runs list), which is why
the output is a fresh created line. Steps that succeeded are preserved
and do not execute again; the failed subtree re-runs — here the
duplicate parks on the wait step again, and continue finishes it.
--follow works exactly as on create. If part of the tree is still
known-broken, replace the skip mask for the retry:
opschain runs retry <run-id> --skip-step "deploy/optional_bit/**"
A retry normally re-runs at the original's pinned commit. For a project or environment git change you can instead re-resolve the branch or tag to its current head and pin the duplicate to the moved head:
opschain runs retry <run-id> --refresh-git-rev
A sha rev re-resolves to itself, and an asset change refuses the option with a named error (its commit comes from its template version, lesson 3.4).
4. Pause instead of killing
To hold a run without killing it, pause it — the step that is running finishes, nothing new starts, gates still answer, and the hold survives a server restart:
opschain runs pause <run-id> --reason "change window closed"
paused run <run-id> — pause state pausing
pausing means a step is still finishing; it settles to paused when
nothing is running. Release it when the window reopens:
opschain runs resume <run-id>
resumed run <run-id> — pause state none
The pause and resume are audited in the run's paused_by history.
Pausing a paused or finished run, and resuming an unpaused one, are
refused with the server's named errors.
Check yourself
- Park a change on a wait step, cancel it and retry it: what status does
runs showreport while it waits, which states may be retried, and what doesruns retryprint that proves the cancelled attempt's record is untouched?
What you learned
- A wait or approval step holds the run at
waiting;continue,approveandrejectanswer it by step id, with the--messageaudited. cancel --reasonends a run and aborts its executing step on the runner; a finished run refuses a second cancel by name.- A retry needs an
error,cancelled,abortedorrejectedrun — a success is refused by name; it is a new run (created change …) that re-runs only the failed subtree, and--refresh-git-revmoves a git change to the branch's current head. pause/resumehold a run between steps without killing anything.- Every one of these verbs leaves an audit event with its actor and message — the record lesson 5.1 reads.
Next: lesson 3.3 — Git remotes and the identity policy (opschain learn 3.3), or back to your track (opschain learn).