Workflows

The pipeline a run follows: phases, gates, defaults, and the board automation that starts them.


A workflow says what happens, in what order, and where a person looks at it. It is stored with your project as plain YAML and edited in a visual editor — in the dashboard, the desktop app, the Mac app, or a phone — so the whole team shares one definition and every run is reproducible. Everyone can read a workflow; changing one takes the Manage workflows permission (see Roles & permissions).

What one looks like

version: 1
id: feature            # stable identity — renaming never breaks a reference
name: Feature pipeline

phases:
  - id: spec
    title: Spec
    gate: true                       # stop for review after the spec
    steps:
      - type: prompt
        prompt: Write a short spec for the task. List the files you expect to touch.

  - id: coding
    title: Coding
    gate: true
    steps:
      - { type: command, name: install, run: pnpm install }
      - { type: prompt, prompt: Implement the approved spec. }
      - { type: check, name: lint, run: pnpm lint }
      - { type: check, name: test, run: pnpm test }

  - id: merge
    title: Merge
    terminal: true
    steps:
      - { type: git, action: merge, method: squash }
      - { type: notify, to: [slack], message: Merged and shipped. }

A project keeps as many workflows as it likes, and each run picks one. With none defined at all, a built-in spec → coding → merge pipeline applies, so a fresh project runs out of the box — and the marketplace has ready-made ones to install.

Every run also carries whatever skills and MCP servers its project has picked, without a workflow having to say so.

Phases

FieldWhat it does
gate: trueWhen the phase's steps finish, the run stops and waits for a person. See Gates & reviews.
terminal: trueMarks the closing phase. The editor keeps exactly the last phase terminal, and a terminal phase cannot gate.
approvers, selfWho may approve this phase, and whether the author may approve their own work.
runnerWhere the phase executes — see Run on your own machine.
includeRestricted folders or files the phase works with, like [assets/brand/], which runs otherwise leave out. The run gets what the person who started it can open of them. See Restricted folders and files.
whenRun this phase only when a condition holds. See below.
timeoutHow long one attempt of the phase may work, first step to last — 3h. See Time limits below.
onStatus, setStatusBoard automation. See below.

Steps run in order within a phase. Ten types cover the pipeline — see the step reference.

Skipping a phase

A phase can say when it applies:

  - id: notify-release
    title: Announce
    when: branch == "release/*" && !trigger
    steps: […]

The expression reads the run's own facts at the moment the phase would start: source (manual, trigger, api…), branch, repo, trigger, and task (whether a backlog task is behind the run).

The grammar fits in a sentence: clauses joined by && or by || — one or the other, never mixed — where a clause is a bare field (task), a negated one (!trigger), or a comparison against a quoted string. Comparisons match with the same wildcards trigger filters use. A phase whose condition is false settles as skipped and the run carries on.

Identity, order, and the default

A workflow's identity is its id, which never changes. Renaming changes only the display name; every task and routing rule keeps pointing at the same workflow.

There is no magic filename either. The project's default workflow is simply the first one in order, which is what an unpinned task or a routing rule's fallback resolves to.

Defaults

defaults:
  agent: { provider: claude, model: opus-5, effort: high }
  basePrompt: Keep diffs small; follow the repo architecture docs.
  cacheWorkspace: true
  ownsStatus: true
  runner: { labels: [linux] }
  timeouts: { command: 20m, agent: 1h, phase: 3h, run: 8h }

secrets: [NPM_TOKEN, SENTRY_AUTH_TOKEN]
metrics: [sales.hourly, org/sales.daily]
reports: [weekly-summary]
FieldWhat it does
agentThe provider, model, and reasoning effort every prompt-driven step inherits. Any step can override it. See Agents & models.
basePromptThis workflow's own standing instructions for every agent in its runs, added after the organization's and the project's — never replacing them.
systemPromptThe older spelling. It used to replace the project's instructions; it is read now as the start of basePrompt, so it adds like every other layer.
cacheWorkspaceReuse one workspace across the run's phases, for install-heavy repositories. See Machines & isolation.
ownsStatusWhether a run of this workflow moves the task's board status. Defaults to true; set it false on a component workflow meant to be used as a lane.
runnerThe default machine selector for every phase.
timeoutsThis workflow's time limits, any of command, agent, phase and run. A step's or a phase's own timeout wins. See Time limits below.
secretsAt the top level, beside phases. Names of organization secrets to put in the run's environment — only what is listed resolves, and values never appear in the file.
metrics, reportsAt the top level too. The values a run writes for the dashboards: a step writes a metric as $PERACTOR_METRICS/<key>.json (a number, {"value": 12, "unit": "USD"}, a list of numbers or a table) and a report as $PERACTOR_REPORTS/<key>.md. Each is written as the step ends, and the run says so. A key not listed here is refused. org/<key> is the organization's own, which a project's run may write only after someone with Manage store creates it and names the project. If the run fails, the last good value stays and is marked stale. Known run secrets are removed from these files before transfer and again before storage. If redaction leaves an invalid metric, the previous value stays.

