Articles

Revision control for a changed world

Every version control system in wide use today was built on two assumptions: one human sits at each keyboard, and the things being versioned are small text files. Perforce deserves a carve-out on the second, because it took huge binaries seriously decades ago and carried much of the games industry on the strength of it. But the Git-shaped world that came after kept both assumptions, and the Git-shaped world is where nearly everyone now lives.

Both assumptions have failed.

Walk through any modern game studio or ML lab. A growing share of the commits landing in the depot (the industry's word for the central store of everything) were authored or assisted by AI coding agents, operating under a person's identity but doing their own reading and writing. Meanwhile the artifacts under version control stopped being small text years ago. A single character's source PSD can outweigh the compiled engine that renders it. A training checkpoint outweighs both.

Git is the best tool ever built for the world where those assumptions held, and I say that as someone who has used it happily for two decades. But look at what we bolt onto it now. Git LFS for the files that never fit. A forge on top, because review policy needs somewhere to live and stock hosting gives it nowhere. A tracker beside the forge for the work. A secrets manager beside the tracker. And in the last few years, an AI layer draped across all of it with no governance at all. Each patch is sensible. The pile is not.

I have spent my career in games watching studios contort themselves around this pile. Grixel, which I'm building at Unabated Games, is the alternative: a depot redesigned from the foundation for the world that actually exists now.

#What Grixel is

Grixel is a server-authoritative version control and data platform for game development and AI/ML work. Everything a company makes lives in one global namespace: code, multi-gigabyte binary assets, datasets, models, brand guidelines, the lot. Canonical paths look like /companies/acme/games/seasonal. You never clone the whole thing; grixel checkout materialises a workspace view of just the subtree you need, narrowed to single folders if the project is enormous. One server daemon, grixeld, is authoritative for all of it: metadata, permissions, content storage, atomic commit transactions. The command-line client is deliberately thin, and a small companion server speaks MCP, the standard interface coding tools like Claude Code use to read from external systems, so AI assistants draw context from the same governed source as everyone else.

Picture it as one building with one archivist at the front desk. Git, by default, hands every contributor a photocopy of the entire archive and reconciles the copies afterward. In Grixel, you ask the archivist for the rooms you need and you are handed keys. The archivist also decides, and this is the unusual part, which rooms you are allowed to know exist. Every change to the building's contents passes across that desk, gets stamped with a record of who and what produced it, and certain rooms will not accept a filing until the right people have signed the slip.

In industry terms, Grixel is Perforce-shaped rather than Git-shaped. It keeps Perforce's strongest ideas: one global depot, path-prefix permissions, workspace views, atomic changelists, a server that is genuinely in charge. It replaces the parts that have aged badly, then builds upward: the same depot tracks the work as jobs, records the provenance of every change including which AI tool and model produced it, enforces review and sign-off inside the version control system, holds per-user secrets the server cannot read, and ingests code from git. The goal is one system of record a studio or lab can run its operations from, in place of the five loosely stapled systems most run today.

· · ·

#The decisions, and what they cost

Grixel is a small set of deliberate design decisions. Each one buys something and costs something.

The server is the truth. There is no local commit graph to reconcile later. The consequence you feel immediately: no offline commits, a cost anyone who has run on Perforce already knows how to live with. If the server is unreachable, a read-only offline mode still answers "what have I changed" from your working copy, but you cannot commit until you can reach the depot. A fair trade, I think: nearly everything interesting below depends on there being exactly one place where truth lives.

One namespace instead of many repositories. Projects are paths under a single root, and a project can pull other subtrees (company coding standards, brand guidelines, a vendored library) into its workspace at pinned revisions. Because the same server owns both sides of every pin, reproducibility is structural: the pin names an exact revision the depot can always resolve. The cost is operational concentration: running Grixel means running a server that matters. grixeld ships offline operator commands for garbage-collecting unreachable objects, point-in-time backup and restore, and a verify pass that integrity-checks the store end to end (a content-addressed store can prove its own bits). What it does not have is high availability: if the server dies today, the answer is restore from backup, and since backup takes the exclusive data-root lock it is honestly a stop-the-server operation for now.

Permissions mean invisibility. Path-prefix rules decide what you can read and write, which is ordinary. What is not ordinary is the failure mode: a path you cannot read is not "permission denied", it is gone. Absent from listings, absent from search, absent from error messages. This needs the first two decisions: git's wire protocol has no path-level read permission, so anyone authorized to fetch can fetch everything; shallow and partial clones reduce how much you copy, and the protocol still cannot say "this path is not yours to read". Invisibility has to be enforced where the one true copy lives.

Commits serialize through a single writer. Every changelist (Grixel's unit of commit: a batch of edits that lands all at once or not at all) passes through one write path per data root. Chunk upload happens outside the critical section; only the metadata commit is serialized, and that critical section is short (the pipeline is write-ahead-logged, crash-recoverable, and fault-injection tested). I chose this for simplicity: atomicity stops being a distributed-systems problem and becomes a property you can audit in one place. At some scale this decision gets revisited, and its ceiling is unmeasured. Perforce ran most of the games industry for decades on the same shape, which is why I am comfortable starting here.

Content-addressed chunks on a plain filesystem. Files split into content-defined chunks, each hashed; only new chunks are stored or transferred. The store is a flat layout on the local filesystem rather than an object store, which keeps a single-operator deployment simple. And because chunks are immutable and named by their hash, a transparent caching read proxy can sit in front of the store for LAN fan-out: a cache never has to wonder whether a chunk changed behind its back.

· · ·

#Day one feels ordinary, on purpose

New systems earn trust by being boring where boring is correct. Two words before the first transcript. A ref is a project's named line of history: the address a change is filed under. And the -as alice flag you will see throughout just says who is acting, because these transcripts ran in local mode; in a real deployment you run grixel login once and the flag disappears. Every transcript in this piece is verbatim output from the current build.

Here's the command:

$ grixel submit -as alice -ref companies/acme/games/seasonal -m "init seasonal project" readme.md

And what it prints:

Committed: dbda63ad6783be618aaff43acfc0be2c723f3f7cade4d057fa1b2174710bc342
  ref     : companies/acme/games/seasonal
  changes : 1 files, 52 B
  stored  : 52 B new, 0 B deduped
  parent  : (initial commit)
  author  : alice
  message : init seasonal project

A content hash, a ref, an author, a parent. Nothing exotic. The line worth remembering is stored. Plain Git can delta-compress binaries when it repacks; Git LFS, which is what large files ride on in practice, stores each version whole. The stored line is about to show something different from either: dedup at submit time, chunk-level, across files and history, with no pointer bolt-on.

#Big binary files are the normal case

Here is a 64 MB Photoshop file going in for the first time. Here's the command and what it prints:

$ grixel submit -as alice -ref companies/acme/games/seasonal -at art -m "main key art" keyart_main.psd
grixel: holding exclusive lock on /companies/acme/games/seasonal/art/keyart_main.psd (lockable type)
Committed: 2565dde94e79baae2337885ed2cbd60bc022633e3b359eefb1dd895dcae39b8c
  ref     : companies/acme/games/seasonal
  changes : 1 files, 64.0 MiB
  stored  : 64.0 MiB new, 0 B deduped
  parent  : 4ee43c8d07ae3d068dccfa44bc074e0a309b073183536f647dd365cb4f61ae50
  author  : alice
  message : main key art

Now the artist adjusts one layer and submits again. Same command; here is what it prints the second time:

$ grixel submit -as alice -ref companies/acme/games/seasonal -at art -m "key art: adjust sky layer" keyart_main.psd
grixel: holding exclusive lock on /companies/acme/games/seasonal/art/keyart_main.psd (lockable type)
Committed: a633634ee24180a7bca0559d152888292cb9a5432c7e4ae63b1e78067f554e59
  ref     : companies/acme/games/seasonal
  changes : 1 files, 64.0 MiB
  stored  : 2.7 MiB new, 61.3 MiB deduped
  parent  : 2565dde94e79baae2337885ed2cbd60bc022633e3b359eefb1dd895dcae39b8c
  author  : alice
  message : key art: adjust sky layer

The file is 64 MiB either way, but the second submit stored 2.7 MiB, because content-defined chunking found the boundaries that survived the edit and reused everything else. No pointer files, no separate large-file store, no .gitattributes ritual. The depot treats a 64 MB PSD exactly the way it treats a 52-byte readme.

Notice also the lock line. Photoshop files cannot be merged, so an admin policy marks the type as lockable. The habit that actually saves an artist's afternoon is claiming the lock before opening the file: grixel edit art/keyart_main.psd takes it up front, the same muscle memory as p4 edit. What the transcript shows is the backstop: submit auto-claims the lock, so nothing ever silently clobbers a colleague's work, and a refused submit fails early with the holder's name. But the backstop protects the depot, and only the up-front claim protects your afternoon; paint for hours into a file a colleague had already claimed and those hours are still yours to lose. Perforce trained studios into this habit for good reason; Grixel keeps it.

#"Permission denied" is a leak

Here is the invariant I care most about. Bob, a platform engineer, can see everything under the company namespace. Here's his listing command and what it prints:

$ grixel ls -as bob /companies/acme/games
/companies/acme/games/
  seasonal/  -> ref @ 4ee43c8d
    design/
    readme.md
  secret/  -> ref @ 6db73c65
    pitch.md

Alice, an artist on the seasonal project, runs the identical command. Here is what it prints for her:

$ grixel ls -as alice /companies/acme/games
/companies/acme/games/
  seasonal/  -> ref @ 4ee43c8d
    design/
    readme.md

No secret/. No hint that anything was withheld. And if she names the hidden path directly, or a path that never existed, here is what each prints:

$ grixel ls -as alice /companies/acme/games/secret
grixel: not found: /companies/acme/games/secret

$ grixel ls -as alice /companies/acme/games/doesnotexist
grixel: not found: /companies/acme/games/doesnotexist

Byte-identical. A denied path and a nonexistent path are indistinguishable, which means the error message itself cannot be used as a probe. Most systems get this wrong, because "permission denied" is an answer: it confirms the thing exists. In a studio holding an unannounced title, that confirmation is the whole leak.

This is enforced structurally, not by discipline. Every metadata-returning handler resolves the caller's view through a single chokepoint before it can build a response, and a model-based test asserts that a less-privileged principal's view is byte-identical whether hidden content exists or not. The same invariant follows the data everywhere it surfaces: listings, search, jobs, provenance, and the AI integration we will get to shortly. A job filed against a hidden project is itself hidden.

#When the author is not a person

Once agents write a meaningful fraction of a depot, "who changed this" stops being answerable from a username. Alice's account submitted the change; the fingers may have belonged to a model. Grixel records this as structured provenance on the changelist. Here's the command, an ordinary submit with attribution flags:

$ grixel submit -as alice --tool claude-code --model claude-opus-4-8 --role assisted --note "drafted from quest brief" -m "festival opening dialogue"

And what it prints:

Committed: 9c80ad050744013231cdcf05e1d5856a866ac1843e05b81c6ee0cdd4107ab498
  ref     : companies/acme/games/seasonal
  changes : 1
  stored  : 98 B new, 0 B deduped
  parent  : 4bb92aee3ed1c442e92da6ecc482c0561231ca0631fdb91a4dfce77f90caf507
  base    : 9c80ad050744013231cdcf05e1d5856a866ac1843e05b81c6ee0cdd4107ab498 (advanced)

(A reading note: the base line is workspace sync bookkeeping; showing the same value as the new commit just means your checkout is now current.)

Later, an art director asks what produced that commit. Here's the query and what it prints:

$ grixel provenance -as maya 9c80ad050744013231cdcf05e1d5856a866ac1843e05b81c6ee0cdd4107ab498
9c80ad05  (declared by alice, 2026-07-05T23:32:41Z)
    claude-code (claude-opus-4-8) — assisted: drafted from quest brief

Tool, model, role, note, and the server-verified principal who declared it. Agents can tag their own commits automatically through an environment variable. The record lives beside the commit, keyed by its hash rather than stored inside it, so content hashes stay untouched and the design leaves room for server-verified attestation later. A change can also capture the AI's working context, the transcript the assistant worked from, bound to the commit in the same content-addressed store, so a compliance review can ask what the tool knew when it wrote. grixel blame separates an AI-authored baseline from a human's later edits, line by line. And provenance on a commit you cannot read is simply absent.

#Governing what the machine gets to know

Here is the wedge, the part that made me build the rest. AI assistants working on real projects fail in a characteristic way: they invent brand guidelines, or reason from a stale copy of a standards document somebody pasted into a prompt six weeks ago. The failure is less about model quality than about what the model was given: ungoverned context.

In Grixel, a project declares its context in a committed file. Mappings pull the company's brand voice and coding standards into the workspace at pinned revisions, and the MCP server exposes exactly that, filtered through the same permission chokepoint as every human operation. You can inspect precisely what an assistant will see before it sees it. Here's the command:

$ grixel mcp preview -as alice

Three things to spot in what it prints: the file tree contains no secret project, the two standards documents arrive at exact revisions, and there are no write tools anywhere.

MCP session preview — principal: alice
(everything an AI assistant authenticated as "alice" can see)

reach — file_tree(/), ACL-filtered (paths you can't see are simply absent):
  //
    companies/
      acme/
        games/
        standards/

governed context — get_scope_context for scope "SeasonalEvent2026":
  # Scope: SeasonalEvent2026
  ID: SeasonalEvent2026  Revision: sm:a676caae42c90ae35a90342933cbb7e880a08d3bdcb577e78da5f0f7a4a9bb58  Owner: team:seasonal
  Boundaries: ~/demo/alice-ws
  Stale-mapping warnings: 0
  
  ## AI hints (from scope manifest)
  
  Tone: playful spring festival. Palette and voice live in .standards/brand.
  
  ## Attached references
  
  <grixel:doc path="/companies/acme/standards/brand" revision="2b55b19b9de198035e7faf679dbd1060655eaad00ea522beb58532066b389369" pin="head" locked="true">
  # Acme palette
  Primary: #2B6CB0. Accent: #ED8936. Never use pure black.
  
  # Acme brand voice
  Playful, concrete, never snarky. Numbers over adjectives.
  Address the player as 'you'; we never say 'users'.
  
  </grixel:doc>
  
  <grixel:doc path="/companies/acme/standards/coding/go" revision="363680814dfa01eac245f8fa15d07fad25767d686cce0db9b10ca8acd52e3ed4" pin="head" locked="true">
  # Acme Go style
  Wrap errors with %w. Table-driven tests. No panics past main.
  
  </grixel:doc>
  
  <grixel:doc path="/companies/acme/games/seasonal-vendored/noise" revision="2fccf9804fef0faf7089130231214b3f69dbbaab9f5702fe4a786441d0160cd1" pin="rev" locked="true">
  # noise — tiny noise lib
  --- noise.go ---
  package noise
  // Perlin returns smooth noise in [0,1).
  func Perlin(x, y float64) float64 { return 0.5 }
  // v2: real gradients
  
  </grixel:doc>

Read it as a contract. The assistant working as Alice sees the brand voice and the Go style guide at exact revisions, a vendored library at an exact pin, and a file tree in which the unannounced project does not exist. Run the same preview as Bob and the tree is wider, because Bob may see more. A permission means the same thing to a human at the CLI, a CI bot, and a model over MCP, because there is only one place permissions are decided.

The MCP surface is strictly read-only by design; there is no submit or write tool. When an agent wants to change the depot, it goes through the authenticated CLI write path, under the same locks, gates, and permissions as anyone else.

· · ·

#The bricks add up to a system of record

Each mechanism above stands alone, but the point is what they amount to together, so let me be direct about ambition. I am not building a storage backend that defers the interesting parts to a forge. Review, diffs, and work tracking are bricks Grixel lays itself; here I only sketch the shape.

Review and sign-off live inside the depot. A policy file attached to a path declares which roles must approve before a change can be promoted to a live ref, and the gate is enforced server-side at the promotion chokepoint. A sign-off names the exact content revision it approved; edit the file and the approval goes stale automatically. Git forges have a real answer here, and protected branches on GitHub or GitLab do block direct pushes. But that protection is a configuration of the hosting layer, not a property of the repository: it is per-branch and off by default, admin bypass toggles exist, and it evaporates the moment the repository lives anywhere else, on a mirror, a second remote, or a self-hosted bare repo. The approval record itself lives outside the object database. In Grixel, the gate is a committed file evaluated by the same server that owns the bytes, and the sign-off is a content-addressed record in the depot itself.

Work tracking lives inside the depot too, as a modern take on Perforce jobs: lightweight tickets anchored to paths, with admin-defined types and status state machines, linked in both directions to the commits that resolve them; anchored to paths, they inherit invisibility for free. Secrets get the opposite treatment: a per-user .env is encrypted on the client before it ever reaches the server, padded so even the plaintext length does not leak, and decryptable by the owner, granted recipients, and an escrow key, never by the server.

Because the world's code lives in git, Grixel imports from it rather than pretending it away. grixel import snapshots a repository's branch tip into a fresh project, grixel vendor pins an external dependency read-only at a fixed revision, and both record the source URL, branch, and SHA as provenance, so the lineage between upstream and your edits stays auditable.

#Where this build falls short

Grixel is pre-release, and I want to be precise about what that means.

It is unproven at customer scale. Every transcript in this piece ran against my own rig. The caching read proxy that carries the fan-out story has not yet had its multi-node, multi-gigabyte benchmark published; until it has, treat that part as design intent with early evidence. The single-writer commit path is a simplicity decision with a short critical section, and nothing has yet pushed it to its ceiling, so the ceiling is unmeasured.

Server authority costs you offline commits; nothing covers "commit from a plane". There is no web UI; the surfaces today are the CLI, the JSON output contract, the SDKs, and MCP. There is no multi-region replication, so a distributed studio leans on proxy caches rather than a second authoritative site, and there is no failover beyond restore-from-backup. There are no FUSE virtual mounts; sparse checkout covers enormous projects, a good answer but a different one. There are no Unreal or Unity plugins yet, which matters for the artists this is partly for; external diff and merge tool hooks are the bridge. Review has gates and sign-offs but no threaded comments; the conversation about a change still happens somewhere else.

And git's real advantages do not disappear because I wrote a different server. Two decades of muscle memory, tooling, and hosting are worth exactly what they appear to be worth. Grixel's answer is to ingest from that world and record the lineage rather than ask anyone to renounce it. Full-history migration, for teams moving a project's canonical home, is on the roadmap and not built.

· · ·

#Where the record lives

The question a version control system must answer has changed underneath us. It used to be "what changed?", and Git answers that question about as well as it can be answered.

The questions that matter now are different: who was allowed to see this, and can we prove who was not? What produced this change, and what did it know when it made it? Add whether the exact revision that shipped was the one signed off, and you are describing a system of record, which today is smeared across a depot, a forge, a tracker, a secrets vault, and a prompt history nobody kept.

I think those answers belong in one place, under one permission model, at the layer where the bytes actually live. That is the claim Grixel stakes. For now: the depot in these transcripts is real, the invariants hold on my machine, and the distance between "holds on my machine" and "holds under a studio's deadline week" is exactly the work in front of me.


Grixel is pre-release. Read the docs, or request early access.