# ModernCI: Agent-first CI

> Your agents write the code. ModernCI judges it, merges it, and tells you what each merge cost.

Self-hosted CI built for coding agents. Every move goes on the record. One binary in place of your runner, cache, merge queue, cheap runners and fix bot.

In use on our own repos today. Self-host release: follow the [changelog](https://modernci.dev/changelog.md).

## The flow

```
committed → judged → merged
              ↘ appealed → judged again
```

appealed: sent back to an agent, which returns with a fix.

Committed, judged, merged — or appealed.

## Cost per merged change · acme · today (example)

$0.07. 38 changes merged. 214 machine minutes, $1.12 of agent spend, 6 minutes of waste. These are example rows in the shape of real usage records; your own record replaces them.

| Move | Verdict | Checks | Machine | Agent | Waste | Done to merge | Cost |
|---|---|---|---|---|---|---|---|
| #4402 lane-search → predev | merged | 2 run · 5 reused | 3m 40s warm-2 | none | 0s | 11m | $0.03 |
| #4399 lane-billing → predev | red | 4 run · 3 reused | 6m 05s warm-1 | 1 try · $0.38 | 0s | open | $0.44 |
| #4398 lane-docs → predev | merged | 0 run · 7 reused | 0s | none | 0s | 2m | $0.00 |
| #4396 lane-api → predev | merged | 3 run · 4 reused | 4m 12s warm-2 | none | 1m 02s flake | 19m | $0.06 |
| #4395 lane-ui → predev | waiting | 5 run · 2 reused | 5m 48s warm-1 | none | 0s | open | $0.05 |
| #4390 predev → dev | merged | 3 run · 4 reused | 9m 31s cloud-c7 | none | 0s | 17m | $0.11 |

- #4399 is red on `billing::invoice_rounds_half_up`. Appealed: work item wi-0412 sent to agent host mbp-3, attempt 1 of 2.
- #4398 is docs only. Every check's input key already had a green verdict, so nothing ran and it cost nothing.
- #4390: two warm slots would miss the 20m deadline, so the router sent the tests to one cloud runner.

Cost per merged change = machine minutes + agent spend ÷ changes merged.

## 01 · The problem: your agents open 30 PRs a day, your CI was built for 3

We ran our own large Rust monorepo with a fleet of coding agents for months. These are numbers from that log, not a survey.

- 4,000 / 4,000 min: the Actions minutes cap, hit. A 4-shard matrix, a preflight and a fold job ran on every PR, about 2,000 extra runner-seconds each. Then everything stopped.
- 8 h: a silent stall. The engine acted only on events. A quiet repo produced none, so nothing merged and nobody knew.
- 0 lines: a red blamed on the wrong PR. The failing test was a flake. The PR it blocked had not touched it.
- 8 h: every agent idle. A weekly usage limit stopped the fleet, and nothing resumed it.

## 02 · The thesis: quality is how CI gets cheap

Quality, speed, cost: a chain, not a trade-off. Each link pays for the next.

1. Quality, right verdicts: a verdict that is right the first time, and a reuse key that is right, so nothing is judged twice.
2. Less waste, fewer wasted runs: no re-run, no wrong red handed to an agent, no attempt spent chasing a flake.
3. Speed, faster done-to-merge: the time from an agent's "done" to the merge, with nothing waiting on a stale verdict.
4. Cost, lower cost per merged change: and lower risk. The one number ModernCI is built to move.

When two goals conflict, the earlier link wins. A correct verdict beats a fast one; a fast one beats a cheap one. Waste is a bug: it is measured, named and cut.

## 03 · The loop: committed, judged, merged — or appealed

The agent owns the whole loop. People step in only to answer a question, which becomes a ruling, or when the appeals run out.

1. Committed. The agent pushes and calls `done` over MCP. ModernCI opens the pull request.
2. Judged. Each check's input key is looked up first; a stored green costs nothing. The rest run on the cheapest backend that finishes before the deadline. The verdict is green or red, and it cites its evidence.
3. Merged. On a green verdict, the target fast-forwards onto the judged commit.
4. Red becomes a work item: the failing check, the smallest log that shows it, the command to reproduce it, the diff and every earlier attempt.
5. Appealed. The failure ladder sends it to a free agent host. The failing check is judged first; the full move is judged again only if that comes back green.

A branch file, `.ci/predev.yaml`:

```yaml
# the verdict a commit on predev needs to enter dev
to: dev
deadline: 20m
checks:
  build:  { run: cargo build --workspace --all-features }
  clippy: { run: cargo clippy --workspace --all-targets -- -D warnings }
  test:   { run: cargo nextest run --workspace, scope: affected }
  gates:  { run: cargo xtask gate --all, baseline: target }
  oracle: { run: bin/oracle replay --strict }
guarded: [ golden/**, "**/tests/**", .ci/** ]
on_green: merge
```

Example work item wi-0412 (red, attempt 1 of 2, prompt fix, to mbp-3 on claude): #4399 lane-billing → predev, verdict red on test.

- check: test · cargo nextest run -p acme-billing
- failed: billing::invoice_rounds_half_up (1 of 214 judged; target 214)
- log: assertion `left == right` failed, left: 400, right: 201, at crates/billing/src/invoice.rs:188
- reproduce: `modernci ci --from lane-billing --only test`
- diff: 3 files, +41 −12
- evidence: red before, green after, measured by ModernCI
- sealed: weakening `**/tests/**` is sealed: only a person's order unseals it

## 04 · Honest agents: ModernCI takes no agent's word

Built for agents means built for agents that cut corners. A verdict you can trust means no wrong merge to unwind and no wrong red to chase. A suspected flake gets a second witness, a re-run on another machine, before anyone is blamed.

- Per-test truth. "Nothing was judged" is red: zero tests, a duration that collapsed because tests skipped, or a count that dropped against the target. Example: 1,406 tests judged against 1,418 on target; 12 disappeared, so red.
- Evidence, not claims. Red before the fix, green after, measured by ModernCI, not reported by the agent. Stored as evidence on #4399.
- Sealed paths. Deleting an assertion, skipping a test or editing a golden is sealed: only a human order unseals it. It goes to the owner's Questions queue, not to merge. Example: golden/billing/invoice.json edited, question q-88, merge held.

## 05 · One number: cost per merged change

Machine minutes plus agent spend, divided by changes merged. Everything goes on the record. Measured on our own monorepo, before and after. No projections: until the switch-over is done, this page shows "before" and says so.

| | Before: GitHub Actions + homegrown engine | After: ModernCI |
|---|---|---|
| Machine minutes per merged change | measuring, from the Actions bill | at switch-over |
| Agent spend per merged change | measuring, from host reports | at switch-over |
| Waste by class | ~2,000 runner-seconds per PR on cold shards rebuilding the same tree | at switch-over |
| Done to merge, median | 8 h stalls with no alarm | at switch-over |
| Cost per merged change | measuring | at switch-over |

How the number is measured, and what we charge: [Cost](https://modernci.dev/cost.md).

## 06 · Yours to run: one binary, any forge, your machines, no cloud required

Every install is a complete daemon. Machines connect out to it, so nothing needs an open port. Connect to modernci.dev only if you want one view of many daemons.

- Agent hosts: any laptop or server; Claude Code, Codex, anything; `modernci host join`. They reach the daemon over MCP, outbound.
- Your daemon: the loop, the store, secrets; the UI and the operator MCP; forges GitHub, GitLab, Gitea, Forgejo.
- Backends: this machine, an SSH host, a VM; any cloud, priced per minute; warm pools sized by workload.
- Optional: `modernci cloud connect` links a daemon to a modernci.dev tenant over one outbound connection. Code, logs and secrets stay home.

## 07 · Try it: three commands

Set it up today: paste this into your coding agent.

```
Read https://modernci.dev/llms.txt and set up ModernCI on this repo.
```

Works with Claude Code, Codex, or any agent that can run commands. The runbook is [Deploy ModernCI with your agent](https://modernci.dev/docs/agents.md).

Or by hand (preview):

```
docker run -p 8080:8080 -v mci:/data modernci/modernci
modernci repo add you/your-repo
modernci host join https://ci.your.lan
```

Already on GitHub Actions? `modernci import .github/workflows/ci.yml` writes the branch file. Then it judges beside your old CI, move by move, and you cut over when the verdicts agree.

## Used by

- [Busbar](https://getbusbar.com)
- [FreeMKV](https://freemkv.org)
