4.2 Store, browse, run
Included from Basic tier
After this you can: try a workflow locally, store it on the server, browse the stored catalog, and run it with runs create workflow.
Before you start — tick each one:
- Lesson 4.1 — Anatomy and the step kinds (
opschain learn 4.1) - Lesson 3.1 — Your first change (
opschain learn 3.1)
In the GUI: the Workflows tab on a project page is the stored catalog; running one opens the Run form, and the run page shows the orchestration with one row per step.
Lesson 4.1 taught you to read a definition. This lesson takes the Environment Start workflow from there through the whole loop: rehearse it on your own machine, import it into the server's versioned catalog, find it again, and run it — with a plan first — so that by the end you have one workflow run on the server and know where its spawned changes went.
1. Try it locally first
Workflows have a local lane too. Files live in .opschain/workflows/,
carry an identity header workflow: <code> (any non-blank code — the
same contract the server stores, so hyphenated codes like
patch-infrastructure work unchanged in both lanes), and run by code:
opschain workflows list --local
opschain runs create workflow env_start --env dev --plan --local
opschain runs create workflow env_start --env dev --local
2026-10-12T11:20:04Z Start database pending -> queued
2026-10-12T11:20:04Z Start database queued -> running
[Start database/database:start/start] starting postgres
2026-10-12T11:20:35Z Start database running -> success
2026-10-12T11:20:35Z Confirm before the app tier pending -> waiting
…
2026-10-12T11:21:16Z App tier running -> success
run 01a14210-…-example success (1m12s)
A local workflow run executes its change steps as nested local runs, attached to your terminal like any lesson 2.1 run, and the wait step prompts you right there, as lesson 2.3 showed — the quick way to prove the orchestration before promoting it to the server.
2. Store it on the server
The server keeps workflows in a versioned, per-project catalog. The way
in is workflows import — and the common case is the file you already
have: the flat definition from section 1, exactly as it sits in
.opschain/workflows/, no wrapping required:
opschain workflows import acme -f .opschain/workflows/env-start.yaml
created workflow acme/env_start (version 1)
The file is stored verbatim as the workflow's definition. Its identity
comes from the file itself — the code from the workflow: header, the
display name from name: — and when a bare steps:-only file carries
neither, the code defaults to the filename stem and the name follows the
code. It imports published; --code, --name and --draft override any
of this from the command line:
opschain workflows import acme -f .opschain/workflows/env-start.yaml --code env_start_v2 --draft
Either way the import is validated by the server's own authoring
machinery — a published (draft: false) workflow must be runnable to
save; a draft may be saved incomplete. Re-importing an existing code
without --update is refused by name; with it, the import mints the
next version rather than editing history — versions that have run
are immutable:
opschain workflows import acme -f .opschain/workflows/env-start.yaml --update
updated workflow acme/env_start — minted version 2 from .opschain/workflows/env-start.yaml
Import also accepts the envelope format that workflows export
writes — the identity on top, the definition nested under definition:
— so an exported backup round-trips without edits:
opschain workflows export acme/env_start --out env-start.yaml
exported workflow acme/env_start to env-start.yaml
# env-start.yaml
workflow: env_start
name: Environment Start
draft: false
definition: |
name: Environment Start
steps:
- type: change
name: Start database
target: /projects/acme/environments/dev/assets/database
action: database:start
That export is your backup and change-control format; lesson 8.2 builds the discipline on it.
3. Browse the stored catalog
opschain workflows list --project acme
opschain workflows show acme/env_start
workflow: acme/env_start
id: 01a14212-…-example
name: Environment Start
description: Starts all assets in dependency order
draft: false
archived: false
latest version: 2
--definition prints the latest version's authored YAML instead of the
summary — ready to redirect to a file:
opschain workflows show acme/env_start --definition
4. Plan it, then run it
Preview before running: --plan sends the same payload the run would
and prints the step tree in two tiers, creating nothing. Tier 1 —
concrete: will-run/will-skip verdicts (when: conditions evaluated
over your overrides and inputs) and fan-out expansions rendered per
element with their resolved names, targets and actions. Tier 2 —
symbolic: ? markers for anything only execution can decide —
output-dependent spans render as their expression with the producing
step named:
opschain runs create workflow acme/env_start --override env=dev --plan
plan: workflow env_start on acme — advisory, re-evaluated at step entry
✔ Start database → /projects/acme/environments/dev/assets/database · database:start
✔ Confirm before the app tier
✔ App tier
├─ ✔ Start SAP → /projects/acme/environments/dev/assets/sap · sap:start
└─ ✔ Start Web → /projects/acme/environments/dev/assets/web · web:start
A − marker means will-skip (with the reason), ? means symbolic —
decided only at step entry.
--plan takes the same -i inputs, so input-driven when: conditions
preview concretely. Then run:
opschain runs create workflow acme/env_start --follow
created workflow 01a14220-…-example (env_start on acme) — pending
run 01a14220-…-example pending
run 01a14220-…-example pending → running
run 01a14220-…-example running → waiting
The run is created from the latest published version; --version N
pins an explicit one (including a draft, for testing). --override k=v
passes property overrides, -i key=value supplies the workflow's typed
inputs (repeat the flag; a repeated key builds a list; an unknown name,
missing required input or type/enum mismatch is refused before the run
exists), and --skip-step <name> skips steps by their workflow step
names.
That waiting is the wait step: answer it exactly as lesson 3.2 did
(runs show <run-id> -o json for the step id, then
runs continue <run-id> --step <step-id>), and --follow carries on to
success.
5. Where the work went
A workflow run's change steps spawn real changes — they appear in
opschain runs list in their own right, with their own logs and their
own retries:
opschain runs list
ID KIND ACTION TARGET STATUS CREATED DURATION
01a14226-…-example change web:start acme/environments/dev/assets/web success 2026-10-12T11:31:02Z 33.6s
01a14226-…-example change sap:start acme/environments/dev/assets/sap success 2026-10-12T11:31:02Z 40.0s
01a14221-…-example change database:start acme/environments/dev/assets/database success 2026-10-12T11:30:11Z 31.2s
01a14220-…-example workflow env_start acme success 2026-10-12T11:30:10Z 3m05s
opschain runs show <workflow-run-id> shows the orchestration tree, and
opschain logs <workflow-run-id> plays back every spawned run's lines
interleaved, labelled with the nested step's path under its workflow
step. Cancel with opschain runs cancel <run-id>; pause and release the
orchestration between steps with opschain runs pause|resume <run-id>
(a spawned change already running finishes first). What happens when a
step fails — retries, on_error:, compensation — is lesson 4.3, a lab
you run yourself.
Check yourself
- Import your definition twice: what does the second import print without
--update, and with it? Then run it and count: how many rows doesruns listgain for a workflow with three change steps, and which row is the workflow run itself? - Does
opschain workflows show acme/<workflow>list version 1, and does the run you created from it report success? The Academy'sworkflow-runexercise readsapi:workflows:create,api:workflow_runs:createandapi:workflow_runs:successon your project — created as the everyday operator you signed in as (a run executes as its creator; the bootstrap admin's every act is break-glass). - Verified version:
opschain academyruns theworkflow-runexercise against this lesson.
What you learned
- A workflow runs locally by code from
.opschain/workflows/, attached to your terminal, before it ever reaches a server. workflows import <project> -f <file>stores the flat file verbatim as version 1;--updatemints the next version, never rewriting one that has run;exportwrites the envelope that round-trips.workflows list --projectandshow [--definition]are the catalog reads.runs create workflow <project>/<code> [--plan] [-i …] --followruns the latest published version; each change step is a real change inruns list, and the workflow run's id plays back all their logs.
Next: lesson 4.3 — Error semantics (opschain learn 4.3), or back to your track (opschain learn).