# 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](../reference/workflow-schema.md) for the exact frontmatter
and body syntax, and
[Architecture: The Workflow Engine](../architecture/workflow-engine.md) 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](scheduling.md).

## 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.

```sh
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](../architecture/workflow-engine.md#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.

```sh
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:

```sh
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](../reference/workflow-schema.md#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:

```sh
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:

```sh
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:

```sh
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:

```sh
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.

```sh
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**

```sh
akm workflow list --active
# → lists runs by workflow ref, status, currentStepId, and updatedAt
```

## Resume a blocked or failed run

```sh
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](../architecture/workflow-engine.md#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

```sh
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.

```sh
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](../reference/workflow-schema.md#the-childs-environment-is-an-allowlist)),
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 by `akm 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 its `exec.pass_env` lines.
  The durable v4 family (`irVersion: 5`) rejects `inherit_env`. Authors must
  use exact named bindings and `pass_env` names.
- **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 via `akm 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
  `dryRun` or `interactive` — those flags affect bookkeeping, not execution.
  `akm workflow status` is read-only; `akm workflow run` executes 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](../reference/workflow-schema.md) — exact frontmatter,
  refs, gates, and outputs syntax
- [Author's Guide: Writing Workflows](../guides/author-workflows.md) —
  writing and testing a workflow definition
- [Architecture: The Workflow Engine](../architecture/workflow-engine.md) —
  persistence, dispatch, and resume internals
- [Scheduling](scheduling.md) — running akm tasks (including workflow runs)
  through the OS scheduler
- [CLI Reference](../reference/cli.md) — full flag documentation for all
  `workflow` subcommands
