Implement ^ref exclusion prefix syntax in muse traversal commands
Background
Git supports two syntactically different but semantically equivalent ways to express commit exclusion in traversal commands:
- Range syntax —
A..B: commits reachable from B but not from A. Already implemented inmuse rev-list. - Exclusion prefix —
^ref: exclude all commits reachable from ref. More flexible because multiple exclusions compose:git rev-list A ^B ^C ^D.
Git uses ^ref idiomatically in rev-list, log, cherry, and anywhere commit traversal is involved. Agents have deep ^ref muscle memory from git training data. When they write muse rev-list task/feature ^dev --json they get a hard CLI error with no hint about the correct form.
Additionally, muse log currently accepts only a single [ref] — it has no range or exclusion syntax at all. This means the idiomatic git log main..feat (show what's on feat that isn't on main) has no muse equivalent, forcing agents to reach for rev-list as a workaround or fall back to the broken ^ syntax.
Discovered during phase1-merge-engine merge prep (2026-06-12).
Goal
Every command in muse that idiomatically accepts commit ranges or exclusions in git supports both A..B range syntax and ^ref exclusion prefix. Agents can write natural commit traversal expressions without hitting CLI errors or unexpected output.
Phases
Phase 1 — Audit: where does git use ^ref and A..B?
Deliverable:
AU_01— Produce a table mapping git commands that accept^ref/A..Bto their current muse equivalents, noting which already support range syntax and which do not. At minimum cover:rev-list,log,diff,commit-graph,cherry.
This table drives Phases 2 and 3. Scope is limited to commands already implemented in muse — no new commands.
Phase 2 — ^ref exclusion prefix support
Add ^ref as a first-class exclusion prefix to every traversal command identified in Phase 1.
Deliverables:
EX_01—muse rev-list ^ref— single exclusion:muse rev-list HEAD ^mainequivalent tomuse rev-list main..HEAD.EX_02—muse rev-listmultiple exclusions:muse rev-list A ^B ^C— commits reachable from A but not from B or C.EX_03—muse log ^ref— same semantics as rev-list, applied to log output.EX_04— Any additional commands surfaced byAU_01that idiomatically accept^refin git.
Each deliverable requires a failing test written first (TDD), named test_EX_NN_*.
Phase 3 — A..B range syntax in muse log
muse log currently accepts only a single [ref]. Add range support so muse log main..feat works.
Deliverables:
RG_01—muse log A..B— shows commits on B not on A, newest-first, in standard log format.RG_02—muse log A..B --json— same, structured output.RG_03—muse log A..B --oneline— compact form.RG_04—muse log A..Bcomposes with existing filters:--author,--since,--touches,--max-count.
Each deliverable requires a failing test written first (TDD), named test_RG_NN_*.
Phase 4 — Glossary and agent-guide
Deliverables:
GL_01— Add^ref→muse rev-list A..B/muse rev-list A ^Brow to the Gitism Glossary table inmuse/docs/agent-guide.md.GL_02— Addgit log A ^B→muse log A..Brow to the same table.GL_03— Add a note explaining direction:A..B= "reachable from B but not A" (easy to mix up).GL_04— grep gate:grep -n " \^[a-zA-Z]" docs/agent-guide.mdreturns zero matches (no surviving^refgitisms in the guide itself).
Acceptance criteria
muse rev-list HEAD ^main --jsonexits 0 and returns the same commit set asmuse rev-list main..HEAD --json.muse rev-list A ^B ^C --jsoncorrectly excludes commits reachable from both B and C.muse log main..feat --jsonexits 0 and returns commits on feat not on main.- All
EX_*,RG_*, andGL_*deliverables checked off. - No
^refsyntax survives in agent-guide.md.
Out of scope
- Three-dot (
A...B) symmetric difference — separate ticket if needed. - New traversal commands not already in muse.
- Changing
muse diffpositional argument handling (it already takescommit_a commit_bexplicitly).