Repository navigation
Description
Summary
In a merge receipt, preferred is emitted as the constant "ours" regardless of which side's
value was actually retained. When the item driver resolves a scalar conflict via
conflict_resolution: "stable_value_order", the retained value is direction-independent — so in
roughly half of all scalar conflicts the receipt reports preferred: "ours" while ours is the
discarded value.
For an agent reconciling a merge, preferred is the field that most directly answers "did my
change survive?", and it answers it wrongly.
Reproduction (pm 2026.8.10)
mkdir mt && cd mt && git init -q . && pm init --id-prefix zz
pm create --type Task --title "Direction test" --author base
ID=zz-xxxx # the created id
git add -A && git commit -qm base
git checkout -b x && pm update $ID --priority 1 --author ax && git add -A && git commit -qm x
git checkout main && git checkout -b y && pm update $ID --priority 3 --author ay && git add -A && git commit -qm y
# ours = y (priority 3), theirs = x (priority 1)
git merge xDriver output:
preferred: "ours"
conflicts:
- "priority"
item:
conflict_resolution: "stable_value_order"and pm merge report:
preferred: "ours"
conflict_resolution: "stable_value_order"
decisions:
- field: "priority"
base: 2
ours: 3
theirs: 1
retained: 1 # <-- theirs
discarded: 3 # <-- ourspreferred: "ours" while ours: 3 is the discarded value.
Running the same merge from the other direction (git checkout x && git merge y, so ours =
priority 1) also prints preferred: "ours", and also retains 1. The field is a fixed literal in
both directions, not a report of the outcome.
Why this matters for agents
The convergent stable_value_order resolution is a genuinely good property — I verified both
merge directions land on the same value, which is exactly what multiple agents merging branches
in arbitrary order need. The surrounding reporting is also strong: the driver's guidance text
does say the stable value was kept, pm health goes ok: false with
merge_decisions_unreviewed:N, and pm merge reconcile refuses to accept discards without
explicit review. No data is lost.
The problem is narrowly that preferred contradicts retained/discarded inside the same
receipt. An agent that reads the structured fields (rather than parsing the prose guidance)
will conclude its own value won when it did not — and the structured fields are precisely what
an agent is expected to consume.
Suggested fix
Either:
- Report the actual outcome per decision — e.g. a
retained_side: "ours" | "theirs"on each
entry indecisions(theretained/discardedvalues are already there; naming the side
makes it directly answerable without comparing values); or - Rename the top-level field to reflect what it really records (the requested preference
supplied to the driver, e.g.requested_preference), so it cannot be read as the result; or - Omit
preferredentirely whenconflict_resolutionisstable_value_order, since the
resolution is by construction direction-independent and no preference was applied.
Option 1 plus option 2 together would make a receipt self-consistent without losing information.
Activity
coderabbitai commentedon Aug 11, 2026
🔗 Related PRs
#614 - SDK-first field-aware merge safety and storage integrity [merged]
#758 - Make branch merge decisions recoverable, replayable, and CI-enforced [merged]
#940 - SDK truthful context, deterministic merges, and package contracts [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
unbraind commentedon Aug 11, 2026
Tracked canonically in pm-qckpnq, with the merge-receipt lineage and acceptance contract recorded there.