# Scion: why 30,000 merges a day broke git at Anthropic

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/anthropic-source-control
> date: 2026-10-14
> tags: agentic-coding, developer-tools, infrastructure, agents

Anthropic's engineering blog published [Redesigning source control at Anthropic](https://www.anthropic.com/engineering/redesigning-source-control) on October 13. The headline: merged changes on its monorepo went from "around 2,500 code changes merged per day" in May 2026 to "over 30,000 per day" in September, git became the bottleneck, and a team of seven built a replacement called **Scion**, plus a code review tool called **Antique**, and "fully migrated our monorepo from git in five months".

No code is released, and the post says plainly that Scion "is by no means a suitable replacement for git for general use". So this is a design teardown: why git strains at this rate, from first principles, then what the post says Scion does instead, then the numbers checked against the post's own charts.

A disclosure that matters here. This site is written with Claude, and this is Anthropic's own post about using Claude. I have no access to Scion, so every figure about it below is **reported** unless I say otherwise. **Measured** means I read it off the post's published images myself. **Reasoned** means my arithmetic on the other two.

## The rate, in units a lock understands

A day has 86,400 seconds. Spread evenly over 24 hours (reasoned):

| merged per day | per minute | per second | one landing every |
|---|---|---|---|
| 2,500 (May, reported) | 1.74 | 0.029 | 34.6 s |
| 18,000 (Sept 7-day average, measured off the chart) | 12.5 | 0.21 | 4.8 s |
| 30,000 (Sept, reported) | 20.8 | 0.35 | 2.88 s |

The last column is the one that matters. Anything that serializes landings, whether a lock, a queue, or a single process that owns `main`, has to finish its work in that interval on average, forever, or the backlog grows without bound. Real traffic has peaks, so the true budget is tighter.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig1.png?v=dc339de43e4f9fed"
  alt="Bar chart of changes merged per day to Anthropic's monorepo, January to September 2026. Blue bars are GitHub pull requests, orange bars are Antique change requests. A black 7-day average line climbs from under 1,000 in January to about 2,000 in May, about 6,000 in late July, dips during a quiet week at the start of August, then rises steeply to about 18,000 in early September. The tallest bar is annotated 30,401 merged on Sep 9."
  caption="Changes merged per day, January to September 2026. Pull requests (blue) give way to Antique change requests (orange) between June and July; the 30,401 label is a single day, and the 7-day average at the right edge sits near 18,000 (Anthropic Engineering blog, figure 1)."
/>

I read the chart's 7-day average line from its pixels. It sits at about 2,000 on May 1 and about 2,600 on May 31, matching "around 2,500". At the right edge it is about 17,000 to 18,000. The 30,401 is the single peak day, September 9 (measured). So the growth is about **12x peak-day over the May average, and about 7x on the 7-day average** (reasoned). The post's own subtitle says "more than tenfold", which is the peak-day reading. Both are large; they are not the same claim.

There is a second thing in that chart. Up to June every bar is a GitHub pull request. From July almost every bar is an Antique change request. The steepest growth comes after the switch. The post does not say whether a change request and a pull request are the same size of unit. If Antique encourages smaller or stacked changes, part of the count's growth is a change of unit, not of work. I can't tell from the post, and I'd want to know before quoting 12x as a measure of output.

## Why git strains: every clone is a server

Git is decentralized by design. Every clone holds the whole history and can diff, blame, log and check out any revision offline. The post's argument is that this property, excellent for most repositories, becomes the cost at this scale. Five mechanisms do the damage.

**1. Clone and fetch are proportional to the repository.** The post gives the sizes: "The history of our main branch alone is almost 100 GB, and a compressed working copy is over 25 GB", with "on the order of 25 million code checkouts per day for CI". Naively, that "would be over 600 PB of git traffic per day" (reported). The multiplication holds: 25 million × 25 GB is 625 PB (reasoned). Short-lived agent sandboxes are the worst case. They start empty, need the tip of `main`, and are thrown away an hour later.

**2. Fetch negotiation is computed per client.** In git's protocol the client tells the server which commits it already has, and the server walks the graph to work out what's missing, then builds a packfile for that client, delta-compressing as it goes. That is CPU on the server, per request, and the answer is specific to one client's state. So a plain HTTP cache can't reuse it.

