> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mutagent.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Triggers and routines

> Which event starts what: board, CI, schedule and CLI events, the triggers that match them, and how stages chain.

> [!NOTE]
>
> **Early Access.** Commands and behavior can change between releases.

A trigger is one entry in `triggers:` with three keys: a `name`, an `on` that matches an event, and
a `run` that says what to start. `run` is either a **stage**, which starts a Factory run, or a
**command**, which runs one agent session once.

```yaml theme={null}
triggers:
  - name: pickup
    on:
      source: board
      event: label
      label: 'factory:pickup'
    run:
      stage: plan
  - name: weekly-arch-review
    on:
      source: schedule
      cron: '0 8 * * 1'
      tz: Europe/Berlin
    run:
      command: /arch-review
      agent: architect
```

## Event → trigger → what runs

The diagram below shows which events start what.

Event sourceTrigger in factory.yamlWhat runsBoard: label, assign, status→`source: board`→Run at `plan`GitHub: CI failed→`source: github`→Run at `build`Schedule (cron)→`source: schedule`→One command, onceCLI: `triggers fire`↗

| Event | Trigger | What happens |
| - | - | - |
| A label is added to a board item | `board` · `label` → `plan` | A run starts at `plan`. |
| A board item is assigned | `board` · `assigned` → `plan` | A run starts at `plan`. |
| A board item moves to a status | `board` · `status` → `plan` | A run starts at `plan`. `triage` is refused. |
| CI fails on a pull request branch | `github` · `ci-failed` → `build` | The run that owns the branch goes back to `build`. On a branch no run owns, a new run starts at `plan`. |
| The clock reaches a cron time | `schedule` → `command` | One agent session runs the command once. No run, no gate. |
| You run `triggers fire <name>` | `schedule` → `command` | The same, now. |

The trigger column reads `on.source` · `on.event` → `run.stage` or `run.command`.
Narrow a match with filters under `on`: `label`, `assignee` or `status` for board events, `branch`
for CI, and `repo` for both. `branch` takes a glob such as `factory/*`. A schedule trigger takes `cron` and `tz` instead. The
[reference](https://docs.mutagent.io/factory/reference/factory-yaml.md#triggers) lists every key.

A board trigger only starts work on an item that has **every** pickup label and no hold label, and
never on an item in `triage`. So assigning an unlabelled item to `factory` does nothing. The
number of open runs is capped by `limits.maxActiveRuns`; a run over the cap waits in a queue.

### Defaults

If your file has no `triggers` key, these three apply:

| `on` | `run` |
| - | - |
| `{ source: board, event: label, label: <pickup label> }`, one per pickup label | `{ stage: plan }` |
| `{ source: board, event: assigned, assignee: factory }` | `{ stage: plan }` |
| `{ source: github, event: ci-failed, branch: 'factory/*' }` | `{ stage: build }` |

Once you write a `triggers` list, it replaces the defaults: copy the ones you want to keep.
`triggers: []` turns off every trigger from the file.

## How stages chain

Triggers only **start** work. After that, a run moves from stage to stage on its own, and waits for
you at two gates. You open each gate with a label, never by typing a command.

plan`/spec`→You approve the plan`factory:approved`→build`/implement`↓ ↑ reworkreview`/code-review`←You approve the merge`merge-ready`←land`/ship`

Review sends work back to build when it finds problems or CI fails.

| Stage | Who runs it | What happens next |
| - | - | - |
| `plan` | `architect`, `/spec` | The plan is posted on the item, and the run waits for you. |
| Plan gate | you | `labels.approvePlan` → `build`. `labels.revisePlan` → `plan` again, with your notes. |
| `build` | `developer`, `/implement` | One lane per sub-item opens a pull request, then `review`. |
| `review` | `reviewer`, `/code-review` | It approves, or sends the work back to `build`. |
| Merge gate | you | `labels.approve` → `land`. `labels.revise` → `build` again, with your notes. |
| `land` | `orchestrator`, `/ship` | The reviewed head is merged. |

A run starts at `plan` from a `run.stage: plan` trigger, from `architech factory runs start`, or
when CI fails on a branch no run owns. A `run.stage: build` trigger sends the run that owns the
branch back to `build`.

The label names above are the defaults; see [label roles](https://docs.mutagent.io/factory/reference/factory-yaml.md#labels).
How many gates apply depends on `policy.autonomy`, and a change to a protected path always waits
for merge approval. See [approvals and guards](https://docs.mutagent.io/factory/configuration.md#approvals-by-label).
`architech factory runs show <run>` lists each stage with its command.

## Routines

A **routine** is a `source: schedule` trigger. When the clock reaches its time, one agent session
runs one command, once. It creates no Factory run and passes no gate. The session appears in your
workspace's sessions list.

```yaml theme={null}
triggers:
  - name: weekly-arch-review
    on:
      source: schedule
      cron: '0 8 * * 1'  # Mon 08:00
      tz: Europe/Berlin
    run:
      command: /arch-review
      agent: architect
```

| Key | Allowed values |
| - | - |
| `on.cron` | A five-field cron expression: minute, hour, day of month, month, day of week. |
| `on.tz` | An IANA time zone, such as `Europe/Berlin` or `Etc/UTC`. |
| `run.command` | A slash command a routine may run: `/arch-review` (architect) or `/triage` (orchestrator). |
| `run.agent` | Optional. The agent that runs the command; leave it out to use the command's owner. |

A schedule trigger has no `event`, and it can't start a stage: `run.stage` is refused for it.

Push the file to create or change the routine. Removing the trigger from the file and pushing
removes the routine. Disabling the trigger pauses it.

### Run a routine now

Test a routine, or run it outside its schedule:

```bash theme={null}
architech factory triggers fire \
  weekly-arch-review --json
```

`fire` takes the trigger's `name` from `factory.yaml`. It reads the Factory from the local file;
pass `--factory <id>` to pick another one. It works only for a schedule trigger, and only while the
trigger and its Factory are enabled. Each call is a new firing. To retry a firing without running
it twice, pass the same `--key <key>` both times.

## Test an event trigger

To check which trigger an event would match, without starting anything:

```bash theme={null}
architech factory triggers list \
  --factory <factory-id> --json
architech factory triggers test \
  <trigger-id> --event-file event.json \
  --json
```

An event file holds JSON such as `{"kind":"board.label","data":{"label":"factory:pickup"}}`.
A disabled trigger never matches.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.