# Require a person's approval

> Hold a change to chosen paths until a person signs a yes for its exact checked candidate.

Source: https://getbay.io/docs/approvals (HTML).

An approval check makes bay wait for a person before it merges a change to chosen paths. You list the paths in `when_paths`, such as `billing/` or `infra/`. A change that touches them waits with `APPROVAL_PENDING` until an enrolled approver signs a yes. A change elsewhere merges without one.

An approval is a signed evidence record, not a comment or a button. It names the exact candidate that bay checked, so a later edit needs a new approval. Only a key in the registry's `approver` file can sign one that counts. bay is not a code-review tool. The approver decides how to review.

## When to use approvals

- **Risky paths**: billing, auth or infrastructure code needs a person's yes before it merges.
- **Agents at scale**: agents merge most work alone, and a person signs off where it matters.
- **Named approvers**: only the people a check lists may approve its paths.

## Add an approval check

Add a `[[check]]` with `kind = "approval"` to `.bay/policy.toml` on main. Every approval check needs `when_paths`. Use `["**"]` to ask for an approval on every change. An approval check runs nothing, so it takes no `command`, `adapter`, `expected_minimum` or other run settings. It is never a `post` check. `producers` narrows which approvers may answer it. Every policy still needs a run check with `expected_minimum` above zero, unless it declares `unchecked = true`.

```toml
[[check]]
id = "billing-approval"
environment = "people"
kind = "approval"                  # a person answers; bay runs nothing
when_paths = ["billing/"]          # the paths that need a yes
producers = ["alice"]              # optional: only these approvers count
```

## Enrol an approver

Approvals need the key registry. Without one, no approval counts, and the refusal says so. Add the person's public key to the registry's `approver` file, as for any key. An approver name is never also an agent, producer or reviewer name. On the approver's machine, set `bay.approverName` and `bay.approverKey` in Git config.

```sh
git config bay.approverName alice
git config bay.approverKey ~/.ssh/bay-approver   # a hardware key, such as ed25519-sk, is best
```

## Approve a change

Read the waiting change with `bay status <change>` from your own clone. `operation.approval` names each owed check, who can approve it, and the candidate `commit`. Each owed check also carries `record`, the exact command to run. Review that candidate, then record your answer on that exact commit. `--verdict pass` approves, and `--verdict product_failure` rejects. bay signs the record with your approver key and pushes it.

```sh
candidate=$(bay status c7k2m4qa --json | jq -r '.detail.changes[0].operation.approval.commit')
bay evidence record --kind approval --check billing-approval --verdict pass --commit "$candidate"
```

## Merge after the approval

The agent runs `bay land` again. bay fetches the approvals first, then judges the change. With every required check passed and the approval in, bay merges it. A candidate with a different tree needs a new approval, such as one built on a newer main.

```sh
bay land --wait
```

## How bay judges an approval

- bay reads the approval check from the policy on main, like every check.
- bay asks for an approval only once every other required check passed.
- An approval counts for the candidate's tree, under the policy that judges it.
- A change to the policy needs an approval under both policies. `bay evidence record` writes one for each.
- Only an `approval` record answers an approval check, and an approval never answers a run check.
- A no from any approver, or two approvers who disagree, rejects the change.

## What an approval cannot do

- An unsigned yes never counts. Without a key registry, no approval check can pass.
- A key of any other class never signs an approval that counts.
- On a host from `bay host init` with a registry, the hook refuses such a record.
- An approver key signs `approval` records only, never a check record or a review.
- A quarantine sets an approval check aside, like any check.
- bay never reviews the code. It records who said yes to which tree.

## Refusals

Each refusal is `blocked` and exits `10`. The Results page lists what to do next for each one.

| Code | Meaning |
|---|---|
| `APPROVAL_PENDING` | Every other check passed, and an approval is still owed. `detail.approval` names the approvers and the candidate `commit` and `tree`. Without a registry, its `why` says that no approval can count. |
| `CHECK_FAILED` | An approver rejected the candidate. `detail.checks` names each `approver` who said no. |

## Limits

- **Record size**: one record is at most 64 KiB of JSON.
- **Scope**: one approval covers one tree under one policy.
