Blog

What we took from Scion for AI agent Workspaces, and what we deferred

· by

TL;DR. We compared Anthropic's Scion design with plori Workspaces. We added a per-path history, and the platform now accepts a worker's change without review when the merge has no conflicts. We measured Scion's merge cost rule before we built it. Finding the changes was already cheap. Most of the cost is in building the merged file list, and our trees are small enough that we deferred that work.

On October 13, 2026, Anthropic described Scion, the system that replaced git for its monorepo. The monorepo went from about 2,500 merged changes a day in May 2026 to more than 30,000 a day in September. Anthropic wrote that "nearly all of our clients are agents and jobs."

Our scale is much smaller. A plori Workspace holds the files of one manager agent and its workers, saved as restic revisions. Each Workspace has its own restic repository. A worker proposes a change, and a storage service merges it into the current revision on the server. The current revision moves with a compare-and-swap on one Postgres row, as a Scion ref does. We used Scion's design as a list of questions about that system.

What did we add?

A per-path history

Workspaces kept their saved revisions, but they had no index by path. To answer "which run last changed this file, and who accepted it?", someone had to read run logs. Path history is not new. git log -- <path> does it on every clone. The part we took from Scion is where the history comes from. Scion derives path history on the server from its changesets. We derive ours from each accepted revision in a background job.

After the platform accepts a revision, a background job asks the storage service for the paths that differ from the previous revision. Restic stores directories by content hash, so the diff skips every directory that did not change. The job records up to 10,000 changed paths per revision. Above that, it marks the list as incomplete. Each history row names the run, the worker's change if there was one, and the person who made or accepted it. The API returns the history for a Workspace or for one path, and the Files panel shows who last changed the open file.

While we built it, we found an attribution bug. When a member edited a file in the web app, the platform recorded the save under the account owner. It now records the member.

Accepting a clean worker change without review

Anthropic wrote that "a proper merge queue, for example, doesn't make much sense at agent scale." Anthropic could not replicate the usual global repository lock on its merge queue. Instead, it "invested in post-merge and pre-deploy automated testing." Its review tool still separates simple changes from sensitive ones. The goal is "that human reviewers can focus on the most consequential changes."

In Workspaces, the manager agent reviewed every worker change before it became current, even when the merge had no conflicts. Now the platform accepts a worker change that changes files and merges with no conflicts. A change with conflicts waits for review. So does a change whose conflicts someone resolved, because the resolution is new content that no comparison checked. A Workspace owner can turn automatic acceptance off and review every change.

This is a different trade from Anthropic's. A three-way merge with no conflicts can still produce code that does not build, or two files that no longer agree. Workspaces run no tests before or after an automatic accept. There is also no one-step undo. The path history shows which revision changed a file and that the platform accepted it. The owner can read the old version of the file from the earlier revision and save it again through Files, as a new change. Automatic acceptance is still the default. A review of every clean merge costs the manager a model turn per change, and the change waits until the manager acts. An owner who wants a check before every change can turn the setting off.

While we built it, we also found a bug in the accept path. Sometimes the current revision moved after the platform compared a worker change. The accept then failed its compare-and-swap, and the platform retried it every 15 minutes without end. A retry of the same write can never succeed after the base moved. The change now goes back to comparison against the new current revision.

Why did we measure Scion's merge cost rule before we built it?

Scion's design requires that the cost of a commit "scale with the size of the change, not the overall size of the repo." A Scion changeset stores the files that changed relative to its first parent. Our merge does not meet that rule. Part of each comparison processes the whole tree.

The cost repeats. For a change that waits for a person, the platform compares again only when someone accepts it. Automatic acceptance changes that. Every accept moves the current revision, and the platform must compare each clean change that is still waiting again. Ten clean changes that finish together can cost up to 55 comparisons.

We measured one comparison before we changed anything. The benchmark merges two changes of 10 files each into synthetic trees, in memory. The two changes touch different files, so no text merge runs.

Files in the tree Time per comparison (median of 3) Bytes allocated per comparison
1,000 15.6 ms 5.7 MB
10,000 125 ms 47.1 MB
100,000 1.93 s 447 MB
500,000 13.9 s 2.43 GB

Finding the changed paths scales with the change. At every tree size, the comparison loaded only 44 to 62 directories of the base and the incoming change. The merge skips subtrees with equal hashes. About 75% of the time goes to building one flat list of every path in the merged tree, and its digest. About 22% goes to loading every directory of the current revision. A stored list of changed paths, as in a Scion changeset, would remove neither cost.

The flat list also sets a hard limit. At 500,000 files, it is 115.5 MB, and the storage service accepts at most 96 MiB per write. With our short synthetic paths, the limit falls at about 435,000 files. Longer real paths lower it. A merge that writes only the changed directories and reuses the others by hash would remove the limit. It needs a change to the write path in our restic fork. A smaller first step is to process only the changed entries inside the merge. We have not measured that step yet.

Across our Workspaces, the latest revision has a median of 19 entries (files and directories), a 99th percentile of 9,361 and a maximum of 32,226. At the 99th percentile size, 55 comparisons take about 7 s of single-core time on the benchmark host, spread across ten accepts. On the 1-CPU production service it can take longer. We accepted that cost for now. We will revisit it when a Workspace with active workers reaches about 100,000 entries, or when we measure more than about three comparisons per accepted change on average. The ten-change case above is a worst case of 5.5 per accept, and the trigger counts what happens in practice.

These numbers come from one 8-core development host that also ran CI jobs. The production storage service runs with 1 CPU and 1 GiB of memory. The byte column counts total allocations, not resident memory. The benchmark does not include restic reads from object storage or the time to write and check the merged revision.

What should you check if you build agent storage like this?

  1. With content-addressed trees, finding what changed is proportional to the change. Measure where your merge spends its time before you build a changeset store.
  2. Count how often your platform compares a waiting change again. Every change that becomes current without review forces a new comparison for each change still waiting for automatic acceptance.
  3. When a compare-and-swap fails because the base moved, compare again. A retry of the same write cannot succeed.
  4. A clean three-way merge is not a test. If nothing runs before a change becomes current, tell the owner and make the undo path known.