FAILURE MAP
← Case archive

FA-96731 / Phone number formatting / Open access

Number list range compression: the tail is always two digits · case 01

"5550198-5550201" is emitted as "5550198-01".

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

ROOT CAUSE

The tail is the last two digits instead of the part after the shared prefix.

VERIFIED REPAIR

Drop exactly the prefix START and END share.

Unsuccessful approach: Sizing the tail by the digit count of the span still writes "5550198-1".

Case contract

Input a list of digit strings. Deduplicate and sort by (length, text). Consecutive numbers of equal length (each one greater than the previous) form a run. A run of one is emitted as is; a longer run is emitted as START-TAIL where TAIL is END without the prefix it shares with START.

Why this case matters

Number inventories are displayed and exported as compressed ranges; a wrong run boundary or tail misstates which numbers are owned.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    nums = sorted(set(x), key=lambda v: (len(v), v))
    out = []
    i = 0
    while i < len(nums):
        j = i
        while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
            j += 1
        a, b = nums[i], nums[j]
        if a == b:
            out.append(a)
        else:
            k = 0
            while a[k] == b[k]:
                k += 1
            out.append(a + '-' + b[-2:])
        i = j + 1
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
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 fixtureActualExpectedOutcome
run and singleton['5550110-12', '5550160']['5550110-2', '5550160']Failed
duplicates inside a run['5550115-16']['5550115-6']Failed
unsorted input['5550110-12']['5550110-2']Failed
run crossing a hundred['5550198-01']['5550198-201']Failed
different lengths do not join['98-99', '100-01']['98-9', '100-1']Failed
run crossing a thousand['1998-00']['1998-2000']Failed
shorter number sorts first['100', '0050']['100', '0050']Passed
gap of two['5550130', '5550132']['5550130', '5550132']Passed

SHA-256 / 23df212b97f4d50429085e493cd863d4d417de599437e987a598d6bba556d082

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    nums = sorted(set(x), key=lambda v: (len(v), v))
    out = []
    i = 0
    while i < len(nums):
        j = i
        while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
            j += 1
        a, b = nums[i], nums[j]
        if a == b:
            out.append(a)
        else:
            k = 0
            while a[k] == b[k]:
                k += 1
            out.append(a + '-' + b[len(b) - len(str(int(b) - int(a))):])
        i = j + 1
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
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 fixtureActualExpectedOutcome
run and singleton['5550110-2', '5550160']['5550110-2', '5550160']Passed
duplicates inside a run['5550115-6']['5550115-6']Passed
unsorted input['5550110-2']['5550110-2']Passed
run crossing a hundred['5550198-1']['5550198-201']Failed
different lengths do not join['98-9', '100-1']['98-9', '100-1']Passed
run crossing a thousand['1998-0']['1998-2000']Failed
shorter number sorts first['100', '0050']['100', '0050']Passed
gap of two['5550130', '5550132']['5550130', '5550132']Passed

SHA-256 / 09e933c906afadae901d6c1df95c52914c3af65b80aeb768c031ec899876477a

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    nums = sorted(set(x), key=lambda v: (len(v), v))
    out = []
    i = 0
    while i < len(nums):
        j = i
        while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
            j += 1
        a, b = nums[i], nums[j]
        if a == b:
            out.append(a)
        else:
            k = 0
            while a[k] == b[k]:
                k += 1
            out.append(a + '-' + b[k:])
        i = j + 1
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
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 fixtureActualExpectedOutcome
run and singleton['5550110-2', '5550160']['5550110-2', '5550160']Passed
duplicates inside a run['5550115-6']['5550115-6']Passed
unsorted input['5550110-2']['5550110-2']Passed
run crossing a hundred['5550198-201']['5550198-201']Passed
different lengths do not join['98-9', '100-1']['98-9', '100-1']Passed
run crossing a thousand['1998-2000']['1998-2000']Passed
shorter number sorts first['100', '0050']['100', '0050']Passed
gap of two['5550130', '5550132']['5550130', '5550132']Passed

SHA-256 / 7718be816344333c3799284f36a47556ec1529b37d765452bd619981c869c99a

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

Case digest / 46b5f5f12659b67ee965f8a9f51b3ee9683d10955b73db35309babf9f16149d8