Running Workflows
This guide walks through operating a workflow run day to day: starting or continuing one, checking on it, resuming it after it blocks, and abandoning one you no longer want. It assumes you already know what a workflow is; see Workflow Schema for the exact frontmatter and body syntax, and Architecture: The Workflow Engine for what happens under the hood (frozen plans, dispatch, resume-without-replay). For task/schedule-driven workflow runs — akm tasks and the OS scheduler — see Scheduling.
Start or continue a run
akm workflow run <run-id|workflows/ref> starts or continues a persisted run
and executes it until completion, failure, verification rejection,
interruption, or an explicit invocation limit. Run state is scoped to the
current project directory (nearest .akm/config.json, git root, bundle root,
or current directory), so the same workflow can run independently in separate
projects.
Every new run/start compiles source IR v1 and freezes the durable plan v4
family's current executable format (irVersion: 5) before publishing the run.
Markdown .md and GitHub-shaped .yml refs use the same freeze path.
akm workflow run workflows/ship-release --version 1.2.3
akm workflow run workflows/review --changed_files a.ts --changed_files b.ts
akm workflow run <run-id> --max-retries 2 --timeout 10m
Parameter flags must come after the target and exactly match declared
params keys. Values are coerced through each parameter's JSON Schema:
repeat an array flag, pass an object or whole array as JSON, and use a bare
boolean flag for true. There are no hyphen/underscore aliases. Parameters
are accepted only while creating a new run — a later invocation against an
active run rejects parameter flags.
--max-steps <n> leaves a partial run active after at most n steps.
--max-retries <n> retries a failed step on the same run up to n additional
times (0 through 100). --timeout <duration> bounds the whole invocation and
accepts N, Nms, Ns, or Nm; bare N is milliseconds. A timeout or
signal abort releases the run's lock without advancing the active step, so the
run remains resumable. Failed, gate-rejected, timed-out, and interrupted runs
exit nonzero.
The run freezes its plan, exact models, execution limits, parameter snapshot, and verifier selection at creation — edits to source or config do not alter an in-flight run. See Architecture: Frozen plans for why.
Check status
akm workflow status shows the full run state — all step statuses, notes,
and evidence — for a given run ID or workflow ref.
akm workflow status <run-id>
akm workflow status workflows/ship-release
# When given a ref, resolves to the most-recently-updated run in the current scope
Use this to inspect where a run is after a context window break, or to verify all steps completed cleanly before closing a PR.
--units — per-unit diagnostics. For an orchestrated run, add --units
to also list the run's journaled unit rows — each unit's id, status,
failure_reason, and any result/error diagnostic text the row carries:
akm workflow status <run-id> --units
This is a diagnostic surface, deliberately kept out of the deterministic
artifact graph. A step's promoted artifact (what steps.x.output resolves to,
and what a gate judges) keeps only a failed unit's structured failure_reason
— never the raw error text — so step evidence stays reproducible across
resumes. When you need the human-facing why behind a failure, --units
reads the unit journal directly and shows it without ever feeding that text
back into an artifact or input hash.
Child runs
A step that composes another workflow (see Workflow Schema: Child
workflows) drives that
child inline when the composing step runs. akm workflow status on a run
that composes children renders a children: tree after its steps: block:
akm workflow status <run-id>
steps:
- build [work] (completed)
- dispatch [dispatch] (completed)
children:
- ✓ 4f2b1a9c workflows/leaf [completed] (step "dispatch")
Each row shows the child's run id, its workflow ref, its status, and which
of the parent's steps spawned it; a blocked child also shows its resume
command. Nesting reflects composition depth — a child that itself composes
a grandchild shows its own indented children: rows.
Recovering a blocked child. A blocked child blocks the composing step
(and the parent run) too — akm will not resume a child for you. Resume the
child first, then resume and re-run the parent; re-driving the
parent is what advances it, because that re-enters the composing step and
drives the now-resumed child:
akm workflow resume <childRunId>
akm workflow resume <parentRunId>
akm workflow run <parentRunId>
Nested blocks (composition depth 2+). The three-command sequence above
clears a block exactly one level deep. When a grandchild blocks — a root
workflow composes a child, and that child composes the grandchild that
actually blocks — re-driving the root does not cascade down into
re-driving the still-blocked grandchild: a composing step never re-drives a
child whose own status is already blocked, so re-running the root just
re-observes the child's block and re-blocks the root the same way, without
ever reaching the grandchild. Resume every blocked run in the chain,
deepest first, then re-run only the root:
akm workflow resume <grandchildRunId>
akm workflow resume <childRunId>
akm workflow resume <rootRunId>
akm workflow run <rootRunId>
The status tree prints resume/then commands on each blocked node, but
those two commands alone only ever name that node and the root — they do not
enumerate an intermediate chain. Read the tree top to bottom and resume every
blocked row you see (deepest first) before re-running the root.
Child runs are hidden by default. akm workflow list excludes child
runs so the list stays a view of the runs you started; pass --children to
include them:
akm workflow list # child runs excluded
akm workflow list --children # child runs included
A child run id always works directly, listed or not — akm workflow status <childRunId>, resume, abandon, and run all operate on it exactly like
any other run.
List runs in scope
akm workflow list shows workflow runs in the current scope.
akm workflow list # All runs in this scope (any status)
akm workflow list --active # Only status=active (executable) runs
akm workflow list --ref workflows/ship-release # Runs for a specific workflow
--active filters to runs whose status is exactly active — currently
executable work. A blocked run (parked awaiting a human akm workflow resume) or a failed/completed run is not active and is excluded, so a
script that treats --active output as runnable never picks one up. Blocked
runs remain listed by the unfiltered akm workflow list with their blocked
status.
Example: see what is in flight
akm workflow list --active
# → lists runs by workflow ref, status, currentStepId, and updatedAt
Resume a blocked or failed run
akm workflow resume <run-id>
Flips a blocked or failed run back to active. Completed runs cannot be
resumed. Use akm workflow list to find runs by status. Once resumed,
akm workflow run <run-id> continues it — units already journaled as
completed are skipped, not re-run (see
Architecture: Resume skips completed units).
A resume consumes the frozen plan and journaled attempts; it does not re-read
configuration or the index, and reads the authored workflow only to warn once
when it changed since the run was frozen (the run continues on the frozen
plan; akm workflow run <ref> --new picks up the edit). A stored plan this akm
cannot decode is abandoned with a message naming how to start a new run.
Abandon a run
akm workflow abandon <run-id>
Marks a run failed so it stops counting as active. This only changes the
run's status — resume can still reopen it later if you change your mind.
Follow a run's events
There is no akm workflow watch (0.9.0: dropped — a foreground polling
daemon in a one-shot CLI). akm log --run <run-id> reads the same
workflow_* / workflow_unit_* events from the general append-only events
stream: --since '@offset:<id>' gives a durable row-id cursor a cooperating
process can poll from, in place of watch --stream's in-process loop.
akm workflow run <run-id> & # engine in one shell
akm log --run <run-id> --since '@offset:0' # backlog so far
akm log --run <run-id> --since '@offset:<nextOffset>' # poll for more, from the prior call's nextOffset
Event metadata is ids/status/enums only — never workflow-authored content — so following a run's events is safe to pipe into logs or dashboards.
Security: workflow sources are executed code
Workflow steps that include shell commands run with the full filesystem and
network access of the user invoking akm workflow run — same as if the user
had typed those commands in their shell. There is no sandbox and no separation
between trusted and untrusted workflows. An exec unit's environment is
narrowed to an allowlist by default
(Workflow Schema),
which bounds accidental exposure of unrelated credentials — but a command that
runs at all can still read those credentials off disk, so it is a hygiene
boundary, not a containment one. AKM
directly orchestrates workflows as a defined execution surface — it does not
blindly execute arbitrary indexed content — but that orchestration still runs
the workflow's own declared shell commands with your full access once you
choose to run it.
This is by design: a workflow is a runbook authored by you or by a bundle maintainer you trust. The flexibility of "run any shell command, read any file, hit any network" is what makes workflows useful as automation.
The consequence is that you should treat workflow sources the same way you treat package dependencies:
- Only add workflow sources you trust.
akm bundle add github:<some-user>/<bundle-repo>followed byakm workflow run workflows/<their-thing>is functionally equivalent to piping a stranger's bash script into your shell. Read the workflow file first (akm show workflows/<name>) before running it. - Audit before run for any workflow that touches secrets, deploys to
production, or writes outside the project tree. Read the
env:bindings a workflow declares, and read itsexec.pass_envlines. The durable v4 family (irVersion: 5) rejectsinherit_env. Authors must use exact named bindings andpass_envnames. - Pin known-good versions when adding workflow sources from a registry
or git remote (
akm bundle add github:owner/repo#v1.2.3), and update deliberately rather than viaakm bundle update --all. A trusted workflow source can become hostile if its upstream is compromised. - Workflow steps cannot escape this trust model by being labeled
dryRunorinteractive— those flags affect bookkeeping, not execution.akm workflow statusis read-only;akm workflow runexecutes configured units with your process's access.
If you operate a CI runner or shared host where untrusted workflows might be executed, scope the process: a dedicated user account with no secrets in its environment, ephemeral working directory, and a network/filesystem allowlist enforced outside akm.
See also
- Workflow Schema — exact frontmatter, refs, gates, and outputs syntax
- Author's Guide: Writing Workflows — writing and testing a workflow definition
- Architecture: The Workflow Engine — persistence, dispatch, and resume internals
- Scheduling — running akm tasks (including workflow runs) through the OS scheduler
- CLI Reference — full flag documentation for all
workflowsubcommands