Skip to content

Merge-decision receipts are clone-local and gitignored, so a commit that silently discarded a peer agent's edit passes pm health --strict-exit in CI #922

Closed

Description

@unbraind

The only artifact that records "agent A's edit was silently discarded by this merge" is a merge
receipt, and receipts live in the clone-local, gitignored .agents/pm/runtime/. They are never
pushed. A fresh checkout of the exact commit that lost the edit therefore reports zero pending
decisions and passes pm health --strict-exit, so merge safety cannot be enforced in CI at all.

Why this is not obvious

The lossy case is already invisible to normal review: the merged .toon has no conflict markers,
is valid TOON, and already holds the winning value, so pm validate returns ok:true. pm health
is the one surface that goes red — which makes it the natural CI gate. But it only goes red in the
clone that performed the merge.

Reproduction (pm CLI 2026.8.5)

Two branches each set a different title on the same item, then git merge, git add -A,
git commit (the marker-free file stages cleanly):

# in the clone that performed the merge
$ pm merge report --json | jq .count
1
$ pm health --strict-exit ; echo $?
1                      # correctly red

# fresh clone of that very same commit
$ git clone <repo> fresh && cd fresh
$ pm merge report --json | jq .count
0                      # receipt was never pushed
$ pm health --strict-exit ; echo $?
0                      # PASSES - the discarded edit sails through CI

.agents/pm/runtime/ is in the gitignore block pm init itself writes, so this is the default and
intended state of every repo, not a misconfiguration.

The pushed history does not disambiguate it either

The merged history stream contains both writes, ordered by timestamp:

2026-...:26.423Z agent-a ["Renamed by agent A"]
2026-...:27.066Z agent-b ["Renamed by agent B"]

and the item holds agent-b's value, so pm history <id> --verify reports
current_matches_latest: true with matching hashes. From pushed state alone, "two concurrent branch
edits where one was discarded" is indistinguishable from "two sequential edits where the second
superseded the first". Every check is correct in isolation and the loss is still invisible.

pm merge reconcile publishes the evidence but does not make it gateable

pm merge reconcile appends a merge_reconcile event into the tracked history stream carrying the
full decision, including conflict_fields, retained_hash and discarded_hash. That evidence
is pushed and readable in a fresh clone — good. But:

  • After reconcile, pm health --strict-exit exits 0 in both the local and the fresh clone, so the
    durable record of a discarded value produces no signal.
  • Reconcile is optional. A contributor who never runs it pushes a commit that looks clean
    everywhere.

So there is no configuration in which CI can observe that an edit was discarded.

Suggested fix

Any one of these would close it:

  1. Make receipts durable by default — write pending merge decisions into the tracked tracker
    (or require reconcile before the tree is considered clean), so the evidence travels with the
    commit that caused it.
  2. Give pm health a repository-scoped mode that inspects merge commits reachable in git rather
    than only clone-local state — for a merge commit, compare each parent against the merge base and
    report same-field divergences that the merged item resolved. This is the only signal that
    genuinely exists in pushed state.
  3. Surface reconciled-with-discarded-values as a distinct, gateable condition, so a
    merge_reconcile event whose decisions[] contains a discarded_hash can fail a strict gate
    until a human or agent explicitly acknowledges the loss.

Related: GH-920 (health counts lossless receipts identically to lossy ones), which compounds this —
the counter is simultaneously too noisy locally and entirely blind in CI.

Observed on pm CLI 2026.8.5. Found when rolling pm health --strict-exit out as a CI gate across a
20-package fleet; the gate was demonstrated to pass a commit that had silently dropped a peer
agent's edit.

Activity

  1. coderabbitai commented on Aug 6, 2026

    @coderabbitai
    ⚠️ Possible Duplicate Issue(s)
    🔗 Related PRs

    #200 - Add validation self-repair and agent ergonomics primitives [closed]
    #614 - SDK-first field-aware merge safety and storage integrity [merged]
    #712 - SDK context integrity: atomic Plans, batch similarity, sparse settings, and tombstones [merged]
    #758 - Make branch merge decisions recoverable, replayable, and CI-enforced [merged]
    #783 - Harden SDK context integrity, merge safety, and agent-efficient output [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 7, 2026

    @unbraind
    OwnerAuthor

    Canonical PM item: pm-1j5j21. This link preserves the implementation lineage, dependencies, decisions, and verification evidence in the project system of record.

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