Repository navigation
Description
Reproduced on pm-cli 2026.9.3 (global install, Node 26.5.0) in a throwaway repository with pm merge install freshly run.
What happens
When two branches set the same scalar field to different values, the field-level driver resolves the conflict by stable_value_order. In practice that selects the lexicographically smaller value, and it does so regardless of which side is ours, regardless of --prefer, and regardless of which write is newer.
Reproduction
One item, two branches, each setting status. The branch that writes blocked commits first; the branch that writes in_progress commits three seconds later. Then merge the earlier branch into the later one:
earlier(blocked) commit 2026-09-03T19:30:07+02:00 updated_at "2026-09-03T17:30:07.414Z"
later(in_progress) commit 2026-09-03T19:30:10+02:00 updated_at "2026-09-03T17:30:10.251Z"
$ git checkout late-inprogress
$ git merge early-blocked
CONFLICT (content): Merge conflict in .agents/pm/tasks/pm-i0ea.toon
$ grep ^status: .agents/pm/tasks/pm-i0ea.toon
status: blocked
The older value wins. Both sides carry updated_at, so the information needed to prefer the newer write is present in the inputs and is not consulted.
The same ordering decides numeric fields: with priority: 3 on one side and priority: 1 on the other, 1 wins.
--prefer has no effect
pm merge --help documents --prefer <side> as "side that wins unresolvable conflicts (ours|theirs; default ours)". It does not win these:
| driver invocation | ours | theirs | result |
|---|---|---|---|
default (--prefer ours implied) |
in_progress / 3 |
blocked / 1 |
blocked / 1 |
explicit --prefer theirs |
in_progress / 3 |
blocked / 1 |
blocked / 1 |
| default, merged in the reverse direction | blocked / 1 |
in_progress / 3 |
blocked / 1 |
Three different configurations, one outcome. The receipt honestly records what was asked — requested_preference: "ours" or "theirs" as appropriate, which is a real improvement over the constant reported in #974 — but the requested side never decides anything, so the option reads as a control that is not wired to the behaviour it documents.
Why this matters for the multi-agent case
Direction-independent convergence is genuinely the right property for concurrent agents: two agents merging each other must not end up with different trees. The problem is not determinism, it is the ordering key. Sorting values alphabetically makes the winner an artefact of spelling:
blockedbeatsin_progressbecauseb<iclosedbeatsin_progressbecausec<i, so a stale agent that closed an item silently reverts a peer that had just started work on itpriority: 1beatspriority: 3regardless of who set it or when
An agent has no way to reason about this. It cannot predict the winner from its own intent, only from the alphabet.
Suggested direction
Order by updated_at (newest wins), falling back to the current stable order only when the timestamps are identical. That keeps determinism and direction-independence — both sides already agree on both timestamps, so both merge directions still converge — while making the winner the write that actually happened last. If lexicographic order is deliberate, then --prefer should either be honoured for these conflicts or documented as not applying to them, because as written it promises a control that does nothing.
Environment
pm-cli2026.9.3, Node 26.5.0, Linux- Fresh
pm init+pm merge install, drivers confirmed registered in.git/config - Item hash version 3
Activity
unbraind commentedon Sep 3, 2026
Canonical PM lineage: pm-7wzb6d — scalar merge conflict ordering ignores recency and requested preference. It is linked as a recurrence/semantic-policy extension of the shipped direction-independence and truthful-receipt work, rather than reopening those completed baselines.
Correct release inventory failures and self-hosted changelog readiness (