FA-96586 / Phone number formatting / Open access
tel URI builder: local numbers are emitted without a context · case 01
"5550115" becomes "tel:5550115" with no phone-context.
ROOT CAUSE
The missing-context rejection is absent.
THE FAILURE
The missing-context rejection is absent.
Unsuccessful approach: Requiring a context only for multi-group local numbers still emits unqualified single-group numbers.
Case contract
Input {"number", optional "ext", optional "context"}. A number starting with "+" is global. Digits are grouped at runs of visual separators (- . space parentheses); any other character returns None. Build "tel:" + ("+" if global) + groups joined by "-", then ";ext=" + digits when an extension is given, then for local numbers ";phone-context=" + context (required).
Why this case matters
tel URIs are exchanged between softphones, CRMs and SIP gateways; a missing context or misordered parameter makes local numbers undialable.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
num, ext, ctx = x['number'].strip(), x.get('ext', ''), x.get('context', '')
glob = num.startswith('+')
body = num[1:] if glob else num
parts, cur = [], ''
for c in body:
if c.isdigit():
cur += c
elif c in '-. ()':
if cur:
parts.append(cur)
cur = ''
else:
return None
if cur:
parts.append(cur)
if not parts:
return None
uri = 'tel:' + ('+' if glob else '') + '-'.join(parts)
if ext:
if not ext.isdigit():
return None
uri += ';ext=' + ext
if not glob:
uri += ';phone-context=' + ctx
return uri
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['global number', {'number': '+42 555-010-1211'}, 'tel:+42-555-010-1211'], ['local number with context', {'number': '555-0114', 'context': '+42'}, 'tel:555-0114;phone-context=+42'], ['local number with extension', {'number': '555-0114', 'ext': '12', 'context': '+42'}, 'tel:555-0114;ext=12;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0114', 'ext': '1'}, 'tel:+42-555-0114;ext=1'], ['local without context', {'number': '5550114'}, None], ['run of separators', {'number': '+42 555 - 0114'}, 'tel:+42-555-0114'], ['slash is not a visual separator', {'number': '+42 555/0114'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0114', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1222'}, 'tel:+42-555-010-1222'], ['local number with context', {'number': '555-0125', 'context': '+42'}, 'tel:555-0125;phone-context=+42'], ['local number with extension', {'number': '555-0125', 'ext': '22', 'context': '+42'}, 'tel:555-0125;ext=22;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0125', 'ext': '2'}, 'tel:+42-555-0125;ext=2'], ['local without context', {'number': '5550125'}, None], ['run of separators', {'number': '+42 555 - 0125'}, 'tel:+42-555-0125'], ['slash is not a visual separator', {'number': '+42 555/0125'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0125', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1233'}, 'tel:+42-555-010-1233'], ['local number with context', {'number': '555-0136', 'context': '+42'}, 'tel:555-0136;phone-context=+42'], ['local number with extension', {'number': '555-0136', 'ext': '32', 'context': '+42'}, 'tel:555-0136;ext=32;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0136', 'ext': '3'}, 'tel:+42-555-0136;ext=3'], ['local without context', {'number': '5550136'}, None], ['run of separators', {'number': '+42 555 - 0136'}, 'tel:+42-555-0136'], ['slash is not a visual separator', {'number': '+42 555/0136'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0136', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1244'}, 'tel:+42-555-010-1244'], ['local number with context', {'number': '555-0147', 'context': '+42'}, 'tel:555-0147;phone-context=+42'], ['local number with extension', {'number': '555-0147', 'ext': '42', 'context': '+42'}, 'tel:555-0147;ext=42;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0147', 'ext': '4'}, 'tel:+42-555-0147;ext=4'], ['local without context', {'number': '5550147'}, None], ['run of separators', {'number': '+42 555 - 0147'}, 'tel:+42-555-0147'], ['slash is not a visual separator', {'number': '+42 555/0147'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0147', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1255'}, 'tel:+42-555-010-1255'], ['local number with context', {'number': '555-0158', 'context': '+42'}, 'tel:555-0158;phone-context=+42'], ['local number with extension', {'number': '555-0158', 'ext': '52', 'context': '+42'}, 'tel:555-0158;ext=52;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0158', 'ext': '5'}, 'tel:+42-555-0158;ext=5'], ['local without context', {'number': '5550158'}, None], ['run of separators', {'number': '+42 555 - 0158'}, 'tel:+42-555-0158'], ['slash is not a visual separator', {'number': '+42 555/0158'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0158', 'ext': '1a'}, 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 |
|---|---|---|---|
| global number | tel:+42-555-010-1211 | tel:+42-555-010-1211 | Passed |
| local number with context | tel:555-0114;phone-context=+42 | tel:555-0114;phone-context=+42 | Passed |
| local number with extension | tel:555-0114;ext=12;phone-context=+42 | tel:555-0114;ext=12;phone-context=+42 | Passed |
| global with extension | tel:+42-555-0114;ext=1 | tel:+42-555-0114;ext=1 | Passed |
| local without context | tel:5550114;phone-context= | None | Failed |
| run of separators | tel:+42-555-0114 | tel:+42-555-0114 | Passed |
| slash is not a visual separator | None | None | Passed |
| letters rejected | None | None | Passed |
| non-numeric extension | None | None | Passed |
SHA-256 / 00b6f8cf86211284e6e5f4df7f4a3aca91ef495eee87e1942a768f63f3ab6ce4
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
num, ext, ctx = x['number'].strip(), x.get('ext', ''), x.get('context', '')
glob = num.startswith('+')
body = num[1:] if glob else num
parts, cur = [], ''
for c in body:
if c.isdigit():
cur += c
elif c in '-. ()':
if cur:
parts.append(cur)
cur = ''
else:
return None
if cur:
parts.append(cur)
if not parts:
return None
uri = 'tel:' + ('+' if glob else '') + '-'.join(parts)
if ext:
if not ext.isdigit():
return None
uri += ';ext=' + ext
if not glob:
if not ctx and len(parts) > 1:
return None
uri += ';phone-context=' + ctx
return uri
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['global number', {'number': '+42 555-010-1211'}, 'tel:+42-555-010-1211'], ['local number with context', {'number': '555-0114', 'context': '+42'}, 'tel:555-0114;phone-context=+42'], ['local number with extension', {'number': '555-0114', 'ext': '12', 'context': '+42'}, 'tel:555-0114;ext=12;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0114', 'ext': '1'}, 'tel:+42-555-0114;ext=1'], ['local without context', {'number': '5550114'}, None], ['run of separators', {'number': '+42 555 - 0114'}, 'tel:+42-555-0114'], ['slash is not a visual separator', {'number': '+42 555/0114'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0114', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1222'}, 'tel:+42-555-010-1222'], ['local number with context', {'number': '555-0125', 'context': '+42'}, 'tel:555-0125;phone-context=+42'], ['local number with extension', {'number': '555-0125', 'ext': '22', 'context': '+42'}, 'tel:555-0125;ext=22;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0125', 'ext': '2'}, 'tel:+42-555-0125;ext=2'], ['local without context', {'number': '5550125'}, None], ['run of separators', {'number': '+42 555 - 0125'}, 'tel:+42-555-0125'], ['slash is not a visual separator', {'number': '+42 555/0125'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0125', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1233'}, 'tel:+42-555-010-1233'], ['local number with context', {'number': '555-0136', 'context': '+42'}, 'tel:555-0136;phone-context=+42'], ['local number with extension', {'number': '555-0136', 'ext': '32', 'context': '+42'}, 'tel:555-0136;ext=32;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0136', 'ext': '3'}, 'tel:+42-555-0136;ext=3'], ['local without context', {'number': '5550136'}, None], ['run of separators', {'number': '+42 555 - 0136'}, 'tel:+42-555-0136'], ['slash is not a visual separator', {'number': '+42 555/0136'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0136', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1244'}, 'tel:+42-555-010-1244'], ['local number with context', {'number': '555-0147', 'context': '+42'}, 'tel:555-0147;phone-context=+42'], ['local number with extension', {'number': '555-0147', 'ext': '42', 'context': '+42'}, 'tel:555-0147;ext=42;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0147', 'ext': '4'}, 'tel:+42-555-0147;ext=4'], ['local without context', {'number': '5550147'}, None], ['run of separators', {'number': '+42 555 - 0147'}, 'tel:+42-555-0147'], ['slash is not a visual separator', {'number': '+42 555/0147'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0147', 'ext': '1a'}, None]], [['global number', {'number': '+42 555-010-1255'}, 'tel:+42-555-010-1255'], ['local number with context', {'number': '555-0158', 'context': '+42'}, 'tel:555-0158;phone-context=+42'], ['local number with extension', {'number': '555-0158', 'ext': '52', 'context': '+42'}, 'tel:555-0158;ext=52;phone-context=+42'], ['global with extension', {'number': '+42 (555) 0158', 'ext': '5'}, 'tel:+42-555-0158;ext=5'], ['local without context', {'number': '5550158'}, None], ['run of separators', {'number': '+42 555 - 0158'}, 'tel:+42-555-0158'], ['slash is not a visual separator', {'number': '+42 555/0158'}, None], ['letters rejected', {'number': '+42 555 FAKE'}, None], ['non-numeric extension', {'number': '+42 555 0158', 'ext': '1a'}, 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 |
|---|---|---|---|
| global number | tel:+42-555-010-1211 | tel:+42-555-010-1211 | Passed |
| local number with context | tel:555-0114;phone-context=+42 | tel:555-0114;phone-context=+42 | Passed |
| local number with extension | tel:555-0114;ext=12;phone-context=+42 | tel:555-0114;ext=12;phone-context=+42 | Passed |
| global with extension | tel:+42-555-0114;ext=1 | tel:+42-555-0114;ext=1 | Passed |
| local without context | tel:5550114;phone-context= | None | Failed |
| run of separators | tel:+42-555-0114 | tel:+42-555-0114 | Passed |
| slash is not a visual separator | None | None | Passed |
| letters rejected | None | None | Passed |
| non-numeric extension | None | None | Passed |
SHA-256 / 273bc3c26d61865ffaba2a13cf9a1df58c5564986eabbe1cdbba91557404727d
HELD IN THE MEMBER ARCHIVE
The verified repair and its recorded checks are member-only.
This mechanism has 9 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.
Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.
Member access is invitation-based. Sign in with your invited account to inspect the repair.
Sign in to the archive ↗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:24.338374+00:00.
Case digest / 8c5fa95730ba6898787ba17ce81d29bd512813e5f45818cf1dbdf8c98a06db89