**3. One ref, many writers.** A push to `main` must be a fast-forward. The writer fetches the tip, rebases, tests, and pushes. If anyone else lands in between, the push is refused and the loop starts again. With landings arriving as a Poisson process at rate λ, the chance that someone else lands inside a window of w seconds is 1 − e^(−λw). For a 10 second window that is 25% at 2,500 a day and 97% at 30,000 a day (reasoned). The 97% is not a typo. At September's rate, the plain fetch-rebase-push loop barely ever wins.

**4. Workarounds rot as the merge rate climbs.** Shallow clones, sparse checkout, partial clone with promisor remotes, mirrors and seed snapshots all exist. The post says Anthropic used all of them, and "at our peak, we operated several hundred internal git mirrors". The failure it names is specific: "Seeds fell further from the tip of main between refreshes". A seed taken at noon is a fixed number of hours stale. At 12x the merge rate it is 12x as many commits stale, and every one of them has to be fetched on top.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig2.png?v=38f16811381cf0ae"
  alt="Diagram of the git scaling setup. On the left, a Jobs box holding CI jobs, developer environments, agent sandboxes and research jobs. Arrows go right to a per-cluster group of packfile cache, mirror replicas and mirror primaries, then to Upstream. Below, mirror replicas feed blob storage holding a seed snapshot, which feeds a node seed back into the jobs."
  caption="The usual way to scale git checkouts: per-cluster packfile caches and mirrors in front of the upstream, plus seed snapshots in blob storage (Anthropic Engineering blog, figure 2)."
/>

**5. CI fans out per change.** This is what makes the merge queue impossible, and the post is unusually direct about it. A conventional merge queue holds a lock, tests each change on top of the current `main`, and only then lets it land. That only works if the test is shorter than the interval between landings. At 2.88 seconds it isn't, unless the "test" is nothing.

## The merge queue, as a queue

You can see the whole problem with three textbook models. A serialized queue that holds the lock for s seconds per change has a capacity of 86,400 ÷ s changes a day. With 30 second tests that is 2,880 a day, already 87% busy at May's rate. With 5 minute tests it is 288 a day (reasoned).

The industry's two fixes buy a constant factor. Batching tests B changes at once and bisects a red batch. Speculative lanes (Uber's SubmitQueue idea, and GitHub's merge queue build concurrency) build k changes at once, each assuming the ones in front will pass. One failure invalidates everything behind it, so a round lands (1 − (1 − p)^k) ÷ p changes on average, where p is the chance a change fails on top of `main`. At p = 5% and 16 lanes that is about 11 changes per test time, not 16 (reasoned). Both fixes leave the capacity proportional to 1 ÷ s, and s is your CI.

<MergeQueueSim />

The widget runs those formulas. Set it to 30,000 a day, 5 minute tests and a 5% failure rate. All three queued strategies fall over: the serialized queue handles 288 a day, batches of 16 about 1,400, sixteen speculative lanes about 3,200 (reasoned, from the model). To keep up with a serialized lock you need tests under about 2.9 seconds.

The fourth row is the option the post says Anthropic took. It wrote: "We haven't been able to effectively replicate the industry standard of a global repository lock on the merge queue, so instead, we've invested in post-merge and pre-deploy automated testing. This does mean that concurrent changes can conflict". The cost is easy to state. With a 30 minute test suite at 30,000 a day, about 625 other changes land while any one change's tests run (reasoned). The post's own version is "hundreds or even thousands of changes can merge while tests are running". Main is then green only in the sense that something caught it later. The post says Claude "can typically fix these conflicts faster than humans can even identify them". That is a claim about repair speed, and the post gives no breakage or revert rate to check it against.

The model is a model. It assumes arrivals spread evenly over the day, one culprit per failed batch, and that failed changes leave the queue. The post publishes none of Anthropic's test times or failure rates.

## What Scion is, as described

The post lists six principles. Read together they are one decision: **the server is the repository, and the client is a cache.**

