FA-80916 / Music interval and transposition theory / Open access
Spelled transposition by named interval: letter moved by the full generic number · case 01
A major third up from C is spelled on F (Fb4) instead of E4, because the letter advances one step too far.
ROOT CAUSE
The letter displacement uses the generic number itself, but a third spans only two letter steps.
VERIFIED REPAIR
Restore the letter step count step so that it reads `sign * (number - 1)`.
Unsuccessful approach: Reducing the displacement modulo seven drops compound octaves and turns every downward move into an upward letter move.
Case contract
Input [note, interval, direction]. note: letter, # or b accidentals, nonnegative octave. interval: quality letter (P, M, m, A, d) and generic number >= 1 with perfect-class numbers 1, 4, 5 (compounds reduce by 7). Direction up or down moves the letter by number-1 steps and the pitch by the interval size; the result is spelled with the moved letter, octave taken from the letter position. Results needing more than two accidentals, or a negative octave, 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) != 3:
return None
note, ivl, direction = x
if direction not in ('up', 'down'):
return None
if 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 '#b':
acc += 1 if note[i] == '#' else -1
i += 1
if not note[i:].isdigit():
return None
octave = int(note[i:])
if not isinstance(ivl, str) or len(ivl) < 2 or not ivl[1:].isdigit():
return None
quality = ivl[0]
number = int(ivl[1:])
if number < 1:
return None
simple = (number - 1) % 7 + 1
perfect = simple in (1, 4, 5)
offsets = {'P': 0, 'A': 1, 'd': -1} if perfect else {'M': 0, 'm': -1, 'A': 1, 'd': -2}
if quality not in offsets:
return None
size = [0, 2, 4, 5, 7, 9, 11][simple - 1] + 12 * ((number - 1) // 7) + offsets[quality]
sign = 1 if direction == 'up' else -1
idx = 'CDEFGAB'.index(note[0]) + 7 * octave + sign * number
pitch = 12 * octave + base[note[0]] + acc + sign * size
letter = 'CDEFGAB'[idx % 7]
new_octave = idx // 7
new_acc = pitch - (12 * new_octave + base[letter])
if abs(new_acc) > 2 or new_octave < 0:
return None
return letter + ('#' * new_acc if new_acc > 0 else 'b' * -new_acc) + str(new_octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['Cb4', 'd3', 'up'], None), (['C4', 'M9', 'up'], 'D5'), (['C0', 'm2', 'down'], None), (['C4', 'X3', 'up'], None), (['C4', 'M4', 'up'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['C4', 'M10', 'down'], 'Ab2'), (['C4', 'M4', 'up'], None), (['C4', 'P3', 'up'], None), (['C4', 'M3', 'sideways'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'P8', 'up'], 'C5'), (['C5', 'P15', 'down'], 'C3')], [(['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['C4', 'M9', 'up'], 'D5'), (['D4', 'd4', 'down'], 'A#3')], [(['E4', 'A4', 'up'], 'A#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['Db4', 'd3', 'up'], 'Fbb4'), (['C4', 'P8', 'up'], 'C5'), (['C4', 'M10', 'down'], 'Ab2'), (['F4', 'A4', 'down'], 'Cb4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | Fb4 | E4 | Failed |
| oracle 1 | G#3 | Ab3 | Failed |
| oracle 2 | Dbb4 | C4 | Failed |
| oracle 3 | None | None | Passed |
| oracle 4 | Ebb5 | D5 | Failed |
| oracle 5 | None | None | Passed |
| oracle 6 | None | None | Passed |
| oracle 7 | None | None | Passed |
SHA-256 / 025b196689b08770daa015dd9b7b548e535f0ea4557d0bd077ac02f0072e2674
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) != 3:
return None
note, ivl, direction = x
if direction not in ('up', 'down'):
return None
if 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 '#b':
acc += 1 if note[i] == '#' else -1
i += 1
if not note[i:].isdigit():
return None
octave = int(note[i:])
if not isinstance(ivl, str) or len(ivl) < 2 or not ivl[1:].isdigit():
return None
quality = ivl[0]
number = int(ivl[1:])
if number < 1:
return None
simple = (number - 1) % 7 + 1
perfect = simple in (1, 4, 5)
offsets = {'P': 0, 'A': 1, 'd': -1} if perfect else {'M': 0, 'm': -1, 'A': 1, 'd': -2}
if quality not in offsets:
return None
size = [0, 2, 4, 5, 7, 9, 11][simple - 1] + 12 * ((number - 1) // 7) + offsets[quality]
sign = 1 if direction == 'up' else -1
idx = 'CDEFGAB'.index(note[0]) + 7 * octave + sign * (number - 1) % 7
pitch = 12 * octave + base[note[0]] + acc + sign * size
letter = 'CDEFGAB'[idx % 7]
new_octave = idx // 7
new_acc = pitch - (12 * new_octave + base[letter])
if abs(new_acc) > 2 or new_octave < 0:
return None
return letter + ('#' * new_acc if new_acc > 0 else 'b' * -new_acc) + str(new_octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['Cb4', 'd3', 'up'], None), (['C4', 'M9', 'up'], 'D5'), (['C0', 'm2', 'down'], None), (['C4', 'X3', 'up'], None), (['C4', 'M4', 'up'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['C4', 'M10', 'down'], 'Ab2'), (['C4', 'M4', 'up'], None), (['C4', 'P3', 'up'], None), (['C4', 'M3', 'sideways'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'P8', 'up'], 'C5'), (['C5', 'P15', 'down'], 'C3')], [(['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['C4', 'M9', 'up'], 'D5'), (['D4', 'd4', 'down'], 'A#3')], [(['E4', 'A4', 'up'], 'A#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['Db4', 'd3', 'up'], 'Fbb4'), (['C4', 'P8', 'up'], 'C5'), (['C4', 'M10', 'down'], 'Ab2'), (['F4', 'A4', 'down'], 'Cb4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | E4 | E4 | Passed |
| oracle 1 | None | Ab3 | Failed |
| oracle 2 | C4 | C4 | Passed |
| oracle 3 | None | None | Passed |
| oracle 4 | None | D5 | Failed |
| oracle 5 | None | None | Passed |
| oracle 6 | None | None | Passed |
| oracle 7 | None | None | Passed |
SHA-256 / 92007f0f2d274e1561ac2f9df72d91f8e782ee1653d2b73280515cf33bdf9433
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) != 3:
return None
note, ivl, direction = x
if direction not in ('up', 'down'):
return None
if 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 '#b':
acc += 1 if note[i] == '#' else -1
i += 1
if not note[i:].isdigit():
return None
octave = int(note[i:])
if not isinstance(ivl, str) or len(ivl) < 2 or not ivl[1:].isdigit():
return None
quality = ivl[0]
number = int(ivl[1:])
if number < 1:
return None
simple = (number - 1) % 7 + 1
perfect = simple in (1, 4, 5)
offsets = {'P': 0, 'A': 1, 'd': -1} if perfect else {'M': 0, 'm': -1, 'A': 1, 'd': -2}
if quality not in offsets:
return None
size = [0, 2, 4, 5, 7, 9, 11][simple - 1] + 12 * ((number - 1) // 7) + offsets[quality]
sign = 1 if direction == 'up' else -1
idx = 'CDEFGAB'.index(note[0]) + 7 * octave + sign * (number - 1)
pitch = 12 * octave + base[note[0]] + acc + sign * size
letter = 'CDEFGAB'[idx % 7]
new_octave = idx // 7
new_acc = pitch - (12 * new_octave + base[letter])
if abs(new_acc) > 2 or new_octave < 0:
return None
return letter + ('#' * new_acc if new_acc > 0 else 'b' * -new_acc) + str(new_octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['Cb4', 'd3', 'up'], None), (['C4', 'M9', 'up'], 'D5'), (['C0', 'm2', 'down'], None), (['C4', 'X3', 'up'], None), (['C4', 'M4', 'up'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['C4', 'M10', 'down'], 'Ab2'), (['C4', 'M4', 'up'], None), (['C4', 'P3', 'up'], None), (['C4', 'M3', 'sideways'], None)], [(['C4', 'M3', 'up'], 'E4'), (['C4', 'M3', 'down'], 'Ab3'), (['B3', 'm2', 'up'], 'C4'), (['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'P8', 'up'], 'C5'), (['C5', 'P15', 'down'], 'C3')], [(['C4', 'm2', 'down'], 'B3'), (['E4', 'A4', 'up'], 'A#4'), (['F#4', 'A4', 'up'], 'B#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['C4', 'M9', 'up'], 'D5'), (['D4', 'd4', 'down'], 'A#3')], [(['E4', 'A4', 'up'], 'A#4'), (['C4', 'd5', 'up'], 'Gb4'), (['C4', 'd7', 'up'], 'Bbb4'), (['C4', 'd3', 'up'], 'Ebb4'), (['Db4', 'd3', 'up'], 'Fbb4'), (['C4', 'P8', 'up'], 'C5'), (['C4', 'M10', 'down'], 'Ab2'), (['F4', 'A4', 'down'], 'Cb4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | E4 | E4 | Passed |
| oracle 1 | Ab3 | Ab3 | Passed |
| oracle 2 | C4 | C4 | Passed |
| oracle 3 | None | None | Passed |
| oracle 4 | D5 | D5 | Passed |
| oracle 5 | None | None | Passed |
| oracle 6 | None | None | Passed |
| oracle 7 | None | None | Passed |
SHA-256 / 35886f0425abb114b99a9bd971915085f6428d96df3a92e85c807241185c32b1
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.077875+00:00.
Case digest / 9c7f57be4e4f65801a1e1105bbd51bcf4f83092760564bd2510d0dda0d577468