You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AppendQualityAndSeventh writes 7/maj7 first (Chord.cs#L590-L624). AppendTensions then appends the natural extension number after it (Chord.cs#L645-L688). The resulting "canonical symbol" is not something any chart uses:
Parsed from
ToString()
C9 / Cm9 / Cmaj9 / Caug9
C79 / Cm79 / Cmaj79 / Caug79
C11 / C13 / Cmaj13
C711 / C713 / Cmaj713
C7sus4 / C9sus4
Csus47 / Csus479
C6/9 / Cm6/9
C6add9 / Cm6add9
Progression.Parse("4/4 Dm9 | G13 | Cmaj9 | G7sus4").ToString() gives "4/4 Dm79 G713 Cmaj79 Gsus47". Section.ToString() and Arrangement.ToString() inherit the same output.
Parse(ToString()) still round-trips, and the round-trip corpus (ChordRoundTripTests.cs) compares only the parsed structure. Because no test pins the text, this was never caught. The type-safe factories spec calls the output a "canonical symbol (e.g. Cmaj7, C/G)" (spec L95-L153).`` A chart format meant for people should print what a musician writes.
Suggested fix
When a natural extension number is emitted for a seventh chord, let it replace the 7: dominant 7+9 becomes 9, and maj7+9 becomes maj9.
Put sus after the number: 7sus4, 9sus4.
Write a 6 with an added 9 as 6/9, which Chord.Parse already accepts.
Coordinate with #300, which reworks the same AppendTensions branch for 7add13. That issue is about losing the round-trip; this one is the ordinary C9 → C79 case.
Acceptance criteria
ToString() returns C9, Cm9, Cmaj9, C11, C13, Cmaj13, C7sus4, C9sus4, C6/9 and Cm6/9 for the inputs in the table.
The round-trip corpus gains a text assertion, Parse(s).ToString() == s for canonical inputs, alongside the structural one.
Recommended next step: Check on main that Chord.Parse("C9").ToString() == "C9" (and the rest of the acceptance table), then close this issue and its siblings as completed, referencing #362.
Activity
matt-edmondson commentedon Sep 30, 2026
Covered by #360, which redesigns this bug cluster as a whole. Implement it through that issue rather than individually.
Generated by Claude Code
[minor] Redesign chord symbols; fixes #300, #322, #336, #340, #341, #344
matt-edmondson commentedon Oct 5, 2026
Triage
enhancement, but this is wrong canonical output)Semantics.Musicchord symbol writerC9,Cmaj9,C7sus4,C6/9, … print as themselves). Redesign chord symbols: one grammar for Chord.Parse and Chord.ToString (fixes #300 #322 #336 #340 #341 #344 #345 #346 #358) #360's sub-issue summary shows only 1 of 9 closed, though. PR Redesign chord symbols around a shared grammar #362 apparently didn't use closing keywords for the sub-issues, so Chord round-trip breaks for a seventh with add11/add13: "C7add13" prints as "C713", which reparses as C13 with a 9th and 11th #300, Chord.Parse reads "CM9", "CM11" and "CM13" as dominant chords (Bb, not B), and "CmM9" as a plain Cm9 #322, Chord.ToString() writes an altered tension straight after the root: "C(#11)" prints as "C#11" and reparses as a C# dominant eleventh #336, Chord.Parse reads "Cm7#5", "Cm#5" and "Cm+" as augmented chords with a major third (Cm7#5 comes back as Caug7) #340, Chord.Parse stacks the natural 9th/11th under an altered one: "C13#11" sounds both F and F#, "C13b9" both Db and D #341, Chord.Parse drops everything after the slash-bass note: "C/Gm", "C/E/G" and "C/Bb7" parse as "C/G", "C/E" and "C/A#" #344, Chord.Parse reads "-"/"+" alteration signs as chord quality: "C7-9" becomes Cm9, "C7-5" becomes Cm7, "Cmaj7-5" becomes CmMaj7, "C7+9" becomes Caug9 #345, Chord.Parse accepts quality characters in any order and any number: "C7M", "C57", "C77" and "C7m" all parse as C7 #346 and this issue are probably all still open despite being fixed.Recommended next step: Check on
mainthatChord.Parse("C9").ToString() == "C9"(and the rest of the acceptance table), then close this issue and its siblings as completed, referencing #362.Generated by Claude Code