Skip to content

Parse dimmaj7 as a diminished-major seventh, not a dim7 [patch] - #270

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/semantics-269-dimmaj7-parse
Sep 23, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/semantics-269-dimmaj7-parse

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #269

DetermineSeventh asked the diminished question first:

if (quality == ChordQuality.Diminished && body.Contains("dim", StringComparison.Ordinal) && hasSeven)
{
    return SeventhType.Diminished;
}

if (hasMaj7) { return SeventhType.Major; }

That branch tests only for "dim" and a '7'. "dimmaj7" has both, so the diminished-major seventh — a real chord of the harmonic and melodic minor, and exactly what AppendDiminished emits for Diminished + Major — never reached the hasMaj7 line. Swapping the two checks is the whole fix, as the issue proposes.

Why the round-trip suite did not catch it

Worth calling out, because it changes where the test belongs. ChordRoundTripTests.Corpus starts from a symbol and asserts Parse(s) equals Parse(Parse(s).ToString()). For "Cdimmaj7" that passes on the broken code: it parses to a dim7, formats as "Cdim7", and re-parses as a dim7 again. A stable round trip of the wrong chord. Adding the symbol to the corpus would have proved nothing.

The chord object is the only honest starting point, since being the inverse of the formatter is the contract under test, so DiminishedMajorSeventhRoundTrips builds new Chord { Quality = Diminished, Seventh = Major }, asserts it formats as "Cdimmaj7", and asserts the re-parse comes back equal.

Tests

Four cases:

test asserts
ChordRoundTripTests.DiminishedMajorSeventhRoundTrips the issue's first acceptance criterion, from the chord object out and back
ChordTests.Parse_DiminishedMajorSeventh_IsDiminishedTriadWithMajorSeventh Chord.Parse("Cdimmaj7").Seventh is Major, quality still Diminished
ChordTests.Parse_DiminishedSeventh_StillHasTheDiminishedSeventh the issue's second criterion — "Cdim7" is untouched by the reorder
ChordTests.ChordTones_DiminishedMajorSeventh_HasTheMajorSeventhNotTheDoubleFlatSeventh [0, 3, 6, 11] rather than [0, 3, 6, 9]

That last one is there because the misparse is not only a labelling error. ChordTones maps SeventhType.Diminished to offset 9 and Major to 11, so a wrong parse is a semitone error in Voice() and in anything that plays the result — the silent half of the bug, and the half a Seventh assertion alone would not pin.

Confirmed the tests depend on the change by restoring the original branch order and re-running: 3 failed, 1305 passed. The "Cdim7" case passes either way by design — it is the guard on the reorder, not evidence for it.

Full suite green on the fix: 1316 total, 0 failed, 1308 passed, 8 skipped (the skips are the pre-existing Windows-only path tests). Release build clean, zero warnings.

Not covered

hasMaj7 is body.Contains("maj") || body.Contains("M7") || body.Contains('Δ'), so a bare "Cmaj" with no digit already reports SeventhType.Major. That is pre-existing and untouched here — the reorder can only change a body carrying both "dim" and a major-seventh spelling, which is "dimmaj7" and its variants. "°maj7" was already correct, since the '°' spelling sets the quality without putting "dim" in the body, so the diminished branch never fired for it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UscjStBJdW3uHm5NR289DX


Generated by Claude Code

DetermineSeventh tested the diminished-seventh branch before the major
seventh. That branch asks only whether the body contains "dim" and a '7',
which "dimmaj7" does, so the diminished-major seventh the formatter emits
parsed back as a fully-diminished seventh. The round trip was broken and
ChordTones placed the bb7 (9) where the major seventh (11) belongs,
putting Voice() and playback a semitone out.

The major-seventh check now runs first. "dim7" is unaffected: it carries
no "maj".

Fixes #269

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UscjStBJdW3uHm5NR289DX
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chord.Parse misreads "dimmaj7" (diminished major seventh) as a fully-diminished seventh

2 participants