- **Clients are always online.** Agents and CI jobs are online by definition, so there's no need for a full local copy.
- **Client-server asymmetry.** The server answers history and blame, creates commits, performs merges and rebases. The client keeps "a bounded local object cache", lays out files and watches the filesystem.
- **Every query the client uses is an API.** A CI job that needs a merge base or a diff asks the server; it doesn't clone.
- **Horizontally scalable, no packfiles.** "There is no fully materialized copy of the repo anywhere, even on the server", and "no repository maintenance is needed".
- **Commits are server-side, incremental and lock-free.** The cost of a commit "has to scale with the size of the change, not the overall size of the repo".
- **The working copy is optional and sparse.** Sparse checkouts contain only the requested directories; virtualized ones materialize files on read.

### Data: a changeset is a log entry, not a tree

In git a commit points at a root tree that names every directory in the repository. In Scion "the unit of work is a 'changeset,' an entry in a write-ahead log". A changeset stores the usual commit metadata, but instead of a tree pointer it stores "a list of the files that changed relative to its first parent", with content-addressed pointers to compressed blobs in cloud storage.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig3.png?v=7151b87f4ffb6d45"
  alt="Diagram titled Shape of a Scion changeset. A table in a Spanner metadata region lists the fields author, author_date, committer, committer_date, message, parents, signature and file_changes, where file_changes lists docs/guide.md with a hash, src/legacy.rs as deleted and tools/build.sh as executable with a hash. An arrow points to a parent changeset. Dashed boxes for a root tree and a src tree are marked derived by the server, not stored in the changeset. Arrows go down to immutable, compressed blobs in a cloud storage region."
  caption="A changeset holds metadata and a list of changed files in Spanner; file contents are immutable blobs in cloud storage, and trees are derived by the server rather than stored (Anthropic Engineering blog, figure 3)."
/>

This is why writing scales with the change. Adding a changeset writes a row with three file entries, not a new root tree and every tree on the path to each file. It's also why the post can say accepting changesets needs "no packfiles to accept, no locking or cross-server coordination, and no refs to move around".

Refs still exist, but only for `main` and release branches, and "moving one is a compare-and-swap on a single Spanner row that happens separately from creating changesets". That splits git's push into two steps. Creating the commit is parallel and lock-free. Publishing it is one conditional write. September's 0.35 landings a second is a small number for one row's compare-and-swap (reasoned). What the post does not say is how Antique's landing service orders competing changes onto that ref, or what it does when a server-side rebase conflicts. It mentions "novel ways of managing and landing large amounts of reviewed code" and stops there.

A changeset alone can't answer "who last touched this line". So Scion computes **derived data**: blame, path history, skip lists over the commit graph, copy detection indices. Each type is defined recursively, its value for a changeset computed from its parents' values. Each can be eager or lazy, and since clients never see the internals, they can be changed without a client release. Mononoke, the server behind Sapling, has a derived-data framework built on the same idea.

### Reads: ask for what you want, not what you have

The fetch protocol flips git's negotiation. "Scion's negotiation doesn't tell the server what the client already has; instead, it operates exclusively on trees and blobs the client wants." The client starts at a root tree, checks its local store, and asks for each missing tree by hash, recursively, then the blobs.

The consequence is cacheability. Every request names an immutable object, so any cache can answer it, with no per-client computation. Sparse and virtual checkouts fall out for free: the client simply never asks for trees it doesn't need.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig4.png?v=b5c5fd3f7c2a9750"
  alt="Scion checkout architecture. Clients (Antique, developer machines, CI and cluster jobs) send control-plane traffic for branches, history, diffs, upload, commit and land to scion-server, which writes to Spanner (changesets, branches, trees) and cloud storage (file contents). Object-plane traffic for trees and file contents goes to a regional scion-cache; CI and cluster jobs go through a per-node scion-daemon seeded from a periodic snapshot. Misses flow from daemon to cache to server."
  caption="Control plane to scion-server; object plane through a per-node scion-daemon and a regional scion-cache, with misses passed one tier up (Anthropic Engineering blog, figure 4)."
/>

