Workflows
A workflow is a durable, resumable agent pipeline that a single xiv command launches and the
Smithers engine executes. This page explains what a workflow is in this repo, how the metadata
header works, how CLI commands map onto workflows, and how workflows compose.
What a workflow is here
Workflows live in pack/workflows/*.tsx. Each one is a Smithers-orchestrator JSX graph: a
render function that returns a <Workflow> element built from <Task>, <Sequence>, and
<Loop> elements, wired together with typed outputs.
const { Workflow, Task, Sequence, smithers } = createSmithers({
input: inputSchema, // zod schema for the workflow's input
issue: linearIssueSchema, // one named, typed output table per step result
output: finalizeSchema, // the workflow's primary result
});
export default smithers((ctx) => (
<Workflow name="linear-implement">
<Sequence>
<Task id="fetch-issue" output={linearIssueSchema} agent={...}>...</Task>
<Loop id="impl:loop" until={done} maxIterations={3} onMaxReached="return-last">...</Loop>
</Sequence>
</Workflow>
));The key ideas:
createSmithersdeclares typed tables. Every input and every step output is a zod schema. An agent whose output does not parse fails its<Task>instead of silently corrupting downstream state.- Tasks come in two kinds. A
<Task>with anagentprop launches a coding agent against a prompt (the.mdxfiles inpack/prompts/); a<Task>whose child is a plain async function is deterministic engine code — no model, no cost, no nondeterminism. Gates, stack-map writes, and result composition are all deterministic tasks. - Control flow is data-driven. The render function re-runs as outputs land; reads like
ctx.outputMaybe(...)andctx.latest(...)feed loop exit conditions,skipIfprops, and prompt text. A<Loop until={done}>exits when its condition computes true, andonMaxReached="return-last"means hitting the iteration cap returns the last attempt rather than failing the run. - Runs are durable. State lives in
SMITHERS_HOME; a crashed run resumes from its recorded outputs, and each workflow executes against the repo you invokedxivfrom (SMITHERS_TARGET_CWD).
The smithers-* metadata header
Every workflow file starts with a comment block the engine and CLI read as metadata:
// smithers-source: authored
// smithers-metadata-version: 1
// smithers-display-name: Linear Implement
// smithers-description: Take a Linear issue, plan it, implement it, and review ...
// smithers-tags: linear, coding, review
// smithers-aliases: lidisplay-name and description are what listings show; tags group workflows by domain; and
aliases register a short name for each workflow with the Smithers engine (the full table is at
the bottom of this page). The xiv commands themselves always address workflows by their full
name.
How CLI commands map to workflows
Each user-facing command resolves to exactly one workflow and hands it a typed input:
| Command | Workflow | Page |
|---|---|---|
xiv implement <issueId> | linear-implement | /workflows/linear-implement |
xiv ship <issueId> | linear-to-pr | /workflows/linear-to-pr |
xiv pr refine [prNumber] | pr-review-loop | /workflows/pr-review-loop |
xiv pr fix [prNumber] | pr-fix | /workflows/pr-fix |
xiv stack plan / build / push / review / amend | stack-plan … stack-amend | /workflows/stack-workflows |
xiv pr review is the exception: it is an interactive CLI flow, not a workflow — though it parses
its review agent's output against the same localReviewSchema the implement loop uses (see
/concepts/review-model).
For authoring and fast iteration, xiv check <workflow> renders a graph without running agents and
xiv dev <workflow> runs it live from the in-repo pack/ — see
/commands/authoring.
Composition: workflows as subflows
Workflows nest. linear-to-pr is nothing but two subflows — linear-implement followed by
pr-review-loop — and stack-build runs one linear-implement subflow per stack entry. The
SubflowLoose component (in pack/components/) embeds a child workflow as a step and reads the
child's result.
One contract makes this work: a workflow's primary result table must be named exactly
output. The engine populates the run result from the table with that name and no other; a
subflow consuming a child whose result table is named anything else receives undefined and fails
INVALID_OUTPUT validation. test/workflow-contract.test.ts guards this for the composed
workflows.
Alias table
| Workflow | Alias |
|---|---|
linear-implement | li |
linear-to-pr | l2pr |
pr-fix | prf |
pr-review-loop | prl |
stack-plan | sp |
stack-build | sb |
stack-push | spush |
stack-review | sr |
stack-amend | sa |
Shared conventions
- Never merge. Every workflow that can touch GitHub carries a hard system-prompt rule against
merging (
gh pr merge, auto-merge, or otherwise). Merging is a human's job, always. - Review happens locally, before push. Once a PR exists, workflows read only CI status and human comments — never a review bot. See /concepts/review-model.
- Model assignment is per-task. Mechanical steps run cheap models; reasoning-heavy steps run
capable ones, tiered by
XIV_TIER. See /concepts/model-tiers.