FA-96436 / Phone number formatting / Open access
Vanity keypad translator: dots and parentheses are rejected · case 01
"555.QUIZZAP" returns None.
ROOT CAUSE
Only spaces and hyphens are ignorable.
VERIFIED REPAIR
Ignore spaces, hyphens, dots and parentheses and reject everything else.
Unsuccessful approach: Ignoring every other character lets slashes through.
Case contract
Uppercase. Digits are kept, letters map to keypad digits (ABC2 DEF3 GHI4 JKL5 MNO6 PQRS7 TUV8 WXYZ9), spaces, hyphens, dots and parentheses are ignored and anything else returns None. At least ten symbols are needed; symbols after the tenth may only be letters (vanity overflow) and are dropped. Return the first ten digits as "DDD-DDD-DDDD".
Why this case matters
Vanity numbers printed in advertising are translated before dialing or storing; a wrong keypad table dials a stranger.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
KEYS = {'ABC': '2', 'DEF': '3', 'GHI': '4', 'JKL': '5', 'MNO': '6', 'PQRS': '7', 'TUV': '8', 'WXYZ': '9'}
out = []
for c in x.upper():
if c.isdigit():
out.append(c)
elif c.isalpha():
for letters, digit in KEYS.items():
if c in letters:
out.append(digit + '*')
break
elif c not in ' -':
return None
if len(out) < 10:
return None
if any(not t.endswith('*') for t in out[10:]):
return None
d = ''.join(t[0] for t in out[:10])
return d[:3] + '-' + d[3:6] + '-' + d[6:]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['letters with Q and Z', '555-QUIZZAP', '555-784-9927'], ['lowercase letters', '(555) quizzap', '555-784-9927'], ['dotted vanity number', '555.QUIZZAP', '555-784-9927'], ['extra vanity letter is ignored', '555-QUIZZAPS', '555-784-9927'], ['extra digit is rejected', '555QUIZZAP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZAP', None]], [['letters with Q and Z', '555-QUIZZIP', '555-784-9947'], ['lowercase letters', '(555) quizzip', '555-784-9947'], ['dotted vanity number', '555.QUIZZIP', '555-784-9947'], ['extra vanity letter is ignored', '555-QUIZZIPS', '555-784-9947'], ['extra digit is rejected', '555QUIZZIP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZIP', None]], [['letters with Q and Z', '555-QUIZZOO', '555-784-9966'], ['lowercase letters', '(555) quizzoo', '555-784-9966'], ['dotted vanity number', '555.QUIZZOO', '555-784-9966'], ['extra vanity letter is ignored', '555-QUIZZOOS', '555-784-9966'], ['extra digit is rejected', '555QUIZZOO9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZOO', None]], [['letters with Q and Z', '555-QUIZZEN', '555-784-9936'], ['lowercase letters', '(555) quizzen', '555-784-9936'], ['dotted vanity number', '555.QUIZZEN', '555-784-9936'], ['extra vanity letter is ignored', '555-QUIZZENS', '555-784-9936'], ['extra digit is rejected', '555QUIZZEN9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZEN', None]], [['letters with Q and Z', '555-QUIZZED', '555-784-9933'], ['lowercase letters', '(555) quizzed', '555-784-9933'], ['dotted vanity number', '555.QUIZZED', '555-784-9933'], ['extra vanity letter is ignored', '555-QUIZZEDS', '555-784-9933'], ['extra digit is rejected', '555QUIZZED9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZED', None]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| letters with Q and Z | 555-784-9927 | 555-784-9927 | Passed |
| lowercase letters | None | 555-784-9927 | Failed |
| dotted vanity number | None | 555-784-9927 | Failed |
| extra vanity letter is ignored | 555-784-9927 | 555-784-9927 | Passed |
| extra digit is rejected | None | None | Passed |
| too short | None | None | Passed |
| slash rejected | None | None | Passed |
SHA-256 / 2aaf088a56b412faf7f4ffc8c0b9efefe7190b8c1714992e2f75f36d59fca30f
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
KEYS = {'ABC': '2', 'DEF': '3', 'GHI': '4', 'JKL': '5', 'MNO': '6', 'PQRS': '7', 'TUV': '8', 'WXYZ': '9'}
out = []
for c in x.upper():
if c.isdigit():
out.append(c)
elif c.isalpha():
for letters, digit in KEYS.items():
if c in letters:
out.append(digit + '*')
break
elif c.isalnum():
return None
if len(out) < 10:
return None
if any(not t.endswith('*') for t in out[10:]):
return None
d = ''.join(t[0] for t in out[:10])
return d[:3] + '-' + d[3:6] + '-' + d[6:]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['letters with Q and Z', '555-QUIZZAP', '555-784-9927'], ['lowercase letters', '(555) quizzap', '555-784-9927'], ['dotted vanity number', '555.QUIZZAP', '555-784-9927'], ['extra vanity letter is ignored', '555-QUIZZAPS', '555-784-9927'], ['extra digit is rejected', '555QUIZZAP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZAP', None]], [['letters with Q and Z', '555-QUIZZIP', '555-784-9947'], ['lowercase letters', '(555) quizzip', '555-784-9947'], ['dotted vanity number', '555.QUIZZIP', '555-784-9947'], ['extra vanity letter is ignored', '555-QUIZZIPS', '555-784-9947'], ['extra digit is rejected', '555QUIZZIP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZIP', None]], [['letters with Q and Z', '555-QUIZZOO', '555-784-9966'], ['lowercase letters', '(555) quizzoo', '555-784-9966'], ['dotted vanity number', '555.QUIZZOO', '555-784-9966'], ['extra vanity letter is ignored', '555-QUIZZOOS', '555-784-9966'], ['extra digit is rejected', '555QUIZZOO9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZOO', None]], [['letters with Q and Z', '555-QUIZZEN', '555-784-9936'], ['lowercase letters', '(555) quizzen', '555-784-9936'], ['dotted vanity number', '555.QUIZZEN', '555-784-9936'], ['extra vanity letter is ignored', '555-QUIZZENS', '555-784-9936'], ['extra digit is rejected', '555QUIZZEN9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZEN', None]], [['letters with Q and Z', '555-QUIZZED', '555-784-9933'], ['lowercase letters', '(555) quizzed', '555-784-9933'], ['dotted vanity number', '555.QUIZZED', '555-784-9933'], ['extra vanity letter is ignored', '555-QUIZZEDS', '555-784-9933'], ['extra digit is rejected', '555QUIZZED9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZED', None]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| letters with Q and Z | 555-784-9927 | 555-784-9927 | Passed |
| lowercase letters | 555-784-9927 | 555-784-9927 | Passed |
| dotted vanity number | 555-784-9927 | 555-784-9927 | Passed |
| extra vanity letter is ignored | 555-784-9927 | 555-784-9927 | Passed |
| extra digit is rejected | None | None | Passed |
| too short | None | None | Passed |
| slash rejected | 555-784-9927 | None | Failed |
SHA-256 / fcc03ee910dbb779eca4dcf49bf344da69c2d99bd8d70fe155eded6f61ce324e
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
KEYS = {'ABC': '2', 'DEF': '3', 'GHI': '4', 'JKL': '5', 'MNO': '6', 'PQRS': '7', 'TUV': '8', 'WXYZ': '9'}
out = []
for c in x.upper():
if c.isdigit():
out.append(c)
elif c.isalpha():
for letters, digit in KEYS.items():
if c in letters:
out.append(digit + '*')
break
elif c not in ' -.()':
return None
if len(out) < 10:
return None
if any(not t.endswith('*') for t in out[10:]):
return None
d = ''.join(t[0] for t in out[:10])
return d[:3] + '-' + d[3:6] + '-' + d[6:]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['letters with Q and Z', '555-QUIZZAP', '555-784-9927'], ['lowercase letters', '(555) quizzap', '555-784-9927'], ['dotted vanity number', '555.QUIZZAP', '555-784-9927'], ['extra vanity letter is ignored', '555-QUIZZAPS', '555-784-9927'], ['extra digit is rejected', '555QUIZZAP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZAP', None]], [['letters with Q and Z', '555-QUIZZIP', '555-784-9947'], ['lowercase letters', '(555) quizzip', '555-784-9947'], ['dotted vanity number', '555.QUIZZIP', '555-784-9947'], ['extra vanity letter is ignored', '555-QUIZZIPS', '555-784-9947'], ['extra digit is rejected', '555QUIZZIP9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZIP', None]], [['letters with Q and Z', '555-QUIZZOO', '555-784-9966'], ['lowercase letters', '(555) quizzoo', '555-784-9966'], ['dotted vanity number', '555.QUIZZOO', '555-784-9966'], ['extra vanity letter is ignored', '555-QUIZZOOS', '555-784-9966'], ['extra digit is rejected', '555QUIZZOO9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZOO', None]], [['letters with Q and Z', '555-QUIZZEN', '555-784-9936'], ['lowercase letters', '(555) quizzen', '555-784-9936'], ['dotted vanity number', '555.QUIZZEN', '555-784-9936'], ['extra vanity letter is ignored', '555-QUIZZENS', '555-784-9936'], ['extra digit is rejected', '555QUIZZEN9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZEN', None]], [['letters with Q and Z', '555-QUIZZED', '555-784-9933'], ['lowercase letters', '(555) quizzed', '555-784-9933'], ['dotted vanity number', '555.QUIZZED', '555-784-9933'], ['extra vanity letter is ignored', '555-QUIZZEDS', '555-784-9933'], ['extra digit is rejected', '555QUIZZED9', None], ['too short', '555-FAKE', None], ['slash rejected', '555/QUIZZED', None]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| letters with Q and Z | 555-784-9927 | 555-784-9927 | Passed |
| lowercase letters | 555-784-9927 | 555-784-9927 | Passed |
| dotted vanity number | 555-784-9927 | 555-784-9927 | Passed |
| extra vanity letter is ignored | 555-784-9927 | 555-784-9927 | Passed |
| extra digit is rejected | None | None | Passed |
| too short | None | None | Passed |
| slash rejected | None | None | Passed |
SHA-256 / daae50b438ddd3cced1c118c7ed824812a7812e6d11ff2c1580281c9ff60722b
Verification & scope
A deterministic toy contract over synthetic, obviously fictitious telephone numbers and toy country tables. The rules are stipulated for this model only and make no claim of conformance with any numbering plan or telecommunications standard. 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:52:22.693490+00:00.
Case digest / 830e16dc27d7ee64c977b938b3c767b1c6a7b95aa81ff733b3aea921241592ca