Scion: Source Control at 30,000 Merges a Day

In May 2026, Anthropic merged about 2,500 changes a day into its monorepo. By September the number passed 30,000. The part of git that stopped fitting was its basic assumption that every clone is a whole repository. This essay reads the design of Scion, the system Anthropic built to replace git inside the company. It then names which part of a git setup fails first as agent merge rates rise.

Anthropic published Redesigning source control at Anthropic on October 13, 2026, and updated it on October 15. Scion is not open source, and the post says it is not a replacement for git in general use. Every Scion number below is Anthropic's own report. Nothing here was run against Scion. The git commands were run on an ordinary repository to confirm their flags.


Machine · A merge line with three cache tiers

Three agents send changes to scion-server, which appends each one to the changeset log and moves main with one compare-and-swap. A CI job on one node checks out through scion-daemon and scion-cache, and a blame question goes to the server with no checkout. The component names, the first-parent file list, the tree walk, and the three tiers come from the Anthropic post. The object counts per request are illustrative.

Where the git model stops fitting

Git is decentralized by design. Every clone holds the whole repository, so it can compute a diff, read blame, and check out any revision while offline. That design made git the standard, and the post says so. The cost is that each clone must hold every object the repository has ever stored.

The Anthropic monorepo outgrew that cost. The history of the main branch alone is close to 100 GB. A compressed working copy is more than 25 GB. CI checks out code about 25 million times a day, before developer machines, agent sandboxes, and research jobs are counted. Fetched the plain way, that is more than 600 PB of git traffic a day.

Git has tools for a slice of a repository: shallow clones, sparse checkouts, and promisor remotes. Large monorepos add two more. Internal git mirrors spread the fetch load, and repository snapshots in blob storage, called seeds, give a job most of its files before it fetches. Anthropic used all of them and ran several hundred mirrors at its peak.

Each workaround fails at its own rate. Seeds fall behind the tip of main between refreshes. At 30,000 merges a day, a seed refreshed every hour is 1,250 changes behind by the next refresh. On partial clones, the operations that read history, such as diff and blame, took more and more care to keep working. Mirrors have disks and memory, and the repository outgrew both.

The post names the observation behind Scion. Anthropic used git as a centralized system with one authoritative copy, and almost all of its clients were agents and jobs. Those clients are online by definition. The offline property that justifies a full clone had no user, and the full clone still had its full cost.

The first thing to measure is the product of two numbers: checkouts a day and bytes a checkout. The Anthropic pair is 25 million and 25 GB. Yours is smaller, and the figure shows by how much.

Fig. 1 · Fetch-bytes meter

Set how many checkouts your CI runs a day and how many bytes each one materializes. The ruler places your daily fetch volume beside what one mirror can push at line rate and beside the figure Anthropic reported. The tree-share slider is the sparse checkout: a job that reads one tenth of the tree fetches one tenth of the bytes.

Load a preset
20,000
3 GB
100%
Mirror link rate

Arithmetic, not a measurement. Fetch a day is checkouts times bytes. One mirror at line rate moves the link rate divided by 8, times 86,400 seconds. Real mirrors spend CPU on pack generation and run far below line rate. The 600 PB tick is the plain-fetch estimate in the Anthropic post, which pairs 25 million checkouts with a 25 GB compressed working copy.

A changeset is an appended entry

The unit of work in Scion is a changeset, and a changeset is an entry in a write-ahead log. A git commit points at a tree, which is a snapshot of the whole repository. A changeset stores the list of files that changed relative to its first parent, with a content-addressed pointer to each compressed file in blob storage. The server creates it, and it is readable everywhere as soon as it exists.

That shape removes three costs at once. There is no packfile to accept, so the server takes no multi-gigabyte upload. There is no lock and no agreement between servers, so two changesets can land at the same moment. And no ref moves when a changeset is created or fetched. Scion supports refs, and it discourages them except for main and release branches. Moving one is a compare-and-swap on a single Spanner row, done apart from creating changesets.

The first-parent rule is a stated choice. A footnote says that in trunk-based development in a monorepo, people care about history and blame on the main line only. So a merge changeset lists its changes against the main line, and that is the history Scion is built to read.

A changeset says what changed and nothing more. On its own it cannot answer who last edited a line, or which changesets touched a file. Scion answers those from separate structures that it builds from the changesets. They include a file blame structure, a path history structure, and skip lists over the commit graph. The post calls everything outside the changeset derived data, and one generic mechanism computes all of it. Each type is defined recursively. Its value for a changeset comes from its values for the parents of that changeset, plus the changeset itself.

