Skip to content

Step types

Every step has a type: Agent (“Run a coding agent or LLM with a prompt”), Shell (“Run a command: tests, linters, git, deploys”) or Approval (“Pause until a human approves”). You can switch a step’s type at any time with the type picker at the top of the step’s settings; fields for other types are kept but ignored.

UI label JSON Default Notes
Name name New agent step / Run command / Approval Display name in the graph, logs and notifications.
Step id id agent, command, approval (+ a number if taken) Stable key used in templates: {{steps.<id>.output}}. Letters, digits, -, _; must start with a letter or _. Unique within the pipeline. The UI strips other characters as you type.
— description — Optional free text. Not shown in the editor.

An agent step renders its prompt and runs it on a harness with a model, in the run’s workspace.

UI label JSON Default Notes
Harness harnessId the first enabled, detected CLI harness when the step is added (falls back to the first enabled harness, then claude-code) ● marks harnesses detected as available, ○ those that aren’t. API harnesses are labelled “(API)”. Disabled harnesses are hidden unless the step already uses one. Changing the harness clears the model.
Model model Default (the harness’s default model) Chips for the harness’s known models, a Default: … chip, and a text box for any other model id. A refresh chip pulls the latest model list when the harness supports it.

The editor warns when the chosen harness is disabled (“… is disabled, so this step will fail”) or not detected (“… is not available: …”). At run time:

  • An unknown harness fails the step with Harness "x" is not configured.
  • A disabled harness fails it with Harness … is disabled — enable it on the Harnesses page or pick another harness.
  • A CLI that isn’t installed fails with Failed to start <command>: … - is "<command>" installed and on PATH? (Settings → Extra PATH entries).

See Harnesses for what each harness does with the prompt, model and system prompt.

UI label JSON Default Notes
Prompt prompt Resolve {{issue.key}}: {{issue.title}} + blank line + {{issue.description}} Required. A template. Not shell-quoted. Generate with AI writes or revises it for you.
System prompt (optional) systemPrompt empty A template. Passed with the harness’s system-prompt flag if it has one (Claude Code and Pi use --append-system-prompt, which appends to the agent’s built-in system prompt). For CLI harnesses without such a flag it’s prepended to the prompt, separated by a --- line. API harnesses send it as the system message.

Each agent runs in isolation and only knows what its prompt says. Include the issue, the upstream outputs it needs, what not to do, and what to output. See Templates and the prompt guidance in Generating pipelines with AI.

Chips for each subagent defined in Pipeline settings → Subagents; click to attach (subagents: ["id", …]). Only harnesses with Subagent arguments configured receive them (the built-in Claude Code harness does); others ignore them, and the editor warns “… has no subagent arguments configured, so these subagents will be ignored.” See Subagents.

An agent step succeeds when the harness process exits with code 0 (and, for structured output formats, doesn’t report an error result), or the API call completes normally. The Claude API harness fails on a refusal or max_tokens stop reason. Then the verdict patterns are applied.

A shell step runs a command with a POSIX shell in the run’s workspace: /bin/sh on macOS and Linux, and Git Bash (from Git for Windows) on Windows. The same commands work on every OS.

UI label JSON Default Notes
Command command npm test Required. A template. Generate with AI can write it.
  • Exit code 0 is success; anything else fails the step with Command exited with code N.
  • Every interpolated {{value}} is shell-quoted automatically, so issue text can’t inject commands. Add | raw as the last filter to insert a value unquoted, for values that are themselves commands: {{vars.testCommand | raw}}. See Shell quoting.
  • Issue and run data is also exported as environment variables ($FACTORY_ISSUE_KEY, $FACTORY_ISSUE_TITLE, $FACTORY_BRANCH, $FACTORY_WORKSPACE, …). Using them inside double quotes is often the most readable option. See Environment variables.
  • The step’s output is stdout and stderr combined (last 20,000 characters).
  • Multi-line commands are fine; the whole text is passed to the shell.
  • Child processes inherit a non-interactive environment: CI=1 (unless already set), NO_COLOR=1, FORCE_COLOR=0, GIT_TERMINAL_PROMPT=0. There is no stdin, so anything that prompts will hang until the timeout.
Terminal window
# Open a PR (from the built-in templates)
git push -u origin HEAD && gh pr create --base {{run.baseBranch}} --head {{run.branch}} \
--title "$FACTORY_ISSUE_KEY: $FACTORY_ISSUE_TITLE" \
--body "Automated by Jimothy run #$FACTORY_RUN_NUMBER. $FACTORY_ISSUE_URL"

