Skip to content

Read "C6/9" as a six-nine chord, not a slash bass - #250

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/nice-davinci-zpyofu
Sep 17, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/nice-davinci-zpyofu

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #248

The problem

Chord.TryParse treated everything after the first / as a slash-bass note, calling TryParseRoot on it — which requires the first character to be a note letter A–G. The six-nine symbol found on most jazz lead sheets (C6/9, Cm6/9, F6/9) is not a slash chord at all: the /9 stacks an added ninth on a sixth chord. "9" is not a note letter, so Chord.TryParse("C6/9") returned false and Chord.Parse("C6/9") threw FormatException — even though the package documents sixths, ninths and slash bass as supported.

The change

Semantics.Music/Chord.cs:

  • TryReadRoot no longer fails outright when the post-slash token is not a note letter. It first asks TryRewriteSixNine whether the symbol is the six-nine idiom — a bare 9 directly after a 6.
  • TryRewriteSixNine rewrites C6/9 to C6add9, the spelling ConsumeModifiers already understands. Routing through add9 rather than a bare 9 is what keeps the ninth an addition: a bare 9 would run through ApplyExtensions and imply a dominant seventh the chord does not have.
  • The rewritten symbol is read from the top, so a real slash bass after the idiom still parses: C6/9/G is a six-nine over a G bass.

C6/9 parses to Sixth = Natural, Tensions |= Nine, Seventh = None, no bass — tones [0, 4, 7, 9, 14], which is Cadd9 plus the natural sixth.

The guard is deliberately narrow. A 9 not preceded by 6 (C/9), or one followed by another digit (C6/11), is still rejected, so no previously-invalid symbol other than the idiom starts parsing.

ToString() is unchanged and emits C6add9, which re-parses to an equal chord.

Tests

Semantics.Test/Music/ChordTests.cs — five new tests: the parse shape, the ChordTones() identity against Cadd9, the minor form, the idiom over a real slash bass, and the negative cases that must still fail.

Semantics.Test/Music/ChordRoundTripTests.csC6/9, Cm6/9 and C6/9/G added to the round-trip corpus.

Verified by reverting Chord.cs alone and rebuilding: 5 of the 23 chord tests fail (Parse_SixNine_IsAnAddedNinthOverASixth_NotASlashBass, ChordTones_SixNine_IsAdd9PlusTheNaturalSixth, Parse_MinorSixNine, Parse_SixNine_OverASlashBass, CanonicalOutputRoundTrips). Parse_SlashOverANonNoteStillFails passes either way by design — it pins the guard against over-broadening.

With the fix restored, the full suite is green: 1255 tests, 0 failed (8 skipped, Windows-only).

Semantics.Music builds clean across all five target frameworks, and the local SonarCloud analyzer pass documented in CLAUDE.md reports nothing new in the touched files.

Docs

Semantics.Music/README.md and the Chord summary now list the six-nine idiom among the supported symbols.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MNK9bgoZL8ocxTSncTfDEp


Generated by Claude Code

Chord.TryParse treated everything after the first "/" as a slash-bass note
and required it to start with a note letter, so the six-nine symbol found on
most jazz lead sheets — C6/9, Cm6/9 — failed to parse at all.

When the token after the slash is a bare "9" directly following a "6", read
it as the six-nine idiom instead: rewrite it to the equivalent "add9"
spelling the modifier reader already understands, so the ninth is added
rather than implying a dominant seventh the way a bare "9" would. Reading
the rewritten symbol from the top means a real slash bass after it still
parses, as in "C6/9/G".

Everything else after a slash is still required to be a note letter, so
"C/9" and "C6/11" remain rejected.

Fixes #248

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNK9bgoZL8ocxTSncTfDEp
@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 cannot parse the "6/9" chord symbol (and any bare tension after a slash)

2 participants