agents git dx the-hub

Many Agents, One Clone

Three months of git accidents from parallel AI agent sessions sharing one checkout, the worktree convention that stopped most of them, and the trap it created next.

October 6, 2026 · 7 min read · Michael Schilling
Several glowing agent nodes reaching into a single glass repository block, their light trails crossing and tangling

Part 18 of the series: Building The Hub

The scariest message git ever gave me was not an error. It was Everything up-to-date.

It was 23:16 on July 31. A session had just finished its work and pushed main. Git said everything was fine. Nothing had moved. Another agent session had switched the shared checkout to its own feature branch in the middle of that work, so “main” was a stale local branch nobody was looking at anymore. A successful-looking push had moved no commits at all.

(Part 17 promised you a different accident, an npm command that nearly took out FOUNDRY. That one is still in my drafts. This one was more urgent.)

It was not the first accident of the summer, and it was not the last. When you run two or three agent sessions in the same git checkout, git hygiene stops being hygiene. It becomes a safety property.

How I got here

The Hub is a single repository: knowledge base, skills, session logs, scripts. I work in it all day, often with a Claude Code session in one terminal, another one in a second, and Codex or Antigravity in a third. They all start in the same directory, because that is where the context lives.

For a while that worked fine. Then the sessions got longer, more autonomous, and more frequent. And a git checkout turns out to have exactly one HEAD, one index, and one working tree. Every session assumes it owns all three.

The catalogue

Here is a selection from three months of session logs. All of these really happened.

The wrong base. In early July, a session started a new branch, but the clone was still on another session’s feature branch, so the new work was based on someone else’s unfinished commits. The other session then committed onto my branch. Copilot review caught the mess; untangling it took a rebuild on origin/main and a force-push.

The branch that appeared between two commits. Later that month a concurrent session created a branch in between two of my commits. The first landed on main, the second silently on the new branch. The lesson the agent wrote down afterwards is still one of my favorite lines in the Hub: the branch a repo is on is not a fact you can establish once and rely on afterwards.

The reset. On July 29, an agent tried to clean up a messy working tree with git reset --hard origin/main. It destroyed seven files of uncommitted work. Some of those edits had been sitting there for days.

Here is the thing about uncommitted, unstaged changes: git status shows them, but git never stored them. There is no blob, no reflog entry, no stash. git fsck finds nothing because there is nothing to find. Six of the seven files came back in the weirdest way possible: by replaying the agent’s own transcripts. Every Edit call and every Read result had the content in it. As the agent put it afterwards, a Read is just as useful as a Write. The seventh file was gone.

The vanishing folder. On August 24, an entire scripts folder disappeared from the working tree: 62 tracked files, with three Dependabot PRs open against it. git restore brought it back. Two skill files had vanished the same way earlier that day.

The fix: give every session its own worktree

For weeks the answer was rules. git status -sb before every commit. git commit --only <file> so you never commit another session’s staged changes. Never reset --hard; update with git merge --ff-only origin/main. Always branch from an explicit start point:

git switch -c my-feature origin/main

All good rules. All dependent on every session following every rule every time, on a checkout that keeps changing under its feet.

The real fix came on August 11, and it is embarrassingly simple: stop sharing the working tree.

REPO=~/code/hub  # the shared clone
ID=podcast-fix   # any name that identifies the session
git -C "$REPO" fetch origin
git -C "$REPO" worktree add "/tmp/wt-hub-$ID" \
  -b "session/$ID" origin/main

Every session gets its own worktree, on its own branch, starting from a known commit. Its own HEAD, its own index, its own working tree. A branch switch or reset inside another session’s worktree no longer changes my checkout. And git status means something again, because the only changes in my worktree are mine.

It is not a force field, though. Worktrees still share the repository underneath: the same refs, the same config, the same remote. Nothing stops an agent from cd-ing into the wrong path, or from pushing the wrong commits. Shared refs and pushes still need care, as I found out next.

Session logs, which every session writes to, get pushed straight to main without switching branches:

git push origin HEAD:main

# rejected because another session pushed first?
git fetch origin
git rebase origin/main   # same-file conflict: keep both entries
git push origin HEAD:main

One gotcha Copilot caught in review: git fetch alone does not move your branch. You have to rebase onto the new tip before pushing again.

And clean up afterwards, from outside the worktree:

git -C "$REPO" worktree remove "/tmp/wt-hub-$ID"
git -C "$REPO" branch -d "session/$ID"

All of this now lives in the repository’s AGENTS.md, the one convention file every agent reads, so Claude Code, Codex and Antigravity get the same protocol.

The fix caused the next bug

Of course it did.

git push origin HEAD:main pushes your whole branch to main, not just the last commit. In late August, a session committed a feature on its session branch, opened a PR for it, then committed its session log on top and pushed with HEAD:main. The feature commit went along for the ride. GitHub saw the PR’s head commit on main and closed the PR as merged. Zero reviews.

It happened again the next day. CI had passed on the branch, so nothing was broken, but the review gate was simply skipped. And in September it happened a third time, an auto-merge before its requested review had run. The session log called it “the documented trap, walked into anyway”. I appreciate the honesty.

The cure is one check before every push to main:

git fetch origin
git log --oneline origin/main..HEAD

The fetch matters: origin/main is only your local snapshot of the remote, and it goes stale too.

If that shows anything other than your session log, stop. Push the feature through its PR, or use a separate branch for it.

The last one: is that file really dirty?

The shared clone still exists, and it still collects long-lived uncommitted edits from sessions that worked directly in it. In September, a session found three “blocking” edits there. Two were byte-identical to origin/main; they only looked dirty because the clone was eight commits behind. The third one would have deleted an entire day’s section from a log file if anyone had committed it.

git fetch origin
git diff origin/main -- Tracking/quickshare-log.md

Compare against a fresh copy of the remote, not against your stale local HEAD, before you believe a file is dirty. And before you “clean it up”.

What I would tell anyone starting with parallel agents

Most of this is not new. Teams have been dealing with shared branches for decades. What was new for me was the speed and the confidence. The agent that ran reset --hard on July 29 was trying to be helpful: the working tree looked messy, so it tidied up. I would have hesitated, because I have lost work that way before. It had not, at least not in that session.

So:

  1. One worktree per session. Not a rule they should remember, a structure that takes a whole class of accidents off the table.
  2. Branch from origin/main, explicitly. Never from “wherever the clone happens to be”.
  3. Never reset --hard in a shared checkout. Write it in the agent instructions, in capitals.
  4. Check what you are about to push. origin/main..HEAD takes a second.
  5. Write things down. Every rule in this post came out of a session log entry about something going wrong.

I still run three sessions in parallel most days. The difference is that they now each have their own desk, instead of sharing one keyboard.

#agents #git #dx #the-hub