An approval step pauses its branch of the pipeline until a human decides.

UI label JSON Default Notes
Message shown to the approver approvalMessage Approve {{issue.key}} to continue. A template, rendered as Markdown. If empty at run time: Approve to continue.
Let the approver pick from choices.fromStep Nothing (field absent) An agent or shell step whose numbered options (### Idea 1: … headings or a 1. … list) the approver picks from. See Picking from options.
Selection choices.mode multiple multiple (one or more) or single (exactly one). Shown once a source step is set.

When the step starts, it becomes Needs approval, the approval.required notification fires (desktop by default, plus any channel subscribed to it), and the run page shows a banner with the message, an optional comment box, and Reject / Approve. See Approvals.

  • Approve: the step succeeds. Its output is the comment. With choices, the approver must pick first, and the output is the picked options’ text followed by the comment as Approver notes: ….
  • Reject: the step fails with Rejected by <name>: <comment>. Dependents are skipped and the run fails, unless the step has Continue on error or a loop-back.
  • The decision is recorded with the approver’s name (Settings → Your name, default me) and time. Templates can read {{steps.<id>.comment}} and {{steps.<id>.approvedBy}}, and with choices {{steps.<id>.selected}}, .selectedIds and .selectedTitles.
  • Other parallel branches keep running while an approval waits.
  • Rejecting with a loop-back sends work back upstream: set On failure, loop back to on the approval step, and the rejection comment is available to the re-run steps as {{steps.<approval id>.output}}. Use .output here: the loop resets the approval step and clears its comment and approvedBy, but keeps its output.

Approval steps ignore Timeout, Retries and the pass/fail patterns: they wait indefinitely and run once per pass.

UI label JSON Default Notes
Runs after dependsOn previous step (field absent) Steps this one waits for. [] = starts immediately. See DAG & parallelism.
Timeout (minutes) timeoutMinutes none Kills the attempt (whole process tree) after this long. Empty or 0 = no timeout. Not applied to approvals.
Retries on failure retries 0 Extra attempts after a failure (UI allows 0–10). Exponential backoff 2s, 4s, 8s, 16s, 30s cap. Not applied to verdict failures or approvals.
Run only if runIf empty Template; the step is skipped unless it renders truthy. Placeholder: {{vars.deploy}}.
Continue the pipeline even if this step fails continueOnError off Dependents run and the run can succeed even though this step failed.

Details and examples: Retries, timeouts & conditions.

Quality gate & feedback loop (all step types)

Section titled “Quality gate & feedback loop (all step types)”
UI label JSON Default Notes
Pass if output matches (regex) passPattern empty If set and the output doesn’t match, the step fails. Placeholder VERDICT:\s*APPROVE.
Fail if output matches (regex) failPattern empty If it matches, the step fails. Checked before the pass pattern. Placeholder CHANGES_REQUESTED.
On failure, loop back to loopBackTo — fail the step — When the step fails, re-run from this step.
Max loops maxLoops 2 How many times this step may loop back per run (UI allows 1–10). Disabled until a loop-back target is chosen.

Patterns are case-insensitive and multiline. They don’t apply to approval steps. See Loops & verdicts.

env adds environment variables for this step only, on top of global (Settings) and project env. For agent steps the harness’s own env is applied last. FACTORY_* variables can’t be overridden. See Environment variables.

{
"id": "implement",
"name": "Implement",
"type": "agent",
"harnessId": "claude-code",
"model": "sonnet",
"prompt": "Implement {{issue.key}}: {{issue.title}}\n\n{{issue.description}}\n\nPlan:\n{{steps.plan.output}}",
"systemPrompt": "You are a careful senior engineer. Keep changes minimal.",
"subagents": ["test-writer"],
"dependsOn": ["plan"],
"timeoutMinutes": 60,
"retries": 1,
"env": { "NODE_ENV": "development" }
}

All fields:

Field Type Applies to
id string all
name string all
type "agent" | "shell" | "approval" all
description string all
harnessId string (harness id, e.g. claude-code, codex, anthropic-api, simulator) agent
model string; empty = harness default agent
prompt template agent
systemPrompt template agent
subagents string[] (subagent ids) agent
command template shell
approvalMessage template approval
dependsOn string[]; absent = previous step all
timeoutMinutes number agent, shell
retries number agent, shell
continueOnError boolean all
passPattern / failPattern regex string (JSON-escaped: "VERDICT:\\s*APPROVE") agent, shell
loopBackTo step id all
maxLoops number, default 2 all
env object of strings agent, shell
runIf template all