There are three tiers. A per-node `scion-daemon` shares objects across every job on the machine and starts from a periodic snapshot. A regional `scion-cache` streams objects from memory "in the disk format that the daemon will consume". The authoritative `scion-server` reads Spanner and cloud storage. All three answer the same RPCs. The post says tiers are strictly enforced anyway, so a failed regional cache can't stampede the servers. It reports a hit rate "of over 99%" at each tier.

## The numbers, checked against themselves

I can't run Scion, but I can check the post's numbers for consistency with each other.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig6.png?v=5741db9666868b51"
  alt="Horizontal bar chart titled Everyday source control commands on the monorepo, median times git versus scn. Developer environments: pull latest 87 s vs 3.5 s, checkout 15 s vs 1.1 s, rebase 13 s vs 2.7 s, status 3.9 s vs 19 ms, commit 1.4 s vs 1.1 s, diff 407 ms vs 67 ms. Agent environments: new worktree 7.1 min vs 5.1 s, pull latest 3.0 min vs 9.0 s, commit 18 s vs 761 ms, status 4.3 s vs under 0.2 s. Total source control time per change request in agent environments 34 min vs 3.7 min, about 11,000 agent hours a day. CI and compute: checkout at start of compute job 175 s vs 22 s, about 85,000 machine hours a day; checkout in a CI job 31 s vs 3.5 s, about 195,000 machine hours a day."
  caption="Median git and scn times for everyday commands, with the post's daily-hours estimates. Its figures, not reproduced here (Anthropic Engineering blog, figure 6)."
/>

**The ratios hold.** Every "faster" multiple on the chart matches its two medians within rounding: 87 ÷ 3.5 = 24.9 (shown ×25), 426 ÷ 5.1 = 83.5 (×84, a 7.1 minute worktree), 34 ÷ 3.7 = 9.2, 31 ÷ 3.5 = 8.9 (×8.8), 175 ÷ 22 = 8.0. Status, 3.9 s ÷ 19 ms, gives 205 against ×207, which is the rounding of the displayed medians (reasoned).

**The CI hours hold.** 25 million checkouts a day × (31 − 3.5) seconds is about 191,000 machine hours. The chart says about 195,000 (reasoned). So the 195,000 is roughly "every CI checkout, at the median saving".

**The agent hours imply the volume.** 11,000 agent hours a day at a 30.3 minute saving per change request implies about 21,800 change requests a day (reasoned). That sits between September's 7-day average and its peak. It's consistent with the merge chart.

**One sentence disagrees with the chart.** The prose says "CI and research jobs check out code two orders of magnitude faster (in seconds rather than minutes)". The chart's medians for exactly those two rows are ×8.8 and ×8.0, one order of magnitude, and CI's git median was 31 seconds, not minutes (reasoned). Only the agent "new worktree" row, ×84, gets near 100x. The prose may be describing tails rather than medians. The post doesn't say.

**Not checkable from the post:** the "200x smaller" infrastructure footprint in CPU and memory against the mirror fleet, the "25% CPU capacity win" for CI, the "over 99%" hit rate, and the anecdote that Claude Mythos Preview rewrote the ingestion pipeline to use "20x less memory" and run "4x faster". All reported, none with a measurement method.

## "Five months", and "a team of seven"

The post says seven people built Scion and Antique and migrated in five months. It gives no start date. Its sixth chart counts merged changes to the `scm/` and `antique/` directories per week: 12,257 since January.

<Figure
  src="https://media.thesatyajit.com/ai/articles/anthropic-source-control/fig5.png?v=30e60846395d4022"
  alt="Weekly bar chart of changes to the Scion and Antique codebases, January to September 2026. Small blue pull-request bars from February, around 150 a week in April and May, switching to orange Antique change requests in June, rising to about 1,000 a week by late July and 1,560 in the second week of September. An inset says 12,257 changes since January."
  caption="Merged changes per week to the scm/ and antique/ directories: 12,257 since January (Anthropic Engineering blog, figure 5)."
/>

Reading the bars: commits start in late January but stay under about 60 a week until April, when they jump to about 150. The team is merging its own work through Antique by early June, and pull requests are gone by early July (measured off the chart). So "five months" fits counting from roughly April to September, with a prototype phase before it. The post says the first ingestion pipeline was "largely handwritten" "earlier in the year". 12,257 changes over seven people is about 1,750 each (reasoned), and the post says the engineers "would now rate Claude as better at writing code than them". Read that as changes merged, not lines a person typed.