The post names three benefits of that mechanism. A new type of derived data is cheap to add when some operation needs a faster structure. Each type can be computed as changesets land, or deferred until something asks for it. And the client never sees the inside of these structures, so the server can change them without a client release. The post lists types added as the system grew: indices for copy detection and for path-based file search.

The post credits Google's Piper and Meta's Sapling as prior art. Its October 15 footnote names Mononoke, the Sapling server, and its Bonsai data model. A builder of Mononoke wrote on October 14 that Scion's derived data follows the Mononoke design closely, and Anthropic added that footnote on October 15.

Fig. 2 · Changeset tape with a derived blame index

A toy repository with four files of four lines each. Press Append to land the next scripted changeset. Each card lists only the files that changed against its first parent, with a pointer to each blob. The right pane is the derived blame for one file. It is computed from the parent's rows plus this changeset's own diff, so a file the changeset did not touch costs nothing.

tip: c1

Derived blame for

Work counted

The file names, authors, and blob ids are made up. The log walk count is exact for this toy. A walk from the tip inspects every changeset back to the oldest one that still owns a line of the file. The derived read is one lookup by changeset and file, as the post describes.

Three caches that answer the same question

A small fleet of Scion servers answers every operation a client needs: get a changeset, compute a diff, find a merge base, fetch a blob. All of the data is content-addressed. A piece of data never changes once it is stored, so any server can cache it without asking another server. The post describes the servers as large caches that can also compute derived data on demand and write the results back to Spanner.

Metadata questions came first in the migration. Before Scion had its own client, repository format, or protocols, it answered only questions about ancestry and blame. That alone removed a maintenance burden from CI jobs and tools, which no longer needed a local repository to ask who changed a line. The automated bisect and revert pipelines at Anthropic depend on that cheap API access.

Moving file contents needed its own design. The git fetch protocol starts with negotiation. The client tells the server what it already has, and the server computes what is missing and packs it. The Scion client, scn, never tells the server what it has. It asks for the root tree. It checks its local store for the trees that the root names, requests the missing ones, and repeats down the tree. Then it does the same for blobs. Every request names one object that never changes, so any cache can answer it.

The protocol has two more effects. There is no single multi-gigabyte response, so a checkout can resume where it stopped. And sparse or virtualized checkouts need no special support, because the client does not ask for trees it does not want.

The requests pass through three tiers. On each node, scion-daemon stores objects for every job on that node, and most jobs on a node reuse most of the same objects. It starts from a periodically refreshed snapshot and talks only to scion-cache, the tree and blob cache for its region. Those cache servers stream objects from memory, in the disk format the daemon will store. They spend no time computing what the client lacks and none on delta compression. When a cache lacks an object, it asks the authoritative scion-server fleet, which reads from Spanner or cloud storage.

All three tiers answer identical RPCs, so the client does not know the cache topology. Any tier could talk to any other, and Anthropic forbids it. The tiering is enforced to keep failures apart. A failed regional cache must not send its load upstream and take the whole system down with it.

Anthropic reports these results. Each tier keeps a cache hit rate above 99 percent and holds no long-lived state, so adding capacity means adding instances. The Scion footprint is 200 times smaller than the git mirror fleet in CPU and memory. CI and research jobs check out code in seconds instead of minutes, and CI alone got 25 percent of its CPU capacity back.

Fig. 3 · Tier fall-through tally

Ten thousand object requests, one dot each, fall through the tiers. A dot the daemon answers stays pale. A dot that misses the daemon turns blue and goes to the regional cache. A dot that misses there turns red and reaches a server. The grid is split into regions. Take one region's cache down and switch between strict tiering and any-tier routing to see what the servers receive.

1 billion
99.0%
99.0%
4
Region 1 cache
Routing

Dots are scaled: each one stands for the daily total divided by 10,000. The post reports a hit rate above 99 percent per tier. It says the tiering is enforced so a failed regional cache cannot overload the servers. The request total and what a region does with unserved requests are not in the post, so both are reader-set here.

The lock on the merge queue goes

Near its end, the post turns to a workflow change. A merge queue serializes landing. It takes the next change, rebases it on the tip, runs the tests, and lands it before it takes the next one. The industry standard is a global repository lock around that step. Anthropic writes that a proper merge queue makes little sense at agent scale, and that it has not managed to replicate that global lock. Instead it invested in post-merge and pre-deploy automated testing. Concurrent changes can conflict as a result, and the post says Claude repairs those conflicts faster than people can find them.

Anthropic still runs a merge queue in Antique, its review tool, and a Claude watches it. What went away is the lock. A queue that lands one change at a time under a lock has a ceiling. It lands at most 86,400 divided by the test time in seconds, per day. A 10-minute test run allows 144 landings a day. Batching raises the ceiling, and speculative queues test several candidates in parallel, but every design still waits for a test run before the tip moves. At 30,000 merges a day, the tip moves every 2.9 seconds on average.

