FAILURE MAP
← Case archive

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

Enharmonic respelling on a requested letter: respelled note keeps the sounding octave number · case 01

C4 respelled on B is printed B#4 and B3 on C is printed Cb3, an octave off in both cases.

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

ROOT CAUSE

The octave in the output is derived from the sounding pitch instead of from the chosen letter octave.

VERIFIED REPAIR

Restore the spelled octave output step so that it reads `return letter + sign + str(o)`.

Unsuccessful approach: Using the letter octave only for sharp spellings still prints Cb3 for a Cb that belongs to octave 4.

Case contract

Input [note, letter]. note: letter, accidentals (# +1, b -1, x +2), nonnegative octave. Return the same sounding pitch spelled on the requested letter in whichever octave needs the fewest accidentals, rendering +2 x, +1 #, -1 b, -2 bb; octave numbers follow the letter (B#3 = C4). If more than two accidentals would be needed return None.

Why this case matters

Pitch spelling and interval arithmetic decide the notes that notation, transposition and analysis tools display or play.

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}
    if not isinstance(x, list) or len(x) != 2:
        return None
    note, letter = x
    if letter not in base or not isinstance(note, str) or len(note) < 2 or note[0] not in base:
        return None
    i = 1
    acc = 0
    while i < len(note) and note[i] in '#bx':
        acc += {'#': 1, 'b': -1, 'x': 2}[note[i]]
        i += 1
    if not note[i:].isdigit():
        return None
    m = 12 * (int(note[i:]) + 1) + base[note[0]] + acc
    best = None
    for o in range(m // 12 - 2, m // 12 + 1):
        shift = m - (12 * (o + 1) + base[letter])
        if abs(shift) <= 2 and (best is None or abs(shift) < abs(best[1])):
            best = (o, shift)
    if best is None:
        return None
    o, shift = best
    sign = {2: 'x', 1: '#', 0: '', -1: 'b', -2: 'bb'}[shift]
    return letter + sign + str(m // 12 - 1)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'B'], 'B#3'), (['B3', 'C'], 'Cb4'), (['E4', 'F'], 'Fb4'), (['F4', 'E'], 'E#4'), (['G4', 'A'], 'Abb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C0', 'B'], 'B#-1')], [(['B3', 'C'], 'Cb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C4', 'E'], None), (['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['B0', 'C'], 'Cb1')], [(['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['C0', 'B'], 'B#-1'), (['B4', 'C'], 'Cb5')], [(['B3', 'C'], 'Cb4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['C5', 'B'], 'B#4')], [(['C4', 'B'], 'B#3'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['F4', 'G'], 'Gbb4'), (['C4', 'C'], 'C4'), (['Bb3', 'A'], 'A#3'), (['B4', 'C'], 'Cb5')]]
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 0B#4B#3Failed
oracle 1Cb3Cb4Failed
oracle 2Fb4Fb4Passed
oracle 3E#4E#4Passed
oracle 4Abb4Abb4Passed
oracle 5Gx4Gx4Passed
oracle 6Dbb4Dbb4Passed
oracle 7B#0B#-1Failed

SHA-256 / 35b57dfbc56293107557e729b18a5dc7b6aeb9f6cdaf5b0325e735db734426b3

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}
    if not isinstance(x, list) or len(x) != 2:
        return None
    note, letter = x
    if letter not in base or not isinstance(note, str) or len(note) < 2 or note[0] not in base:
        return None
    i = 1
    acc = 0
    while i < len(note) and note[i] in '#bx':
        acc += {'#': 1, 'b': -1, 'x': 2}[note[i]]
        i += 1
    if not note[i:].isdigit():
        return None
    m = 12 * (int(note[i:]) + 1) + base[note[0]] + acc
    best = None
    for o in range(m // 12 - 2, m // 12 + 1):
        shift = m - (12 * (o + 1) + base[letter])
        if abs(shift) <= 2 and (best is None or abs(shift) < abs(best[1])):
            best = (o, shift)
    if best is None:
        return None
    o, shift = best
    sign = {2: 'x', 1: '#', 0: '', -1: 'b', -2: 'bb'}[shift]
    return letter + sign + str(o if shift >= 0 else m // 12 - 1)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'B'], 'B#3'), (['B3', 'C'], 'Cb4'), (['E4', 'F'], 'Fb4'), (['F4', 'E'], 'E#4'), (['G4', 'A'], 'Abb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C0', 'B'], 'B#-1')], [(['B3', 'C'], 'Cb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C4', 'E'], None), (['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['B0', 'C'], 'Cb1')], [(['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['C0', 'B'], 'B#-1'), (['B4', 'C'], 'Cb5')], [(['B3', 'C'], 'Cb4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['C5', 'B'], 'B#4')], [(['C4', 'B'], 'B#3'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['F4', 'G'], 'Gbb4'), (['C4', 'C'], 'C4'), (['Bb3', 'A'], 'A#3'), (['B4', 'C'], 'Cb5')]]
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 0B#3B#3Passed
oracle 1Cb3Cb4Failed
oracle 2Fb4Fb4Passed
oracle 3E#4E#4Passed
oracle 4Abb4Abb4Passed
oracle 5Gx4Gx4Passed
oracle 6Dbb4Dbb4Passed
oracle 7B#-1B#-1Passed

SHA-256 / 716da2facb170d3687329c7e209fdadfe573d0d931cb0eee04b0db8793c3f709

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}
    if not isinstance(x, list) or len(x) != 2:
        return None
    note, letter = x
    if letter not in base or not isinstance(note, str) or len(note) < 2 or note[0] not in base:
        return None
    i = 1
    acc = 0
    while i < len(note) and note[i] in '#bx':
        acc += {'#': 1, 'b': -1, 'x': 2}[note[i]]
        i += 1
    if not note[i:].isdigit():
        return None
    m = 12 * (int(note[i:]) + 1) + base[note[0]] + acc
    best = None
    for o in range(m // 12 - 2, m // 12 + 1):
        shift = m - (12 * (o + 1) + base[letter])
        if abs(shift) <= 2 and (best is None or abs(shift) < abs(best[1])):
            best = (o, shift)
    if best is None:
        return None
    o, shift = best
    sign = {2: 'x', 1: '#', 0: '', -1: 'b', -2: 'bb'}[shift]
    return letter + sign + str(o)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'B'], 'B#3'), (['B3', 'C'], 'Cb4'), (['E4', 'F'], 'Fb4'), (['F4', 'E'], 'E#4'), (['G4', 'A'], 'Abb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C0', 'B'], 'B#-1')], [(['B3', 'C'], 'Cb4'), (['A4', 'G'], 'Gx4'), (['C4', 'D'], 'Dbb4'), (['C4', 'E'], None), (['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['B0', 'C'], 'Cb1')], [(['C#4', 'D'], 'Db4'), (['Db4', 'C'], 'C#4'), (['B#3', 'C'], 'C4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['C0', 'B'], 'B#-1'), (['B4', 'C'], 'Cb5')], [(['B3', 'C'], 'Cb4'), (['Cb4', 'B'], 'B3'), (['Fx4', 'G'], 'G4'), (['G4', 'F'], 'Fx4'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['C5', 'B'], 'B#4')], [(['C4', 'B'], 'B#3'), (['D4', 'C'], 'Cx4'), (['B0', 'C'], 'Cb1'), (['E4', 'D'], 'Dx4'), (['F4', 'G'], 'Gbb4'), (['C4', 'C'], 'C4'), (['Bb3', 'A'], 'A#3'), (['B4', 'C'], 'Cb5')]]
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 0B#3B#3Passed
oracle 1Cb4Cb4Passed
oracle 2Fb4Fb4Passed
oracle 3E#4E#4Passed
oracle 4Abb4Abb4Passed
oracle 5Gx4Gx4Passed
oracle 6Dbb4Dbb4Passed
oracle 7B#-1B#-1Passed

SHA-256 / 9012f4dc4939e13a75f51d914ceb39e05738e9bd9ce525c48bb7679ae30ac78b

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:49:58.613165+00:00.

Case digest / b5e6d169ed4284968004a18abd82529fa823875bd378e49646b0128d3b3a9919