# Rules and checks

In this guide you will write the project's rules file, `.yard/rules.toml`: the checks the merge queue runs on every test build, the review rules that say which changes merge by themselves and which wait for a person, the teams that own paths, and the network the checks may reach. Everything on this page is what the parser accepts; a file it refuses is refused with the line and the reason.

## Where the file lives

Either in the repository, as `.yard/rules.toml` on main, or kept in Switchyard from the project's settings page. By default the set-up keeps it in Switchyard, so nothing has to merge to begin. When the file is in the repository, a change to it merges like any other change, and the merge queue reads the version on main.

Five top-level keys are allowed: `[[check]]` tables, `[[rule]]` tables, `[[owners]]` tables, `network`, a `[merge_queue]` table and a `[github]` table. Any other key is an error.

## Checks

A check is a command the merge queue runs on a fresh machine with the test build checked out and no secrets. Write one `[[check]]` table per command.

```toml
[[check]]
name = "test"
command = "npm ci && npm test"
size = "small"
timeout = "15m"

[[check]]
name = "build"
command = "npm run build"
paths = ["packages/app", "package-lock.json"]
cache = "7d"
```

| Key | Meaning |
| --- | --- |
| `name` | The check's name, at most 64 characters, unique in the file. The board and the merge record show it. |
| `command` | The shell command, at most 4,000 characters. It runs in the checked-out tree. |
| `size` | `"small"` (1 vCPU, the default) or `"large"` (4 vCPU): the machine it runs on. |
| `timeout` | How long it may run, such as `"90s"`, `"15m"` or `"1h"`: ten minutes when left out, at most one hour. A check that hits its limit counts as failed. |
| `paths` | One to sixteen files or folders, without wildcards, that the check reads. A pass is then also kept under the contents of those paths, so a test build that changes nothing under them reuses the pass instead of running the check. |
| `cache` | How long a pass is reused for the same tree: `true` (a day, the default), `false`, or a duration up to `"30d"`. A tree that passed never runs twice within it. |
| `github` | Instead of `command`: the name of a GitHub check to wait for, or `"*"` for every check, when the project's tests run on GitHub Actions. Takes no `size` or `paths`; GitHub's workflow decides them. |

A check has either `command` or `github`, never both. Checks run on every test build, which is main plus the submitted tasks in order; a check that fails runs once more to rule out a flake, and the merge queue then splits the batch to find the task to blame and sends only that one back, with the check's name and the end of its output.

## The network checks may reach

```toml
network = "registries"
```

One of `"open"`, `"registries"` or `"off"`. `registries` lets a check reach package registries and nothing else, which is enough for `npm ci` and `cargo build`; `off` is for tests that need nothing outside the tree. The project's settings page shows the same choice.

## Review rules

A rule matches a change and says what happens to it. Rules are read in order and the first match decides; a change no rule covers waits for a person.

```toml
[[rule]]
name = "docs merge on their own"
match = { path = "^docs/" }
policy = "auto-merge"

[[rule]]
name = "secrets never merge"
match = { added = "AKIA[0-9A-Z]{16}" }
policy = "block"

[[rule]]
name = "payments wait for a person"
match = { path = "^src/payments/" }
policy = "human-review"
reviewers = ["ada", "grace"]
```

| Key | Meaning |
| --- | --- |
| `name` | What the rule is for. The board, the inbox and the merge record use it. |
| `match.path` | A regular expression a changed path must match. |
| `match.added` | A regular expression an added line must match. With both, a path matches when it matches both. |
| `policy` | `"auto-merge"`: merges once its checks pass. `"human-review"`: waits for a person. `"block"`: never merges. |
| `reviewers` | Names of the people who should look at it; they are shown in the inbox. |
| `judge` | A question Claude answers about the change, such as "does this change the public API?". The rule applies when the answer is yes; otherwise the next rule is tried. |
| `threshold` | With `judge`: how sure the judge must be for the rule to apply, from 0 to 1; 0.5 when left out. |

Turning reviews off in the project's settings adds a last rule that lets every change no rule covers merge once its checks pass; rules that ask for review still apply.

## Owned paths

A team's approval is needed before a change to the paths it owns merges, however the rules decide.

```toml
[[owners]]
team = "infra"
paths = ["deploy/**", "infra/**"]
exclude = ["infra/docs/**"]
approvals = 1
```

| Key | Meaning |
| --- | --- |
| `team` | The team's name as the organization knows it, one word. |
| `paths` | Paths or globs the team owns; `"**"` is every path. |
| `exclude` | Paths or globs inside them that the team does not own. |
| `approvals` | How many of the team's members must approve, 1 to 20. One when left out. |

## The merge queue's fairness

When many agents submit at once, weights and aging decide whose task is tested first.

```toml
[merge_queue]
weights = { "fleet:F2" = 0.5, "person:ada" = 2 }
aging = "2h"
```

Each submitter spends a share per task, so a key of weight 2 gets twice the turns and a key of weight 0.25 a quarter of them; a key not named weighs 1. Keys are `fleet:<id>`, `person:<login>` or `agent:<id>`, from 0.01 to 100, at most 1,000 of them. A task that has waited longer than `aging` goes before every other task; an hour when left out, at most seven days. Hotfixes go first in the same way.

## Pull requests on GitHub

```toml
[github]
pull_requests = "submitted"
queue_label = "switchyard"
```

`pull_requests` is `"off"` (the default), `"submitted"` or `"all"`: with it on, a project that follows a GitHub repository shows its tasks there as draft pull requests, each naming the task, its agent and its changes, kept in step as the task moves and closed when it merges. `queue_label` is the label put on them, at most 50 characters; `switchyard` when left out.

## Checking the file

The parser refuses a file with an unknown key, a policy outside the three, a regular expression that does not compile, a duration it cannot read, or two checks with one name, and names the table and the line. Put the file on main or save it in settings, submit a task, and the merge queue's first test build shows the checks by name; the merge record of the first merge names the rules in force.

## What you have now

A rules file the merge queue reads on every test build: checks with names, sizes and time limits, rules that send the right changes to a person, owned paths, and a fair queue. Next, read how [conflicts](/concepts#conflict-conflict-steps) are fixed when two tasks collide, and what the [guarantees](/guarantees) promise about what merges.