The post also removes checkouts where it can. A job that only needs the diff between a commit and its merge base asks the server for it and never checks out. Many write workloads also skip the checkout and work through the API. Both remove checkouts from the count in the first figure.

Operations changed with the system. For each component, the team runs a Claude in a Slack channel with access to that component's telemetry and logs. They call these Claudes superfans: one for scion-daemon, one for scion-cache, one for the Antique merge queue. Traditional alerts stay in place beside them.

What fails first in your setup

Scion is not a product you can install. For other teams, the useful part of the post is the order in which the git setup failed. Each stage has a number you can measure before you reach it.

First, freshness. Seeds and mirrors fall behind the tip by merges a day times the refresh interval. That number is harmless at 200 merges a day and an hourly refresh, where a seed is 8 changes stale. At 5,000 merges a day it is 208, and every job that starts from the seed fetches those 208 changes before it runs.

Second, bytes. Checkouts a day times bytes a checkout is the fetch volume your mirrors serve. It grows with both the number of agents and the size of the repository, and it is the number the first figure prices. A mirror fleet runs out of disk, memory, or CPU here, because every fetch computes a pack.

Third, history. Shallow and partial clones cut the bytes, and they make the operations that read history harder: blame, diff against a merge base, and bisect. A shallow clone does not hold the old commits at all. A blob-less clone holds them, but it fetches each old file version on demand. Those operations then move to the forge API, or they stay slow. Scion put them on the server from the first day of the migration.

Fourth, the lock. A global-lock merge queue caps landings at 86,400 divided by the test time. When agents push the merge rate past that cap, the choice is batching, speculation, or post-merge testing with a fast revert.

Three git commands give the first numbers: merges a day, the bytes a mirror stores, and the bytes one checkout writes.

# merges to main in the last 24 hours, first-parent only
git rev-list --count --first-parent --since="24 hours ago" main

# bytes the packs hold: what a mirror must store and serve
git count-objects -vH

# bytes one checkout materializes, as an archive of the tip
git archive HEAD | wc -c

Verified on git 2.50.1. The last line prints the byte count of a tar of the working tree at HEAD.

The closest git gets to the Scion checkout is a partial clone with a sparse cone. The clone takes no blobs up front, and the sparse set names the one directory the job reads. The server must allow object filtering, and GitHub and GitLab do. A clone from a plain local path ignores the filter, so test it with a file:// URL.

git clone --filter=blob:none --sparse https://example.com/monorepo.git
cd monorepo
git sparse-checkout set src/cache

Flags verified on git 2.50.1. Blobs arrive on demand as the job reads files.

Take an illustrative team to see the order. It runs 400 agents, each merging 10 changes a day, so 4,000 merges a day and one merge every 21.6 seconds. CI runs 20,000 checkouts a day of a 3 GB working copy, which is 60 TB a day. One 10 Gbit/s mirror moves 108 TB a day at line rate, so a single mirror runs at 56 percent. The hourly seed is 167 changes stale at the end of each hour. A 10-minute test under a global lock lands 144 changes a day. The queue must batch about 28 changes per run to keep up, or the lock has to go. For this team the lock fails first, freshness second, and the bytes hold until the repository itself grows.

Three failures repeat on this path. A seed refreshed on a clock gets more stale as the merge rate rises. Refresh it after a fixed number of merges instead. A blob-less clone fetches each missing blob on demand, so blame and bisect over a long history turn into many small fetches. Those operations belong on a server-side clone or on the forge API. A batching queue tests many changes in one run. When that run fails, the queue must split the batch to find the bad change, and every change in the batch waits. Anthropic chose post-merge testing instead, with Claude to repair the conflicts that land. A team that keeps its queue can keep batches small, so one failed run delays fewer changes.

Scion itself stays inside Anthropic. The post expects this class of problem to reach other companies within six to twelve months. It offers the design as something a git server can learn from. It names three changes. Servers can offer history RPCs. The negotiation can ask for objects instead of listing what the client has. Sparse and shallow features can compose. Until a forge offers those, measure merges a day, bytes a checkout, and test minutes under the lock. Then change the part of your setup whose number reaches its limit first.

rg
Rohit Ghumare

CNCF Ambassador and Google Developer Expert. I build agent infrastructure and write about the fundamentals underneath the AI stack. I read the Anthropic post of October 13, 2026, with its October 15 update, and the sunshowers.io post of October 14, on October 18, 2026. Scion is not public, so nothing here was run against it. The git commands were run on git 2.50.1 against an ordinary repository.

Related: Zed Turned Off Pull Requests · Agent Substrate · More posts · X