Skip to content

Multi-agent merge: add/add of the same item (shared history prefix) leaves one side's file without conflict markers, so committing silently drops the other branch's changes #1491

Closed

Description

@unbraind

Problem

When the same pm item enters two branches independently (git add/add: the merge base does not contain the item), pm's merge drivers do not merge it. Git reports CONFLICT (add/add) for the item .toon and its history/*.jsonl, but the working-tree files contain only one side's content and no conflict markers. An agent that runs git add and commits (the natural reaction to a marker-free file) silently loses every change the other branch made to that item.

Both sides share a byte-identical prefix of history (at least the create entry), so the history stream itself carries everything needed for a lossless merge: the common prefix is the base.

Repro (pm 2026.10.17, merge drivers installed)

git init -b main r && cd r
pm init && pm merge install && git add -A && git commit -m init
git checkout -b plan
id=$(pm create --title "Shared plan item" --type Task --json | jq -r '.item.id // .id')
git add -A && git commit -m plan; C=$(git rev-parse HEAD)
git checkout main && git checkout -b a && git cherry-pick $C
pm comment $id "from agent a" && git add -A && git commit -m a
git checkout main && git checkout -b b && git cherry-pick $C
pm comment $id "from agent b" && git add -A && git commit -m b
git merge a
# CONFLICT (add/add) in .agents/pm/history/<id>.jsonl and .agents/pm/tasks/<id>.toon
git status --short          # AA for both files
grep -c '^<<<<<<<' .agents/pm/history/$id.jsonl   # 0
jq -c '{op}' .agents/pm/history/$id.jsonl          # create + ONE comment_add (agent b's); agent a's is gone

Line 1 of both stages (git show :2: / :3:) is byte-identical (same sha1).

How it happens with agents

This is not exotic in multi-agent work:

  • a planning commit that creates items is cherry-picked onto several agent branches;
  • an orchestrator creates an item in an uncommitted checkout and an agent copies it into its worktree so it can comment on it (what happened to us today: two independent histories of one item with a shared create+update prefix, 2 vs 43 entries).

Expected

  • History driver: when %O is empty but both sides start with an identical entry chain, treat the longest common prefix as the base and run the normal append-only merge.
  • Item driver: derive the base item from that same common history prefix (or replay the merged history) and do the field-aware merge.
  • If a merge is truly impossible (different create entries for one id), leave visible conflict markers or exit with a clear message naming the item. Never leave a marker-free single-side file in a conflicted path.
  • pm health after such a merge should flag "item history diverged from a shared prefix" if the user resolved it by picking one side.

Related: #1443 (lossless branch merge ordering), #663 (merge fence on fresh init).

Activity

  1. unbraind commented on Oct 17, 2026

    @unbraind
    OwnerAuthor

    Tracked in pm-gh1491 under the concurrency capability for isolated reproduction. The intake preserves the existing creation-ancestry protections and links the related merge-settlement work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions