FA-81316 / Music interval and transposition theory / Open access
Movable-do chromatic solfege syllables: unknown spelling defaults to flats · case 01
An unknown spelling such as "weird" yields flat syllables instead of None.
ROOT CAUSE
Validation only rejects a missing spelling, so any other value falls through to the flat table.
VERIFIED REPAIR
Restore the spelling validation step so that it reads `spelling not in ('sharp', 'flat')`.
Unsuccessful approach: Checking only for a string type still accepts arbitrary spelling names.
Case contract
Input [tonic_pc, pitch, spelling] with integer pitch values and spelling sharp or flat. The relative pitch class (pitch - tonic) mod 12 maps diatonic degrees to do re mi fa sol la ti; chromatic degrees use di ri fi si li for sharp spelling and ra me se le te for flat spelling. Invalid input returns None.
Why this case matters
Ear-training and sight-singing apps label notes with movable-do syllables relative to the key.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 3:
return None
tonic, pitch, spelling = x
if not isinstance(tonic, int) or not isinstance(pitch, int) or spelling is None:
return None
rel = (pitch - tonic) % 12
diatonic = {0: 'do', 2: 're', 4: 'mi', 5: 'fa', 7: 'sol', 9: 'la', 11: 'ti'}
if rel in diatonic:
return diatonic[rel]
if spelling == 'sharp':
return {1: 'di', 3: 'ri', 6: 'fi', 8: 'si', 10: 'li'}[rel]
return {1: 'ra', 3: 'me', 6: 'se', 8: 'le', 10: 'te'}[rel]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 60, 'sharp'], 'do'), ([0, 62, 'flat'], 're'), ([0, 67, 'sharp'], 'sol'), ([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 65, 'weird'], None)], [([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([0, 65, 'weird'], None)], [([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([0, 65, 'weird'], None)], [([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None)], [([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None), ([0, 63, None], None), ([9, 57, 'flat'], 'do'), ([4, 69, 'sharp'], 'fa')]]
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 | do | do | Passed |
| oracle 1 | re | re | Passed |
| oracle 2 | sol | sol | Passed |
| oracle 3 | ti | ti | Passed |
| oracle 4 | di | di | Passed |
| oracle 5 | ra | ra | Passed |
| oracle 6 | fi | fi | Passed |
| oracle 7 | fa | None | Failed |
SHA-256 / 835217801696dc98288d7a62b471e44ccec786165b3aad5fe1c0380c112bbbcc
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 3:
return None
tonic, pitch, spelling = x
if not isinstance(tonic, int) or not isinstance(pitch, int) or not isinstance(spelling, str):
return None
rel = (pitch - tonic) % 12
diatonic = {0: 'do', 2: 're', 4: 'mi', 5: 'fa', 7: 'sol', 9: 'la', 11: 'ti'}
if rel in diatonic:
return diatonic[rel]
if spelling == 'sharp':
return {1: 'di', 3: 'ri', 6: 'fi', 8: 'si', 10: 'li'}[rel]
return {1: 'ra', 3: 'me', 6: 'se', 8: 'le', 10: 'te'}[rel]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 60, 'sharp'], 'do'), ([0, 62, 'flat'], 're'), ([0, 67, 'sharp'], 'sol'), ([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 65, 'weird'], None)], [([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([0, 65, 'weird'], None)], [([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([0, 65, 'weird'], None)], [([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None)], [([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None), ([0, 63, None], None), ([9, 57, 'flat'], 'do'), ([4, 69, 'sharp'], 'fa')]]
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 | do | do | Passed |
| oracle 1 | re | re | Passed |
| oracle 2 | sol | sol | Passed |
| oracle 3 | ti | ti | Passed |
| oracle 4 | di | di | Passed |
| oracle 5 | ra | ra | Passed |
| oracle 6 | fi | fi | Passed |
| oracle 7 | fa | None | Failed |
SHA-256 / 8a83e14e4a30f268b487b510c3cd68d6db419601dc4449b369b62fd06aabf6b4
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 3:
return None
tonic, pitch, spelling = x
if not isinstance(tonic, int) or not isinstance(pitch, int) or spelling not in ('sharp', 'flat'):
return None
rel = (pitch - tonic) % 12
diatonic = {0: 'do', 2: 're', 4: 'mi', 5: 'fa', 7: 'sol', 9: 'la', 11: 'ti'}
if rel in diatonic:
return diatonic[rel]
if spelling == 'sharp':
return {1: 'di', 3: 'ri', 6: 'fi', 8: 'si', 10: 'li'}[rel]
return {1: 'ra', 3: 'me', 6: 'se', 8: 'le', 10: 'te'}[rel]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 60, 'sharp'], 'do'), ([0, 62, 'flat'], 're'), ([0, 67, 'sharp'], 'sol'), ([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 65, 'weird'], None)], [([0, 71, 'flat'], 'ti'), ([0, 61, 'sharp'], 'di'), ([0, 61, 'flat'], 'ra'), ([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([0, 65, 'weird'], None)], [([0, 66, 'sharp'], 'fi'), ([0, 66, 'flat'], 'se'), ([0, 70, 'flat'], 'te'), ([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([0, 65, 'weird'], None)], [([0, 70, 'sharp'], 'li'), ([7, 66, 'sharp'], 'ti'), ([7, 64, 'flat'], 'la'), ([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None)], [([7, 60, 'sharp'], 'fa'), ([2, 64, 'sharp'], 're'), ([2, 63, 'flat'], 'ra'), ([5, 58, 'flat'], 'fa'), ([0, 65, 'weird'], None), ([0, 63, None], None), ([9, 57, 'flat'], 'do'), ([4, 69, 'sharp'], 'fa')]]
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 | do | do | Passed |
| oracle 1 | re | re | Passed |
| oracle 2 | sol | sol | Passed |
| oracle 3 | ti | ti | Passed |
| oracle 4 | di | di | Passed |
| oracle 5 | ra | ra | Passed |
| oracle 6 | fi | fi | Passed |
| oracle 7 | None | None | Passed |
SHA-256 / bf5a8a690205630dfa9eaf200aea6e26b3c6503abe2aca4caeb111a755d8d5d5
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:01.847114+00:00.
Case digest / 9874d3e1de35303f572922d2c41e9493e9e8d22a2540d91203f29ec6ad18217d