## Prior art, and what's actually new

The post names [Google's Piper](https://research.google/pubs/why-google-stores-billions-of-lines-of-code-in-a-single-repository/) and [Meta's Sapling](https://engineering.fb.com/2022/11/15/open-source/sapling-source-control-scalable/) as the prior art. Both are the same family: a central server that owns the repository and a client that sees a slice of it.

- **Piper and CitC** (Potvin and Levenberg, CACM 2016): Google's custom-built monolithic repository, with developer workspaces served by the Clients in the Cloud system. I could not load the article's full text from here, so I'm not quoting its numbers.
- **Sapling, Mononoke and EdenFS**: Meta's [Sapling README](https://github.com/facebook/sapling) states the goal "that all source control operations scale with the number of files in use by a developer, and not with the size of the repository itself". Scion's commit principle is the write-side version of the same sentence. Meta's 2022 post says the stack serves "tens of millions of files, tens of millions of commits, and tens of millions of branches". The README says Mononoke and EdenFS are buildable from source but "Not yet supported publicly".
- **VFS for Git and Scalar**: Microsoft's route kept git. [VFS for Git](https://github.com/microsoft/VFSForGit) virtualizes the working directory and "only downloads objects as they are needed". Its README now recommends [Scalar](https://git-scm.com/docs/scalar) instead, which ships with git and turns on sparse checkout and background maintenance. Scion goes the other way: no packfiles, so no maintenance to schedule.
- **Jujutsu**: a git-compatible client whose [README](https://github.com/jj-vcs/jj) describes pluggable storage backends and names "Google's cloud-based design, Piper/CitC" as one. Its conflicts are first-class objects that can be committed and resolved later. That is a client-side answer to "concurrent changes can conflict", where Scion's answer is a server and an agent that fixes the conflict after landing.

Against that background, the server, the virtual working copy and derived data are not new. Three things in the post are more specific to the workload. The fetch protocol asks only for wanted objects, so every tier is a dumb content-addressed cache. Commit creation is separated from ref movement, so landing is one compare-and-swap. And the global merge lock is gone, replaced with post-merge testing and an agent on cleanup. The third is a policy choice, not a data structure. It's the one I'd most want numbers on.

A reply under Anthropic's X post linked [FrankenGit](https://github.com/Dicklesworthstone/frankengit). It is unrelated: an independent, pre-release "Git-compatible forge" in Rust by Jeffrey Emanuel, under an MIT licence with a rider that withholds rights from OpenAI and Anthropic. Its own README calls it source-available, not open source. It is not Scion and has no connection to it.

## Where this leaves git

The post doesn't argue for leaving git. Its closing asks forges to adopt the server side: "A git server doesn't have to work the same way as a git client does". Git platforms "can provide comprehensive server-side RPCs like Scion". The negotiation protocol "doesn't have to look exactly like it does today". Read as engineering rather than announcement, the post makes three transferable points.

1. **Make repository questions an API.** Merge base, diff, blame and ancestry should not require a clone. The automated bisect and revert pipelines the post mentions would be impractical otherwise.
2. **Make objects cacheable by name.** A fetch that asks for hashes instead of describing what it has turns a CPU-bound server into a cache hierarchy.
3. **Keep the serialized section tiny.** Whatever must be ordered, make it one conditional write, not a test run. Every second you hold the lock caps your day at 86,400 changes.

What it doesn't settle is the cost of the third point's policy half. Landing without a lock means main is broken some fraction of the time and something repairs it. The post gives no breakage rate, revert rate or time-to-green. If you are tempted to copy the design, measure those first. For the agent side of this story, see [the agent harness essay](/articles/agent-harness), which is about the loop producing those 30,000 changes, and [DeepSeek's DSec](/articles/dsec-agent-sandbox), whose sandboxes load images on demand for the same reason Scion's checkouts do. For indexed search over a monorepo, see [tgrep](/articles/tgrep).
