Skip to content

Merge receipt: preferred is the constant "ours" and contradicts retained/discarded under stable_value_order #974

Closed

Description

@unbraind

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 x

Driver 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     # <-- ours

preferred: "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:

  1. Report the actual outcome per decision — e.g. a retained_side: "ours" | "theirs" on each
    entry in decisions (the retained/discarded values are already there; naming the side
    makes it directly answerable without comparing values); or
  2. 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
  3. Omit preferred entirely when conflict_resolution is stable_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

  1. coderabbitai commented on Aug 11, 2026

    @coderabbitai
    🔗 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 plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 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:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. unbraind commented on Aug 11, 2026

    @unbraind
    OwnerAuthor

    Tracked canonically in pm-qckpnq, with the merge-receipt lineage and acceptance contract recorded there.

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