> ## 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.

# factory.yaml reference

> Every key of the file with its allowed values, label roles, triggers, refused keys, and and how to create the file.

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

The file lives at `.architech/factory/factory.yaml`. Both onboarding and `architech factory init` write this one format, `architech/factory/v2`.

## Example

A Cloud Factory for one GitHub repository. Replace the owner, project number and repository with
yours. [Configure your Factory](https://docs.mutagent.io/factory/configuration.md#a-complete-file) has a longer example with a
comment on every key.

```yaml theme={null}
apiVersion: architech/factory/v2
kind: Factory
metadata:
  name: factory
mode: cloud
board: main
boards:
  main:
    provider: github
    ref: { owner: acme, project: 2 }
repos:
  - name: acme/web
    defaultBranch: main
labels:
  pickup: ['factory:pickup']
  approvePlan: 'factory:approved'
  revisePlan: 'factory:changes'
  approve: merge-ready
  revise: 'merge:changes'
  hold: 'merge:hold'
  loosen: 'merge:yolo'
  pr: [ci]
  layers: []
  origin: { user: 'origin:user', agent: 'origin:agent' }
issues:
  ontology: "[Domain]:[FeatureName]:[Repo]:[Issue-ID]"
policy:
  merge:
    preset: as-ready
limits:
  maxActiveRuns: 4
triggers:
  - name: pickup
    on: { source: board, event: label,
          label: 'factory:pickup' }
    run: { stage: plan }
  - name: ci-fix
    on: { source: github, event: ci-failed,
          branch: 'factory/*' }
    run: { stage: build }
  - name: weekly-arch-review
    on: { source: schedule, cron: '0 8 * * 1',
          tz: Europe/Berlin }
    run: { command: /arch-review, agent: architect }
```

## Keys

These are all the top-level keys. Any other top-level key is refused.

| Key | Allowed values | Default |
| - | - | - |
| `apiVersion` | `architech/factory/v2` | required |
| `kind` | `Factory` | required |
| `metadata.name` | Lowercase letters, digits and dashes, up to 63 characters; not shaped like a UUID. The Factory's identifier. | required |
| `metadata.displayName` | Text shown in the app. | `metadata.name` |
| `metadata.description` | Text. Stored, not used by Cloud. | none |
| `mode` | `local` or `cloud`. Onboarding writes `local`; pushing sets `cloud`. | `cloud` |
| `default` | `true` or `false`. Stored, not used by Cloud. | none |
| `board` | The name of one entry in `boards`. Required when there is more than one. | the only board |
| `boards.<name>.provider` | `github` or `linear` on Cloud. A local file can also name `jira` or `none`. | required |
| `boards.<name>.ref` | The board's address. GitHub: `{ owner: acme, project: 2 }`. Linear: `{ team: ENG }`. | required |
| `boards.<name>.statusMap` | Canonical status → your column name. Partial maps are fine. See [status mapping](#status-mapping). | Cloud's default map |
| `boards.<name>.poll` | A duration of at least `30s`, such as `60s`. | `60s` |
| `repos[].name` | `owner/repo`. | the repository you pushed from |
| `repos[].defaultBranch` | A branch name. | `main` |
| `repos[].zones.protected` | Path globs, such as `["src/auth/**"]`. Changes there always wait for merge approval. | `[]` |
| `labels.*` | Label roles. See [labels](#labels). | see below |
| `issues.ontology` | An issue naming pattern. Stored, not used by Cloud. | none |
| `policy.merge.preset` | A preset name: onboarding writes `standard`. Every preset waits for a person's approve or loosen label. | `as-ready` |
| `policy.autonomy` | `calibrating`, `standard` or `autonomous`. Not the same as the merge preset. | `calibrating` |
| `framework.version`, `framework.digest` | The framework release Cloud runs. Push adds your installed release's pin if there is none, and keeps an existing one. | added by push |
| `sessions` | `per-stage`, `lead-per-item` or `linked-leads`. | `per-stage` |
| `agents.roles.<role>` | `harness`, `model`, `effort` and `environment` for `orchestrator`, `architect`, `developer`, `llm-whisperer` or `reviewer`. See [choose models per role](#choose-models-per-role). | your workspace's default model |
| `limits.maxActiveRuns` | A positive integer. Runs waiting for you count; more runs wait in a queue. | `2` |
| `limits.budget.perRunUsd`, `limits.budget.perDayUsd` | A positive number, or `null` for no dollar cap. | built-in caps |
| `triggers` | A list of triggers. See [triggers](#triggers). | three defaults |

Push lists the stored-but-unused fields as **accepted, not applied**: `metadata.description`,
`default`, `issues`, every board except the selected one, and `mode` (pushing, not this field,
makes a Factory a Cloud one).

## Status mapping

> [!NOTE]
>
> Map only the columns you have. `dropped` is a canonical status too. Unknown keys, blank values
> and empty maps are refused.

```yaml theme={null}
boards:
  main:
    provider: github
    ref: { owner: acme, project: 2 }
    statusMap:
      triage: Triage
      in_progress: In Progress
      awaiting_approval: In Review
      awaiting_merge: In Review
      done: Done
```

* An unmapped status is not mirrored to the board.
* If you leave `statusMap` out, Cloud uses its default map. That is not the same as mapping
  nothing.

When `statusMap` is left out, Cloud uses:

| Canonical statuses | Board column |
| - | - |
| `triage` | `Triage` |
| `planning`, `approved`, `in_progress` | `In Progress` |
| `awaiting_approval`, `in_review`, `awaiting_merge` | `In Review` |
| `done` | `Done` |
| `on_hold` | `On-Hold` |
| `dropped` | `Dropped` |

See [all canonical statuses](https://docs.mutagent.io/factory/reference/statuses.md).

## Labels

Each key under `labels` is a **role**; its value is the label name on your board or repository.
Agents use these names instead of inventing their own. Any other key under `labels` is refused.

| Role | Default | What it does |
| - | - | - |
| `pickup` | `['factory:pickup']` | A person adds it to mark an item for work. A board item needs every pickup label before a run starts. |
| `approvePlan` | `factory:approved` | A person adds it to approve the waiting plan; build starts. Removed after reading. |
| `revisePlan` | `factory:changes` | A person adds it after a comment; planning runs again with that comment as notes. Removed after reading. |
| `approve` | `merge-ready` | A person adds it to the pull request to approve the merge. Stays until the merge; removed when new commits arrive. Onboarding writes `merge:approved`. |
| `revise` | `merge:changes` | A person adds it to the pull request after a comment; the work goes back to `build` with that comment. Removed after reading. |
| `hold` | `merge:hold` | Stops pickup and merge, whoever added it. Wins over every other label. |
| `loosen` | `merge:yolo` | A person adds it to allow a merge without `approve`. Never past `hold`. |
| `pr` | none | The Factory adds these to every pull request it opens, for example a label your CI needs. Up to 100 names of 50 characters. A label missing from the repository is skipped, never created. |
| `layers` | `[]` | A list of label names. Cloud doesn't read it. |
| `origin.user`, `origin.agent`, `origin.factory` | `origin:user`, `origin:agent` | Who created an issue or pull request. Factory pull requests get `origin.factory`, else `origin.agent`. No origin label means a person created it. |

"A person" means a GitHub user with write access to the repository. A label added by a bot, an app
or the Factory doesn't approve anything. See [approvals by label](https://docs.mutagent.io/factory/configuration.md#approvals-by-label).

## Triggers

`triggers` is a flat list. Each trigger has exactly three keys:

| Key | Allowed values |
| - | - |
| `name` | Unique text within the file. `architech factory triggers fire` uses it. |
| `on.source` | `board`, `github` or `schedule`. |
| `on.event` | `board`: `label`, `assigned` or `status`. `github`: `ci-failed`. `schedule`: none. |
| `on.label` | With `event: label`: the label name. |
| `on.assignee` | With `event: assigned`: the assignee's login. |
| `on.status` | With `event: status`: a status. `triage` is refused. |
| `on.branch` | With `ci-failed`: a branch or glob, such as `factory/*`. |
| `on.repo` | With `board` or `github`: `owner/repo`, to match one repository of `repos`. |
| `on.cron` | With `schedule`: a five-field cron expression. |
| `on.tz` | With `schedule`: an IANA time zone, such as `Europe/Berlin`. |
| `run.stage` | `plan`; or `build` for `github` `ci-failed`. Starts a Factory run. |
| `run.command` | With `schedule`: a slash command, `/arch-review` or `/triage`. Runs one session once. |
| `run.agent` | Optional, with `run.command` only: the agent that runs it. Default: the command's owner. |

Give `run.stage` or `run.command`, not both. A `schedule` trigger needs `run.command`.
Without a `triggers` key, three defaults apply; `triggers: []` turns off every trigger from the
file. See the [Triggers and routines](https://docs.mutagent.io/factory/triggers.md) for what each event starts, and for routines.

## Refused keys

`architech factory validate` and push refuse these keys and name where the setting lives instead.

| Key | Message | Use instead |
| - | - | - |
| `triggers[i].guards` | `not part of the language; who and how many live in labels: and limits:` | `labels.pickup` and `labels.hold` decide which items start; `limits.maxActiveRuns` caps how many. |
| `triggers[i].scope` | `not part of the language; repositories are repos: (filter with on.repo); what a command may change is fixed by the command` | `repos`, and `on.repo` to match one repository. |
| `triggers[i].mode`, `triggers[i].entry` | `every trigger is one-shot` | Leave it out. |
| `triggers[i].run.transition` | `a waiting run moves only on its gate's signal (labels.approvePlan, labels.approve)` | The gate labels. A trigger never moves a waiting run. |
| `run.stage` and `run.command` together | `give stage or command, not both` | One of them. |
| `run.agent` without `run.command` | `is only valid with command` | Add `run.command`, or remove `run.agent`. |
| `run.stage` on a `schedule` trigger | `schedule and slack triggers require command` | `run.command`. |
| `on.event` on a `schedule` trigger | `schedule has no event; give cron and tz` | `on.cron` and `on.tz`. |
| `on.status: triage` | `triage is parked and cannot start a run` | Another status. |
| `labels.<other>` | `is not a label role (pickup, approvePlan, approve, hold, loosen, pr, layers, origin, revisePlan, revise)` | One of the listed roles. |
| An unknown top-level key | `is not a key of …`, followed by the allowed keys | A key from [keys](#keys). |

## Choose models per role

There are five framework roles: `orchestrator`, `architect`, `developer`, `llm-whisperer` and
`reviewer`. A role you don't list runs on the `architech` harness with your workspace's default
model. Override a role in the file:

```yaml theme={null}
agents:
  roles:
    developer:
      harness: architech          # architech | claude-code | codex
      model: "provider:model"     # a model on your workspace's provider list
      effort: medium              # off | minimal | low | medium | high | xhigh
    reviewer:
      model: "provider:model"
```

Use the exact `provider:model` names your workspace lists. The dry run checks each role's model,
Environment and effort level, and reports any refusal before anything is pushed. You can't rename
or replace a framework role. `agents.repoAgents.allow` lists your repository's own agents, never a
framework role.

To list every field with its type and default, ask the installed CLI:

```bash theme={null}
architech factory schema --json
architech factory schema --remote --json
```

The first prints the schema offline. `--remote` fetches Cloud's copy and needs sign-in.

## Validate and synchronize

```bash theme={null}
architech factory validate .architech/factory/factory.yaml --json
architech factory push --dry-run --json
architech factory push --json
architech factory pull --dry-run --json
```

With no argument, push and pull use `.architech/factory/factory.yaml`. A factory name picks
`.architech/factory/factory-<name>.yaml`; you can also pass a path. Push and pull can commit
changes, so read their result before other Git work.

See [configuration drift and warnings](https://docs.mutagent.io/factory/operating.md#keep-configuration-in-step).


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