FAILURE MAP
← Case archive

FA-81166 / Music interval and transposition theory / Open access

Chord symbol to pitch classes: slash bass duplicated · case 01

C/E returns [4,0,4,7] with the third listed twice instead of [4,0,7].

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The bass note is prepended without removing it from its existing chord-tone position.

VERIFIED REPAIR

Restore the slash bass placement step so that it reads `if b in tones: tones.remove(b) tones.insert(0, b)`.

Unsuccessful approach: Rotating the chord to start on the bass reorders the remaining tones, so C/E returns [4,7,0].

Case contract

Input a chord symbol: root letter with optional # or b, a quality from "", m, dim, aug, 7, maj7, m7, m7b5, dim7, sus4, mMaj7, 6, and optionally "/" plus a bass note (letter with optional # or b and nothing else). Return pitch classes root-first in template order; for a slash chord the bass moves to the front (removed from its old position if it is a chord tone, otherwise added). Unknown or malformed symbols return None.

Why this case matters

Lead-sheet, accompaniment and guitar-chart tools expand chord symbols into voicings.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    base = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
    shapes = {'': [0, 4, 7], 'm': [0, 3, 7], 'dim': [0, 3, 6], 'aug': [0, 4, 8], '7': [0, 4, 7, 10], 'maj7': [0, 4, 7, 11], 'm7': [0, 3, 7, 10], 'm7b5': [0, 3, 6, 10], 'dim7': [0, 3, 6, 9], 'sus4': [0, 5, 7], 'mMaj7': [0, 3, 7, 11], '6': [0, 4, 7, 9]}
    def root(s):
        if not s or s[0] not in base:
            return None, s
        if s[1:2] in ('#', 'b'):
            return (base[s[0]] + (1 if s[1] == '#' else -1)) % 12, s[2:]
        return base[s[0]], s[1:]
    if not isinstance(x, str):
        return None
    head, slash, tail = x.partition('/')
    r, quality = root(head)
    if r is None or quality not in shapes:
        return None
    tones = [(r + i) % 12 for i in shapes[quality]]
    if slash:
        b, rest = root(tail)
        if b is None or rest:
            return None
        tones.insert(0, b)
    return tones
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('C', [0, 4, 7]), ('Cm', [0, 3, 7]), ('Bb', [10, 2, 5]), ('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('C/E', [4, 0, 7]), ('F/A', [9, 5, 0]), ('D/F#', [6, 2, 9])], [('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('F#m7b5', [6, 9, 0, 4]), ('Ebmaj7', [3, 7, 10, 2]), ('C/G', [7, 0, 4]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('Gb/Bb', [10, 6, 1])], [('Ebmaj7', [3, 7, 10, 2]), ('Gdim7', [7, 10, 1, 4]), ('Aaug', [9, 1, 5]), ('Dsus4', [2, 7, 9]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('G7/F', [5, 7, 11, 2]), ('B/D#', [3, 11, 6])], [('Dsus4', [2, 7, 9]), ('C/Bb', [10, 0, 4, 7]), ('Am/G', [7, 9, 0, 4]), ('Bb/D', [2, 10, 5]), ('Cmaj7/B', [11, 0, 4, 7]), ('Cb', [11, 3, 6]), ('D/F#', [6, 2, 9]), ('Ebm/Gb', [6, 3, 10])], [('C/E', [4, 0, 7]), ('G7/F', [5, 7, 11, 2]), ('Cb', [11, 3, 6]), ('Cbm', [11, 2, 6]), ('E#', [5, 9, 0]), ('A6', [9, 1, 4, 6]), ('D/F#', [6, 2, 9]), ('Gb/Bb', [10, 6, 1])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
oracle 0[0, 4, 7][0, 4, 7]Passed
oracle 1[0, 3, 7][0, 3, 7]Passed
oracle 2[10, 2, 5][10, 2, 5]Passed
oracle 3[10, 1, 5, 8][10, 1, 5, 8]Passed
oracle 4[10, 2, 5, 8][10, 2, 5, 8]Passed
oracle 5[4, 0, 4, 7][4, 0, 7]Failed
oracle 6[9, 5, 9, 0][9, 5, 0]Failed
oracle 7[6, 2, 6, 9][6, 2, 9]Failed

SHA-256 / 80854834ff2bb7e1350a3853f389e7c4e9294f46862de786966d5a438187bbaa

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    base = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
    shapes = {'': [0, 4, 7], 'm': [0, 3, 7], 'dim': [0, 3, 6], 'aug': [0, 4, 8], '7': [0, 4, 7, 10], 'maj7': [0, 4, 7, 11], 'm7': [0, 3, 7, 10], 'm7b5': [0, 3, 6, 10], 'dim7': [0, 3, 6, 9], 'sus4': [0, 5, 7], 'mMaj7': [0, 3, 7, 11], '6': [0, 4, 7, 9]}
    def root(s):
        if not s or s[0] not in base:
            return None, s
        if s[1:2] in ('#', 'b'):
            return (base[s[0]] + (1 if s[1] == '#' else -1)) % 12, s[2:]
        return base[s[0]], s[1:]
    if not isinstance(x, str):
        return None
    head, slash, tail = x.partition('/')
    r, quality = root(head)
    if r is None or quality not in shapes:
        return None
    tones = [(r + i) % 12 for i in shapes[quality]]
    if slash:
        b, rest = root(tail)
        if b is None or rest:
            return None
        tones = tones[tones.index(b):] + tones[:tones.index(b)] if b in tones else [b] + tones
    return tones
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('C', [0, 4, 7]), ('Cm', [0, 3, 7]), ('Bb', [10, 2, 5]), ('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('C/E', [4, 0, 7]), ('F/A', [9, 5, 0]), ('D/F#', [6, 2, 9])], [('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('F#m7b5', [6, 9, 0, 4]), ('Ebmaj7', [3, 7, 10, 2]), ('C/G', [7, 0, 4]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('Gb/Bb', [10, 6, 1])], [('Ebmaj7', [3, 7, 10, 2]), ('Gdim7', [7, 10, 1, 4]), ('Aaug', [9, 1, 5]), ('Dsus4', [2, 7, 9]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('G7/F', [5, 7, 11, 2]), ('B/D#', [3, 11, 6])], [('Dsus4', [2, 7, 9]), ('C/Bb', [10, 0, 4, 7]), ('Am/G', [7, 9, 0, 4]), ('Bb/D', [2, 10, 5]), ('Cmaj7/B', [11, 0, 4, 7]), ('Cb', [11, 3, 6]), ('D/F#', [6, 2, 9]), ('Ebm/Gb', [6, 3, 10])], [('C/E', [4, 0, 7]), ('G7/F', [5, 7, 11, 2]), ('Cb', [11, 3, 6]), ('Cbm', [11, 2, 6]), ('E#', [5, 9, 0]), ('A6', [9, 1, 4, 6]), ('D/F#', [6, 2, 9]), ('Gb/Bb', [10, 6, 1])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
oracle 0[0, 4, 7][0, 4, 7]Passed
oracle 1[0, 3, 7][0, 3, 7]Passed
oracle 2[10, 2, 5][10, 2, 5]Passed
oracle 3[10, 1, 5, 8][10, 1, 5, 8]Passed
oracle 4[10, 2, 5, 8][10, 2, 5, 8]Passed
oracle 5[4, 7, 0][4, 0, 7]Failed
oracle 6[9, 0, 5][9, 5, 0]Failed
oracle 7[6, 9, 2][6, 2, 9]Failed

SHA-256 / 115248e0fb8c89091cbdf451e0dd981d287cc5ff033ab1518b28535fdb6a030f

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    base = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
    shapes = {'': [0, 4, 7], 'm': [0, 3, 7], 'dim': [0, 3, 6], 'aug': [0, 4, 8], '7': [0, 4, 7, 10], 'maj7': [0, 4, 7, 11], 'm7': [0, 3, 7, 10], 'm7b5': [0, 3, 6, 10], 'dim7': [0, 3, 6, 9], 'sus4': [0, 5, 7], 'mMaj7': [0, 3, 7, 11], '6': [0, 4, 7, 9]}
    def root(s):
        if not s or s[0] not in base:
            return None, s
        if s[1:2] in ('#', 'b'):
            return (base[s[0]] + (1 if s[1] == '#' else -1)) % 12, s[2:]
        return base[s[0]], s[1:]
    if not isinstance(x, str):
        return None
    head, slash, tail = x.partition('/')
    r, quality = root(head)
    if r is None or quality not in shapes:
        return None
    tones = [(r + i) % 12 for i in shapes[quality]]
    if slash:
        b, rest = root(tail)
        if b is None or rest:
            return None
        if b in tones:
            tones.remove(b)
        tones.insert(0, b)
    return tones
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('C', [0, 4, 7]), ('Cm', [0, 3, 7]), ('Bb', [10, 2, 5]), ('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('C/E', [4, 0, 7]), ('F/A', [9, 5, 0]), ('D/F#', [6, 2, 9])], [('Bbm7', [10, 1, 5, 8]), ('Bb7', [10, 2, 5, 8]), ('F#m7b5', [6, 9, 0, 4]), ('Ebmaj7', [3, 7, 10, 2]), ('C/G', [7, 0, 4]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('Gb/Bb', [10, 6, 1])], [('Ebmaj7', [3, 7, 10, 2]), ('Gdim7', [7, 10, 1, 4]), ('Aaug', [9, 1, 5]), ('Dsus4', [2, 7, 9]), ('F/A', [9, 5, 0]), ('Bb/D', [2, 10, 5]), ('G7/F', [5, 7, 11, 2]), ('B/D#', [3, 11, 6])], [('Dsus4', [2, 7, 9]), ('C/Bb', [10, 0, 4, 7]), ('Am/G', [7, 9, 0, 4]), ('Bb/D', [2, 10, 5]), ('Cmaj7/B', [11, 0, 4, 7]), ('Cb', [11, 3, 6]), ('D/F#', [6, 2, 9]), ('Ebm/Gb', [6, 3, 10])], [('C/E', [4, 0, 7]), ('G7/F', [5, 7, 11, 2]), ('Cb', [11, 3, 6]), ('Cbm', [11, 2, 6]), ('E#', [5, 9, 0]), ('A6', [9, 1, 4, 6]), ('D/F#', [6, 2, 9]), ('Gb/Bb', [10, 6, 1])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
oracle 0[0, 4, 7][0, 4, 7]Passed
oracle 1[0, 3, 7][0, 3, 7]Passed
oracle 2[10, 2, 5][10, 2, 5]Passed
oracle 3[10, 1, 5, 8][10, 1, 5, 8]Passed
oracle 4[10, 2, 5, 8][10, 2, 5, 8]Passed
oracle 5[4, 0, 7][4, 0, 7]Passed
oracle 6[9, 5, 0][9, 5, 0]Passed
oracle 7[6, 2, 9][6, 2, 9]Passed

SHA-256 / eb72f5d763c630c97bd4a310da45b275a7283ab6666417925f265451589eb65c

Verification & scope

A deterministic bounded teaching model with a stipulated toy contract; it is not a complete music notation or theory engine. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:50:00.563876+00:00.

Case digest / 4668b9be9d9fe65dd8b4593a15f80966344a184078317c26d0b0763b92e412e4