opschain approval-policies convert-step-gates
packs:change-governance
opschain approval-policies convert-step-gates [--dry-run]
Inventories every step-level requires_approval_from: wait gate in the
stored workflow catalog and, on request, creates the equivalent approval
policy (ChangeGovernanceService.ConvertStepApprovalGates). The step gate
is the in-YAML approval: a wait step naming user_names/ldap_groups parks
the run mid-flow until any one named approver releases it. It stays
supported and enforced — but governance-grade approval lives in approval
policies on the estate paths, not in workflow YAML, so this verb is the
operator path from one to the other: it scans every stored workflow version,
reports every gate by name and converts the current ones into policies
through the same store the policy write verbs use.
Converting never edits or removes workflow YAML. The step gate keeps
enforcing exactly as before — a converted workflow holds twice (the policy
before the run starts, the gate at the step) until the workflow's authors
delete the requires_approval_from: keys from their own repositories at
their own pace. The report ends with that reminder.
Each converted gate becomes a policy shaped for the workflow's runs:
- the policy's scope and path pattern are the owning project's path, and its action pattern is the workflow's code — exactly the identity a workflow run is matched on, so the policy holds that workflow's runs for approval before they start;
- the gate's
user_namesandldap_groupsbecome the policy's named approvers, with quorum 1 and self-approval allowed — the step gate is satisfied by any one named approver, the run's creator included when named; - two steps gated on the same approver set propose one policy (the second
reports
already_convertedagainst it), and a different approver set converts under its own deterministic name.
The report names every gate (project/workflow v<version> step "<name>",
approvers included):
converted— the policy was created (name, id and shape in the row);already converted— a re-run creates nothing twice: a policy with the converted shape already exists, whatever it has since been renamed to;superseded— the gate sits on a draft or on a version a newer published version supersedes; only the latest published version converts, and the gate keeps enforcing if that older version is run explicitly;unconvertible— left to recreate by hand: property-placeholder approver names ({{ … }}, resolved per run against project properties) and gates on archived workflows.
The verb requires a PostgreSQL-backed server holding the
packs:change-governance entitlement, the change-governance determination
(updatable on /governance/change, the root scope — the conversion writes
policies across the whole estate) and a stepped-up session. A writing run is
audited as api:step_approval_gates:converted; --dry-run produces the
same report without writing anything and is not audited.
Estate usage is also visible without running the verb: whenever a stored
workflow version carrying the gate is composed for a run or a plan, the
server records one throttled warn:workflows:deprecated_approval_gate
event naming the workflow, version and gate-carrying steps (at most one per
version per day), so opschain events answers where the key is still in
use.
Flags
| Flag | Meaning |
|---|---|
--dry-run | Report what would convert without writing any policy. |
-o json / -o yaml emit the typed report in its protojson wire shape.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Report produced (a writing run may have created policies; no step-level requires_approval_from gates found in the stored workflow catalog when no stored version carries the key). |
| 1 | The conversion failed — the error names the server's Connect code and message, including the packs:change-governance funnel refusal, the missing governance determination and the step-up refusal. |
| 2 | Usage error, including a missing server target. |
Examples
$ opschain approval-policies convert-step-gates --dry-run
[would convert] /projects/web/release v3 step "approve" approvers=alice, dba -> policy "Converted step approval gate /projects/web/release 1a2b3c4d" (path /projects/web, actions release)
[superseded] /projects/web/release v2 step "approve" approvers=old-approver — version 2 is superseded by a newer published version — only the latest published version converts; the gate keeps enforcing when this version runs
1 convertible, 0 already converted, 1 superseded, 0 unconvertible
the step gates stay enforced: converting never edits workflow YAML — delete requires_approval_from keys at your own pace
$ opschain approval-policies convert-step-gates
[converted] /projects/web/release v3 step "approve" approvers=alice, dba -> policy "Converted step approval gate /projects/web/release 1a2b3c4d" (01993…, path /projects/web, actions release)
...