Skip to content

Key.RomanNumeralOf drops the seventh from a fully diminished 7th ("Bdim7" → "vii°"), and a half-diminished 7th round-trips to fully diminished #289

Description

@matt-edmondson

What's wrong

QualitySuffix in Semantics.Music/Key.cs has these arms:

SeventhType.Diminished => chord.Quality == ChordQuality.Diminished ? "" : "7",
...
ChordQuality.Diminished => "°",

This produces two wrong results:

  • A fully diminished seventh (Diminished quality with a Diminished seventh) gets "°" plus an empty suffix, so Bdim7 becomes vii°. That is the same numeral a plain Bdim triad gets, so the seventh is lost.
  • A half-diminished seventh (m7b5, which is Diminished quality with a Dominant seventh) gets "°7". But ChordFromRomanNumeral("vii°7") reads "°" as "dim" and builds Bdim7, the fully diminished chord. RomanNumeralParseTests.Parse_LeadingToneDiminishedSeventh asserts that reading, so the round trip changes the chord.

Repro (C major)

Key c = Key.Create(PitchClass.Create(0), Mode.Major);
c.RomanNumeralOf(Chord.Parse("Bdim"));   // "vii°"
c.RomanNumeralOf(Chord.Parse("Bdim7"));  // "vii°"   expected "vii°7"
c.ChordFromRomanNumeral(c.RomanNumeralOf(Chord.Parse("Bm7b5")));
// "vii°7" -> Bdim7 (Seventh = Diminished, 9-semitone seventh); expected half-diminished (10 semitones)

This was reproduced with a temporary MSTest on net10.0 at 8c06aba.

Why it matters

The two sevenths on the leading tone are the most common place Roman-numeral analysis has to tell fully diminished and half-diminished apart: vii°7 in minor, viiø7 in major. Right now a triad and a diminished seventh get the same label. A half-diminished chord also changes identity when it goes through RomanNumeralOf and then ChordFromRomanNumeral.

Suggested fix / acceptance criteria

  • QualitySuffix emits °7 for Diminished + Diminished seventh and ø7 for Diminished + Dominant seventh (half-diminished).
  • ChordFromRomanNumeral reads ø (and ø7) as m7b5.
  • vii°7 and viiø7 are added to Parse_IsInverseOfRomanNumeralOf, so both survive the round trip.

This is separate from #281, which is about Chord.Parse("C°7").

Activity

  1. matt-edmondson commented on Sep 27, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't workingreadyFully specified; implement as written

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions