Picture a kitchen with seven cooks. Each cook has their own dish to make,
and they all share the same pans, the same ingredients, the same shelves.
If a cook never looks at a pan before using it, they will eventually dump
their sauce into a bowl someone just filled with something else. The dinner
is ruined, and nobody can even say who ruined it.
That is exactly the problem you get when you let many AI coding agents
work on one project at the same time. This post is about how we solved it
in Ogcode, and why the popular solution, making copies, made things worse
instead of better.
Read full article here : https://ogcode.in/blog/hedit-no-worktrees/
A coding agent is an AI program that doesn't just talk about code. It
actually does things: it reads your files, edits them, runs commands in
your terminal, and reports back. One agent is one helper.
Ogcode lets one agent be the boss. We call it the orchestrator. When you
give the orchestrator a big job, it splits the work into pieces and
spawns helpers of its own, called teammates. One teammate might write the
docs, another might fix a bug, another might clean up tests. They all run
at the same time, in the same project folder.
Which brings us to the problem.
Two agents can pick up the same file without meaning to. Here's how it
goes wrong:
Agent A just erased B's work without ever seeing it. A has no idea, B
has no idea, and you only find out much later, when something is broken.
Git, the tool that tracks your code, has a trick for this called a
worktree. A worktree makes a complete second copy of the project folder,
so two streams of work never touch. Agent A works in copy one, Agent B
works in copy two, and you merge the results at the end.
It sounds safe, and for a human working on two things it can be. For a
team of agents it's a trap:
You built two kitchens, cooked two dinners, and still had the same fight.
It just happens at the delivery door instead of at the stove, where the
cooks could have settled it.
Ogcode does the opposite of making copies. Every agent, the orchestrator
and every teammate, works in the same folder, on the same files. There is
no copying step and no merge step at all.
Instead there is one rule, and it is enforced by a gate we built into
every tool that touches files. We call it hedit.
Nobody writes over a change they haven't seen.
That's it. Simple to say, and it has to work perfectly or the whole idea
falls apart. Here's how the gate makes it true.
For every file in the project, the gate keeps a small notebook with
three entries.
A revision number. Think of the ticket counter at a bakery. Every
time someone changes a file, its number goes up by one. Change a file
ten times and it goes from 3 to 13. The number never goes backwards, so
the question "has this changed?" is just "is the number bigger than it
was?" No clocks, no guessing.
A fingerprint. A super quick check of the file's size and the time
it was last touched. It's like glancing at a book's cover to see if it
changed, without rereading every page. The gate uses this to spot that
something moved, and then the next read does the real checking.
A bookmark. For every agent, the revision number that file had the
last time that agent truly read it. That qualifier matters: an agent
that only asked for a quick summary or a file listing doesn't get a
bookmark, because it never saw the actual words. You can't promise you
read a page you never saw.
Nothing in it is a lock, a copy, or a piece of merge machinery. The gate
is just a very careful diary.
Before any edit or write lands, the gate asks one question: has anyone
else changed this file since you last read it?
The gate ignores changes the agent made itself. That's important,
because agents often fix their own mistakes and try again. Nobody should
be stopped from correcting their own work.
But if a different agent changed the file, the edit is refused, and the
agent gets a message like this one:
File changed by another agent since you read it: server.go
Nothing was written. Your edits were NOT applied.
Your last read was revision 7. The file is now at revision 9.
[1] rev 8, 09:14:07, docs-team (edit)
note: fix the wrong port number
@@
- old lines ...
+ new lines ...
[2] rev 9, 09:14:52, nav-fix (edit)
note: rename the navigation section
@@
- old lines ...
+ new lines ...
Re-read the file and re-apply your change against the current content.
That message is the whole feature. Look at what it gives the losing
agent: the names of everyone who touched the file, every change it
missed, with the old and new lines right there, and a clear next step.
It doesn't invite the blind retry a bare error does. It's a full
catch-up report.
The agent then rereads the fresh file and reapplies its edit on top of
what exists now. Nothing gets overwritten, and nothing gets lost.
We call this refuse-and-catch-up. Compare it to the copy-and-merge
world: there, the collision is discovered after both agents are
finished, by a human, in text that stopped making sense an hour ago.
Here, the collision is caught in the exact moment both agents are still
working, and one of them fixes it in seconds while the other keeps
going. Nobody rings a bell to summon you.
Edits can be checked before they happen. A terminal command cannot,
because it's a full shell: it might rewrite five files in ways the gate
can't look inside.
So the gate does the next best thing. It checks afterwards. When a shell
command finishes, the gate looks at every file it tracks and notes any
that changed. The note says what command ran and that something moved,
and that is honestly all it knows.
One detail we're proud of: the gate does not guess who changed the file.
The terminal command might have been run by any agent, or touched files
someone else edited indirectly. Blaming the runner would be a lie, and
lies in a system like this are poison. So the note says "changed,
author unknown," and every other agent gets stopped and shown the change
on its next edit, exactly as if a named agent had done it.
There is one tiny hole in the fingerprint, and we chose it on purpose.
Two changes could fit inside the same file tick and be invisible. To
close that hole we would have to reread every tracked file after every
single command, which would be slow everywhere to fix a rare case. And
the moment anyone goes to edit that file, the stale copy gets caught
anyway. We picked the fast gate with a hairline gap over the slow gate
with none.
The gate keeps the files safe. The team itself has a few rules on top:
docs-team above, so you can always tell who
did what.
Every change that lands goes into a permanent diary, with the file, the
revision, who made the change, which tool they used, a one line reason,
and the actual difference between old and new text. The diary never
shrinks, so even long after an agent is gone you can still look up
exactly what it did.
That diary is where the one-folder rule pays off a second time: it makes
the whole job auditable.
Think about auditing when agents work on copies. Three branches, three
streams of commits, one merge commit at the end that mashes everything
together. Ask later "why does this function look like this?" and the
honest answer is archaeology: dig through chat logs and terminal
scrollback, hoping someone wrote the reason down somewhere.
In Ogcode, asking "what happened to this file?" gets a straight answer.
Here is the shape of it, for the file from the refusal message above,
covering one job:
rev 8 09:14:07 docs-team edit fix the wrong port number
rev 9 09:14:52 nav-fix edit rename the navigation section
rev 10 09:15:31 docs-team edit rewrite the failing test
rev 11 09:16:03 orchestrator edit update the config example
Every line is one landing, and every landing carries the old and new
lines beside it, so you open the record and see the actual change, not
just the fact that a change happened.
And the reason is not luck. In Ogcode, an agent cannot edit a file
without writing one line on why. It is a required part of the edit, the
same way the file itself is. So the first thing every record answers is
exactly the question you will have later: what changed, and why. Nobody
had to remember to document anything after the fact.
Line the records up across a whole feature and the story falls out for
free: which files iterated and how many times, which agent owned each
change, what it actually did to the file, and where two agents met and
settled it. The settling comes from the collide-and-refuse moment
earlier: the diffs a refused agent gets are the very same diary rows,
so the catch-up report and the audit trail are one system. You could
read a whole feature's records top to bottom like a story, which the
copies-and-merge world can't give you.
We are honest about what's missing there: the diary has an interface for
programs but not a real page in the web UI yet. That's coming.
Because git solves a problem the agents don't have. Git is built for
writers who finish their work first and meet later. Two agents are never
finished. They're in the middle of the job, still holding the thinking
behind every change, still able to redo anything.
So Ogcode coordinates where agents are strong and git is weak: at the
moment of writing, while both writers still know what they meant. Git
stays underneath for saving history. It's a great history keeper. As a
bouncer at the door of a busy kitchen, it's out of its depth.
No post about our own system should pretend it's perfect. Here's what
we'd still like to build:
Lots of agents on one project don't need to be copies of each other.
They need to live in one folder and follow one rule: nobody writes over
a change they haven't seen. When that rule is enforced with a refusal
that shows you everything you missed, the agents settle their own
conflicts while the work is still warm, and you never have to stand
between two sets of edits with a broom.