# Adopt bay step by step

> Try bay in small steps on your own repository, score the trial, and switch it off if needed.

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

Adopt bay in small steps, so you can undo every step. Check each machine first, then let bay merge into a scratch copy of main. Next, give bay one small repository or stream, and then main itself, one stream at a time. Write down how you will judge the trial before it starts.

bay keeps its state in Git refs, and every merge is a plain Git commit on main. So switching bay off removes a few settings and deletes no history.

## When to use this page

- **A first trial**: you want to see bay on real changes before it merges into your main.
- **A team decision**: you need numbers that show whether bay helps your team.
- **A way back**: you want to know how to switch bay off before you switch it on.

## The adoption ladder

Climb one rung at a time, and climb only when the scorecard says so. From each rung you can step back to the one below it.

| Rung | What runs | Risk to your main |
|---|---|---|
| 0. Readiness | `bay doctor` on each machine. Nothing merges. | None |
| 1. Beside your flow | bay checks and merges real changes into a scratch copy of main. Your current flow keeps merging main. | None: bay never writes your main |
| 2. Small | bay merges one non-critical repository or stream. | That repository or stream only |
| 3. Main | bay merges main, one stream at a time. The old tool retires last. | Earned by rungs 1 and 2 |

### Rung 0: check each machine

Install bay and run `bay doctor` on each machine that runs agents or an executor. Fix each red row before you go on. Each red row names its fix.

```sh
bay doctor
```

### Rung 1: merge beside your flow

Make a scratch copy of main, and let bay merge into that copy only. The cleanest copy is a separate repository, for example a new host from `bay host init`. Push main there, then commit `.bay/config.toml` and `.bay/policy.toml` to the copy. Agents in a clone of the copy start and merge changes as usual. Your real main never sees them. Send a share of real tasks through the copy, and compare the outcome with your current flow. Make a fresh copy when it falls far behind main.

```sh
# on the host: an empty repository for the trial
bay host init /srv/git/app-trial.git --trunk main

# in a fresh clone of your repository
git remote add trial ssh://git.example.com/srv/git/app-trial.git
git push trial main
# commit .bay/config.toml and .bay/policy.toml, push them to trial, then
bay install --trunk main --remote trial
```

> On a forge, a scratch branch on your current remote works too, for example `bay-trial`.
> Run `bay install --trunk bay-trial` in its own clone, and name the same branch in its config.
> bay refuses a config whose `trunk` differs from the clone's.
> bay's own refs, under `refs/bay/` and `refs/notes/bay/`, then go to your current remote as well.

### Rung 2: one small stream

Pick one repository or stream that nothing urgent depends on. Follow the Quickstart on its real main. On a forge, let only the executor's identity push to that main.

### Rung 3: main, stream by stream

Move one team or one group of agents onto bay at a time. On a forge, or a bay-hosted main without a key registry, commits from your old flow still reach main. `bay watch --landed` marks each of those commits with `outside_bay`. Retire the old tool when no stream uses it.

## Score the trial

Write the scorecard before the trial starts, with a target for each measure. Measure the same weeks of your current flow, so you compare like with like. bay records the times and the check cost. People count interventions, wrongful blocks and recovery.

```sh
git fetch origin 'refs/bay/log/*:refs/bay/log/*' 'refs/notes/bay/*:refs/notes/bay/*'
git log --format='%cI %s' refs/bay/log/<executor>             # one terminal record per operation
git notes --ref=bay/evidence/<producer> show <commit>^{tree}   # the evidence of one merge, with at
bay status <change>                                            # landing_cost and operation.cost
```

| Measure | How to read it |
|---|---|
| Time from a green check to merged, p50 and p95 | Per merged change: the `at` of its passing evidence record, then the commit time of its terminal record in `refs/bay/log/<executor>`. The difference is the wait. |
| Human interventions per 100 merges | Count by hand each time a person acted so that a change could merge. Each refusal in `bay status` names its reason code, which helps the count. |
| Wrongful blocks | Review each refusal by hand. A refusal of a change that was correct and current counts as wrongful. |
| Recovery time | Count by hand the time from a broken main or a stuck queue to the next merge. `bay status --lineage` lists the `Reverts:` and `Fixes:` pairs on main. |
| Check compute per accepted change | `bay status <change>` shows `landing_cost.worker_seconds`, the check time on the merged result. `operation.cost` also counts discarded attempts. |

## Switch bay off

Switching bay off takes a few steps and deletes no history. Afterwards, a plain `git push` to main works again.

1. **Stop the executors** Stop every `bay executor run` through its supervisor. A change still in the queue then stays unmerged. Its record stays under `refs/bay/changes/`.

2. **Open main on a host from `bay host init`** Remove bay's two hooks and its main branch name from the host's Git config. Then Git on the host applies only its own rules again.

```sh
git --git-dir=/srv/git/app.git config --remove-section hook.bay-trunk
git --git-dir=/srv/git/app.git config --remove-section hook.bay-scope
git --git-dir=/srv/git/app.git config --unset bay.trunk
```

3. **Open main on a forge** Remove the branch protection or ruleset that lets only the executor's identity push to main. Keep any rule that refuses a force push to main.

4. **Clean each agent machine** Follow the uninstall steps on the Install page, in each repository and then on the machine. Remove bay's hook entries first. While they stay, bay's hook refuses an agent's `git push` of main. When `.claude/settings.json` sits on main, push that change to main too.

5. **Push without bay** Push one commit to main with plain Git, and confirm that the remote takes it.

```sh
git push origin main
```

> The other settings from `bay host init` are plain Git settings, so they can stay.
> The host also keeps the `reftable` ref format, which Git reads as usual.
> Without bay's main hook, the host takes a force push of main.
> To refuse one, set `receive.denyNonFastForwards` to `true` on the host.

## What stays after you switch off

- Every merge stays on main as a plain Git commit.
- Evidence stays under `refs/notes/bay/evidence/`, and `git notes` reads it.
- Change records and executor logs stay under `refs/bay/`.
- No step above deletes a ref. Keep bay's refs on the remote, so the record stays whole.

## Not built yet

- A shadow executor that checks real changes and records evidence, but never merges. Rung 1 uses a scratch copy instead.
- One command that reports the scorecard measures.

## Limits

- One main branch takes at most 1,440 divided by the check minutes changes a day.
