Inspecting a pipeline
Three commands that read without executing. All of them are safe to run anywhere.
flow explain
Prints the execution plan — the stages in dependency order, what each one will run, and nothing else happens.
flow explain
Pipeline: acme-api
install
$ npm ci
lint ← install
$ npm run lint
typecheck ← install
$ npm run typecheck
test ← install
$ npm run test
build ← lint, typecheck, test
$ npm run build
Stages are listed in topological order. Annotations appear only when they apply:
| Notation | Meaning |
|---|---|
← a, b | Depends on stages a and b |
$ … | A command this stage will run |
[image] | Runs in a container |
when: branch=main | Conditional on branch |
when: tag~v* | Conditional on tag |
when: dynamic | Guarded by a function — can't be resolved ahead of time |
⧉ services: … | Sidecar containers |
→ produces: … | Declared artifacts |
⇄ … | Stash operations |
↺ … | Cache restore/save |
⚠ continueOnError | A failure here won't fail the run |
A stage with no $ lines isn't a mistake — it's usually a join barrier created by
parallel(…, { join: "checks" }), which exists to be depended on rather than to run
anything.
If the pipeline sets concurrency above 1, explain says so under the name, which is
the quickest way to see the default flow run will actually use.
Machine-readable
flow explain --json
{
"version": 2,
"name": "acme-api",
"stages": [
{
"id": "install",
"name": "Install",
"needs": [],
"when": { "kind": "always" },
"timeout": null,
"continueOnError": false,
"retries": 0,
"steps": [
{
"id": "install:0",
"shell": false,
"cmd": ["npm", "ci"],
"raw": null,
"cwd": ".",
"env": {},
"timeout": null
}
],
"produces": [],
"cacheKeys": [],
"dynamic": false
}
]
}
This is the execution plan itself — the same structure the runtime executes and workers receive, not a reporting format invented for the flag. Every field is on the Execution plan reference.
The human view above is a summary — it shows the graph. --json is the complete plan:
retries, timeout, step env and cwd, and the credential list appear only there.
explain validates before printing, in both forms. An invalid pipeline prints the same
errors as flow validate and exits 1 rather than describing an order that could never
run.
flow validate
Loads the pipeline and checks it, without running anything.
flow validate
✓ pipeline.ts is valid (5 stages)
When something's wrong, you get the location, the problem, and a code:
✖ invalid pipeline (1 error)
• stages[0].needs[0]: needs references unknown stage "nope" [unknown_need]
Exit code is 1 on failure, which makes flow validate a reasonable pre-commit hook.
It catches unknown needs, dependency cycles, duplicate stage IDs, malformed
credentials and similar structural problems — everything checkable without running a
command.
The bracketed code is the thing to search for. All 27 of them, with what triggers each and how to fix it, are on Validation errors.
flow doctor
Checks the environment rather than the pipeline.
flow doctor
FlowWright doctor
✓ Node 24.15.0 — node:sqlite available (run history works)
✓ pipeline.ts loads and validates (5 stages)
✓ Docker available — container stages run isolated
⚠ not a git repo — branch/commit context won't be recorded
✓ .flowwright is writable (run history + cache)
✓ ready
| Check | Fails the run? | If it's not happy |
|---|---|---|
Node + node:sqlite | No | Upgrade to Node 24+. Below that, history commands re-exec with --experimental-sqlite. |
| Pipeline file | Yes | A missing file is a warning — run flow init. A file that fails to load or validate is a failure; run flow validate. |
| Docker | No | Container stages fall back to the host. Fine if you don't use them. |
| Git context | No | Branch and commit won't be recorded against runs. |
.flowwright writable | Yes | Check directory permissions. |
Warnings (⚠) don't affect the exit code — only the two failure cases do. doctor
exits 0 when nothing failed and 1 when something did.
doctor doesn't create anything. It checks that it could write, then cleans up
after itself, so running it in a fresh checkout won't leave a .flowwright/ behind.