Step reference

The nine step types, what each one is for, and every option they take.


Steps run in order inside their phase. Nine types cover a pipeline. The three that drive an agent — prompt, publish, and preview — each accept a per-step agent and personas.

prompt — tell the agent what to do

- type: prompt
  name: Implement            # what the step is called while it runs
  prompt: Implement the approved spec.
  personas: [engineer, reviewer]
  allowQuestions: true       # may stop and ask you
  interactive: true          # stops when done and waits for you
  remember: false            # don't carry its conclusions forward
  agent: { provider: claude, model: opus-5, effort: high }

name is what the step is called wherever the run is read — the live transcript, the plan of what is still to come, a loop's target picker. Give one to every prompt in a phase that has more than one. Without it each card reads "Prompt", which says an agent did something but never what.

Peractor puts the task in front of the prompt — its title, description, and attachment paths — so write prompts about "the task", not a restatement of it. The closing message of a phase's last prompt becomes the phase summary used in the pull request and commit message.

Carrying what a step worked out

Every prompt step is a fresh agent session. The next step starts with none of this one's reasoning, and the only thing that crosses on its own is what was committed.

So by default a prompt step ends with a short note of what it decided and why, and every later step of the run — later phases included — is handed that note. Set remember: false on a step whose thinking is not worth passing on. Answers you give to a step's questions are always carried forward either way.

What the agent was really sent

The run's transcript shows it, split into its parts, each openable on its own: your organization's instructions, the project's, the workflow's, the persona, Peractor's own guardrails, the task, what the run has remembered, and this step's prompt.

Stopping for a person

Two different things, and the difference is whose idea it is:

  • allowQuestions — the agent decides it is blocked and asks. See Mid-run questions.
  • interactive — the step stops when its prompt is done and waits for you, and anyone who can see the run can talk to it. See Talking to a running step.

check — verification that has to pass

- { type: check, name: test, run: pnpm test }

Runs a command in the workspace. If it fails, the output goes back to the preceding prompt's agent with an instruction to fix the cause — not to disable the check — for up to two attempts. Still red after that, the phase fails and stops at its gate for a person.

A check needs a prompt step before it in the same phase. Somebody has to be there to fix it.

command — a step that just runs

- { type: command, name: install, run: pnpm install }

Same shape as a check, different contract: a failure here is yours to fix, not the agent's, so the phase halts rather than asking an agent to repair your infrastructure.

Commands run at the workspace root with the run's environment — the secrets the workflow declares, and the git token as GITHUB_TOKEN.

git — pull requests, merges, tags

- { type: git, action: open-pr, draft: true }
- { type: git, action: ready-pr }
- { type: git, action: merge, method: squash }
- { type: git, action: tag, tag: "v{{ date }}", skipIfDone: true }

Branching and committing are automatic — phases commit and push when they complete — so git steps are for the things you want to declare: opening a pull request, undrafting it, merging, tagging.

Each runs exactly where you put it. The work so far is committed and pushed first, then Peractor performs the action with its own credentials. So "open a draft pull request early, keep working, undraft at the end" behaves as written. See Git & credentials.

tag takes the name to create, as a template: v{{ date }}, {{ task.key }}, {{ run.title }}, {{ run.branch }}, {{ run.id }}. Whatever it renders to is repaired into a name git accepts. The tag lands on the base branch as it stands when the step runs — which is the merge commit when a merge step comes first, and that is the order to write them in.

A version number is deliberately not offered: it lives in your repository, so have a prompt step read it and write the tag you want.

skipIfDone — "skip if it is already done". A phase that is re-run reaches its git step a second time and asks for something that already happened: the branch is already merged, the tag already exists. Off, that fails the phase on the one step with nothing left to do. On, the step says so and the run carries on. It never forgives a real failure — a refused token, a missing repository, or a merge conflict fails exactly as before.

notify — tell the team

- type: notify
  to: [slack, telegram]
  message: Spec is ready for review
  compose: |          # optional — write the message from what the run did
    Summarize what changed and what the reviewer should look at.

Posts through the notifiers you name, threaded under the run's own thread. Channels come from each notifier's configuration — the step never picks one. A name is either a specific notifier's id or a provider name (slack, discord, telegram, teams, googlechat, mattermost, webhook).

Notify steps send at the end of the phase, only when it succeeded, and an outage never fails a run.

compose is a brief for writing the message rather than posting message verbatim. Peractor runs it against what the run actually did and posts the result. message stays required and becomes the fallback, because a channel that goes quiet is worse than a channel that gets the plain sentence you already wrote.

publish — produce artifacts for review

- type: publish
  to: [issue, pr, notify]
  prompt: Render each design/ui page to a PNG with a headless browser you install.

A prompt whose job is to produce reviewable artifacts — screenshots, diagrams, rendered pages — and list them in a manifest. When the phase succeeds, Peractor delivers them to the tracker issue, the pull request, and/or your notifiers. See Artifacts & previews.

preview — serve the result at a URL

- type: preview
  name: live preview
  path: site                          # where it is built and served from
  build: npm ci && npm run build      # optional, run first
  start: npx serve -s dist -l $PORT   # must listen on $PORT

Peractor runs the build line, then the start line, and the run stops with the result reachable at a per-run URL. Reviewers click around the real thing, then Continue or Request changes.

Serve on $PORT: Peractor picks the port and passes it in. A server on a fixed port of its own is one nothing can reach.

Because the two lines are written down, the preview can be started again later from the same commit — a button rather than a re-run. See Artifacts & previews.

A preview written before those fields existed carries a prompt instead, and is still served that way: an agent is asked to start a server, and whatever it chose was never written down — so that preview cannot be reopened once the phase is over. Add a start line and the prompt stops being read.

loop — repeat until it is actually done

- type: prompt
  prompt: Implement the task with tests.
  id: impl                   # the step to come back to
- { type: check, name: tests, run: pnpm test }
- type: loop
  to: impl
  until: checks-pass         # checks-pass, agent-verdict, or both
  untilAny: false            # true = either one is enough
  max: 4                     # default 3
  onMax: fail                # fail (default) or continue

When the loop is reached and its condition is unmet, execution jumps back to the target step and runs the section again, threading the failing output into the next attempt.

  • checks-pass — every check and command in the looped section went green.
  • agent-verdict — the section's last prompt declared it done.
  • List both and the loop runs until both hold. Add untilAny: true and either one alone is enough.

At max, fail stops the phase at its gate and continue moves on regardless. Loops cannot nest or overlap.

Looping back to an earlier phase

- type: loop
  phase: build
  to: implement
  until: checks-pass
  max: 2

Name a phase and the loop reaches back into an earlier one — "if the checks in Verify failed, go back to Build's implement step and do the whole thing again".

It must be an earlier phase, and it costs what a phase boundary costs: the work so far is committed to the branch and the re-entered phase starts over from there, exactly as a request for changes does. The conditions are still read from the phase that reached the loop.

workflows — run other workflows side by side

- type: workflows
  lanes:
    - workflow: android-feature
    - workflow: ios-feature

A fan-out: each lane runs another of this project's workflows, at the same time, on its own branch. See Running workflows side by side.