Trigger key, title and prompt templates, and a Git step's tag name, can read a metric: {{ store.metric.release.version }} uses the project's exact key release.version; {{ store.metric.org/release.version }} uses the organization's. Missing metrics render empty. Use | json for a structured value. A tag step reads the value when it runs, so an earlier step can write the version it tags.

Time limits

Every piece of work in a run has a limit, so a test that hangs, a server started in the foreground or an agent going round in circles gives its machine back instead of holding it until somebody notices. There are four, and each holds with nothing configured:

LimitWhat it timesUnless setSet by
CommandOne run of a command or check line, or a preview's build 1hThe step's timeout, else defaults.timeouts.command
Agent turnOne turn of the agent in a prompt, publish, plan, mail, calendar or document step, and each fix a check asks for2hThe step's timeout, else defaults.timeouts.agent
PhaseOne attempt of a phase, first step to last4hThe phase's timeout, else defaults.timeouts.phase
RunThe whole run, every phase and lane together12hdefaults.timeouts.run

Write a limit as 30s, 20m, 1h30m, 2h or 2d; a bare number is minutes. Anything from one second to seven days. A value that isn't a duration is refused when the workflow is saved.

  • Waiting isn't counted. Time at a gate, on a question, at a preview, in an interactive step's conversation, asleep at a wait step or in the queue doesn't count. A run that waits a weekend for review still has all of its limit.
  • A run's time is the clock on the wall. Two lanes working side by side for an hour use an hour of the run's limit, not two.
  • At the limit, the work stops. The step's process and everything it started are stopped. A command fails its phase. A check is red, so its agent gets the usual chance to fix it and an enclosing loop sees it like any failure. An agent's turn ends and its phase fails, with the work kept, so Resume from this step carries on from there. A phase or run that runs out fails at the step it was on.
  • People are told. The step's card says "timed out after 20m", the run's failure reason names the step, and the failure is announced like any other. A limit of 30 minutes or more says in the transcript when 80% of it is used.
  • Your organization can hold them lower. An admin sets the longest each limit may be under Organization → Run defaults, and a project can set its own lower under Settings → General, never higher. A workflow or step that asks for more is held to it.
  • The machine stops it, and Peractor makes sure. A machine that hasn't stopped a couple of minutes past the limit is stopped for it: on Peractor's machines the run's container is killed, and on a machine you paired the work is cancelled and the phase marked failed.

A server a later step needs is started in the background — see command in the step reference.

Status automation: the board drives execution

Phases can bind to board statuses, and the binding works both ways:

phases:
  - id: spec
    title: Spec
    gate: true
    onStatus: in-spec          # dragging a task here starts the workflow at this phase
    setStatus: in-spec-review  # when the phase finishes, the task moves here
    steps: […]
  • onStatus — a task dragged into that status starts this workflow at this phase. Starting mid-pipeline is fine. While the phase runs, the task shows that status, so the board always mirrors what is happening. The run is the mover's, so putting a task there takes the Start runs permission.
  • setStatus — where the task lands when the phase's work completes.

Statuses are qualified by where they come from. A bare name like in-spec means your own board; a tracker's real status is written as <connector>:<Status>. A list holds one per tracker, so a single workflow can drive tasks from several origins, each in its own vocabulary:

onStatus:
  - peractor:queued
  - "linear-a1b2c3:Todo"
setStatus:
  - peractor:building
  - "linear-a1b2c3:In Progress"

With those two fields, dragging a card is what starts, advances, and files work.

Next: the step reference · Gates & reviews