DEV Community

Cover image for Agents need a ledger more than they need a new commit
Ian
Ian

Posted on

Agents need a ledger more than they need a new commit

In May, Anthropic merged about 2,500 code changes a day. By September it was over 30,000.

On October 13 it explained what it built to survive that: Scion, an in-house version control system that replaced git for its monorepo. It also said a public version would be too bespoke to help most teams, so it shared the ideas instead.

A commenter in the thousand-comment Hacker News thread put the obvious question plainly: "If they're not going to open source it, why are they telling us this?" Two days later, an answer showed up as code.

The open version

open_scion, by Federico Elia, is a fork of Gitaly, GitLab's Git RPC server, rebuilt around the ideas in Anthropic's post. It went public on October 15 and had over 1,000 stars a day later. GitHub shows the license as "Other", but the file is GitLab's MIT text plus a clause about an ee/ folder, and that folder is deleted here. The code is MIT.

I read it. I did not run it. make build compiles a bundled Git and pulls a long list of Go modules, which is past what I run for a post. So I cloned it, cloned upstream Gitaly at the release its VERSION file names (19.5.0-rc3), and wrote three small Node scripts to compare the trees:

$ node diffmap.js upstream repo
upstream files 2209 fork files 2826
same 704 changed 1446 added 676 removed 59
added go non-test lines 58555 added go test lines 90162
$ node normdiff.js upstream repo changed.txt
changed files 1446 module-path only 651 real edits 795
$ node rpcs.js upstream repo added.txt
upstream rpcs 156 fork rpcs 174 added 18 removed 0
Enter fullscreen mode Exit fullscreen mode

So this is not a rename. Nearly half the "changed" files only differ by the Go module path, but there are 58,555 new lines of non-test Go (5,636 of them generated protobuf code), 18 new RPCs, and every upstream RPC still in place. The new code is a CLI called graft, a cache daemon, a post-merge verifier, a review service, code search, and a set of monitoring agents.

What Scion actually changed

Scion's boldest move is in the data model. Its unit of work is a "changeset": a log entry listing the files that changed against the first parent, with pointers to their contents in blob storage. No packfiles, and no full copy of the repo anywhere, not even on the server. Anthropic updated its post on October 15 to credit Meta's Mononoke and its Bonsai data model, a day after a widely shared blog post laid out the overlap.

That design answers Anthropic's scale: a main-branch history of almost 100 GB and tens of millions of CI checkouts a day. The post itself says Scion is no general replacement for git, and suggests git servers borrow its best parts instead.

open_scion takes that advice.

What the open version keeps

Every repository is still a normal git repository, and a commit is still a git commit. In the optional database-backed storage mode, each write is stored as a pack in object storage: internal/graft/corestore/types.go (lines 135 to 152) gives every stored changeset a PackKey, of the form repos/<id>/packs/<sha256>.pack. The packfile Scion got rid of is the storage unit here.

"Changeset" also means something else. It is not a commit replacement. It is a record of a branch moving. The message in proto/changeset.proto (lines 38 to 62), with the field comments shortened by me:

message Changeset {
  uint64 sequence = 1;      // position in the log, from 1, no gaps
  bytes reference = 2;
  string old_oid = 3;
  string new_oid = 4;
  string operation = 5;     // "land", "ff_branch", "commit_files", ...
  string actor = 6;
  google.protobuf.Timestamp recorded_at = 7;
  bool rewritten = 8;       // the server rebased or merged
  repeated ChangesetMember members = 9;
}
Enter fullscreen mode Exit fullscreen mode

What an agent gets that a commit does not

Put a git commit next to that record and the gap shows.

A commit's author is whatever the client typed. Here, in its standalone mode, a request made with an agent token is logged with that token's agent name as the actor, whatever user ID the request carries. LIMITATIONS.md names the end-to-end test that pins it.

A commit says nothing about how it reached main. The record does: fast-forwarded or rebased by the server, and for a batched land, each member's original commit next to the commit that actually landed, plus resolved_paths when the server's conflict resolver stepped in. That resolver also writes Graft-Resolved-By and Graft-Resolved-Path trailers into the merge commit (land_resolve.go, lines 403 to 405), so an AI-resolved conflict is labeled in history itself.

And git keeps no shared, ordered story of branch moves at all. The reflog is per machine, and off by default in the bare repositories servers use. Here every accepted update gets the next sequence number, and WatchChangesets streams from any cursor.

That cursor is the spine of the project. The post-merge verifier follows it to decide what to test, bisect and revert. The monitoring agents (Anthropic's post calls them "superfans") follow it instead of server telemetry. graft watch prints it.

Landing without a queue

The write side is UserLand, one new RPC on the existing operations service. The server rebases your commit onto the current tip and moves the branch with a compare-and-swap. If another agent won the race, it backs off (2 ms, doubling up to 100 ms) and tries again, 20 times by default (land.go, lines 32 to 40 and 100 to 133). Concurrent lands on one branch can be folded into one update, which is where members comes from.

Built the way Scion was

The phase plans in doc/graft read like briefs for parallel coding agents: 61 numbered work packages across six phases, each told which files it owns, with some rules marked "added by Opus after gate 1". The first plan cites Anthropic's post. The repository's history starts on October 15 with a single commit holding the whole tree.

One squashed commit hides the process, so I can't say how long it took. But the dates leave about two days, and the test count fits an agent-scale build: 2,116 test functions in the new files alone.

The limits

From its own docs, and from my reading:

  • Reviews don't gate landing yet. The review service sorts files into comment-only, docs, tests and code, and counts approvals, but REVIEW.md says landing does not check reviews. That is a later phase.
  • The batching goal was missed. LIMITATIONS.md says the phase 4a acceptance criteria are not met: p99 latency with batching on came out worse than off at 50 and 100 agents.
  • The log can have gaps. It is written from git's reference-transaction hook, and a recording error is logged, never fatal. Without the database mode it is a JSON Lines file per node, not replicated.
  • CI runs a slice. The final commit's CI run is green, but by my count its package list covers 892 of those 2,116 tests. The server package that implements UserLand is not in it.
  • Issue #1 is a trap. It is a "fast install" PowerShell one-liner that downloads and runs a remote script as admin, padded with one-line "works great" comments. The only real install is make build from the repo.

The part to steal today

You don't need a new VCS for the ledger. Git has had a reference-transaction hook since 2.28, and on your server it sees every ref update. That is exactly where open_scion writes its log (referencetransaction.go, line 213). Append one line per update with a sequence number, the authenticated actor and the operation, and every bot you run can follow main by cursor instead of polling and diffing.

The commit tells you what changed. The ledger tells you who moved the branch, how, and in what order. When most of your committers are agents, the second question is the one you end up asking.

If an agent moved your main branch at 3 a.m., what could you prove about it from git alone?

Top comments (0)