push --dry-run overcounts commits_sent by ignoring remote have negotiation
Background
muse push --dry-run reports an inflated commits_sent count that does not match what the live push actually sends. Observed during the phase1-merge-engine merge (2026-06-12):
muse push local main --force-with-lease --dry-runreportedcommits_sent: 1271- The live push sent
commits_sent: 0
The same pattern appeared earlier in the same session:
muse push local dev --force-with-lease --dry-run(muse repo) reportedcommits_sent: 16, live sent0muse push local dev --force-with-lease --dry-run(musehub repo) reportedcommits_sent: 4, live sent0
The root cause: the dry-run walks the local DAG and counts commits reachable from the local tip but not reachable from the remote branch tip being pushed to. It does not simulate the full have negotiation that the live push performs — specifically, it does not account for other remote branch tips the server already knows about.
In the live push, step 0 fetches all remote branch heads (dev, main, task/phase1-merge-engine, etc.) and adds them all to the have set. Step 1 then computes want - have, which correctly returns 0 when the target commit is already reachable via another remote branch. The dry-run skips this and computes want - {remote_branch_tip} only, which counts the entire history between the two tips.
This is misleading and erodes trust in dry-run output. An agent or human seeing commits_sent: 1271 before a push will reasonably pause and investigate, wasting time.
Goal
muse push --dry-run produces a commits_sent count that matches what the live push would actually send, using the same have negotiation logic.
Phases
Phase 1 — Reproduce and characterise
Deliverables:
RC_01— Write a test that sets up a repo with two branches on the remote (e.g.devandmain), advancesmainlocally to matchdev, then asserts thatpush --dry-run mainreportscommits_sent: 0(matching the live push). Test must be red before the fix.
Phase 2 — Fix dry-run have negotiation
Deliverables:
FX_01— Update the dry-run path inmuse pushto fetch all remote branch heads (same as step 0 of the live push) and use the full set ashavewhen computingnew_commits.FX_02—RC_01is now green.FX_03— Existing push tests remain green.
Phase 3 — Validate the fix end-to-end
Deliverables:
VL_01— Run the exact scenario that triggered this report: localmainfast-forwarded to match remotedev;push --dry-run mainreportscommits_sent: 0.VL_02— Confirm that when there ARE genuinely new commits, dry-run still reports the correct non-zero count.
Acceptance criteria
muse push --dry-runcommits_sentalways matches what the live push sends.RC_01,FX_01–03,VL_01–02all checked off.- No regression in existing push tests.
Out of scope
- Changing the live push
havenegotiation (it is already correct). - Dry-run accuracy for
objects_sent(separate concern, lower priority).