Aa

reading preferences

appearance
prose
face

Building a Git-Backed Canvas · Part 2

Handling concurrent agent and human mutations

Aug 15, 2026 · 6.5 min · System Design

AI Systems / Race Conditions / Concurrency

Failure

Change

Git-on-EFS via pygit2 threadpools.

Result

A blazing-fast, versioned native filesystem.

In the previous part, we achieved and got to a solution for our realtime web-native agent that has a file system where a git repo is a project. However, this is not without its considerations.

Since our primary versioning primitive is git, the commit history is the durable record of the files, the state that is present in the file system is basically the reduction of all of the commits from the root to HEAD.

git is for sole actors

When we work on a repository and are trying to collaborate, we branch out and then merge back in and resolve any conflicts in a single go during the merge/rebase. It generally works (though not always due to big forks/deviations) since they are done offline, after all events have occured, and are generally human-only so operations can be timed properly and they can happen sequentially.

Since our system is a collaborative one, here n humans and n agents are collaborating together on the same source repository. We cannot afford getting the wrong things into the index, or the commit history is spoiled eternally. The usual git add . does not work here since that literally means stage all the things present on the disk currently to the index for committing. This is by definition and not a bug, however it is not going to work for us.

Suppose the following events happen:

  • t0 —> user drags a node; optimistic write lands on the canvas and eventually on the server, uncommitted
  • t1 —> agent finishes its edit and we call git add .
  • t2 —> now the commit contains agent’s edits along with half baked user edits
  • t3 —> the user’s optimistic action now looks to commit the action using git add ., sees no staged files, aborts
  • t4 —> system reverts the user node drag, however the durable history now contains a user action which shouldn’t exist, how do we decide what the truth is here?

The above question is easily answered by selective staging. Instead of naively doing git add ., we can simply choose the explicit files that have been operated on and only stage those, you may have seen the code block in the previous part and may have asked why bother selecting what to add to the index, well this is why.

what about concurrency?

diagram