Skip to content

[finding] refs/remotes/origin/main is shared across worktrees too — another agent's fetch advances it under you, and git reset --soft origin/main then stages their merged files as yours #10743

Description

@os-warren

Measured by the domain:services dev seat while implementing #10547 (PR #10739). Caught before any commit, so nothing shipped — but the same sequence one step later produces a PR carrying four other agents' merged files.

The shape

Worktree isolation covers the working tree and HEAD. It does not cover refs/remotes/*, which live in the common .git directory and are shared by every worktree of the repo.

So while you work, another agent's git fetch advances refs/remotes/origin/main underneath you. The ref you branched from is not the ref that name points at any more, and nothing tells you it moved.

The prescribed-looking recovery then does the damage:

git reset --soft origin/main # ← re-bases onto a NEWER tip than you branched from

Everything merged into main between your branch point and that newer tip is now staged in your index as your change. In the measured instance that was four other agents' merged files. A following git add -A / git commit puts them in your PR under your authorship.

Why it reads as safe

The fix at the recipe level

Reset to the recorded base sha, never to the origin/main ref.

BASE=$(git rev-parse HEAD) # record at branch time, before any work
...
git reset --soft "$BASE" # ✅ a fixed coordinate
git reset --soft origin/main # ⛔ a shared pointer that moves under you

The same reasoning applies to any command taking origin/main as an implicit "where I started": git diff origin/main..., git log origin/main.., git rebase origin/main. They are all reading a ref another agent can move. When the question is "what did I change", the answer must be anchored to a sha you recorded, not to a name someone else can repoint.

This is the third member of one family

Three shared-state hazards are now measured, all with the same signature — an operation that looks local, reaches state shared by every parallel agent, and reports success:

#shared statemeasured
1refs/stash — one LIFO stack for all worktrees#5742 (cost two agents their in-flight work)
2node_modules hardlinks into the shared pnpm store — an in-place vendor edit corrupts every agent's tree and the storemeasured on #10532 (6 hardlinks on one vendor file)
3refs/remotes/origin/main — advanced by any agent's fetchthis card

⚠️ The generalisation worth writing down is not the three instances but the rule they share: git worktree isolates the tree and HEAD, and nothing else in .git. Anything else under .git/ — refs, the stash stack, config, hooks — is common. A recipe is only worktree-safe if it never names shared state.

Related

Filed unassigned for triage. Note that whichever surfaces get the warning (CLAUDE.md, AGENTS.md, the os-dev agent definition, the pm-dispatch skill) are governed surfaces#7863 is the card recording that fixing only the two documents a human reads leaves the recipe shipping in three other places.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions