FAILURE MAP
← Case archive

FA-96781 / Phone number formatting / Open access

Numbering prefix pattern classifier: a number shorter than the pattern matches · case 01

"7" matches "7X".

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

ROOT CAUSE

Running out of number digits is treated as a successful match.

VERIFIED REPAIR

Fail the match when the pattern still has positions but the number has no digits left.

Unsuccessful approach: Succeeding whenever at least one digit matched still accepts the short number.

Case contract

Input [number, rules] with rules [pattern, label] in priority order. Pattern characters: X any digit, [..] a class of single digits and inclusive ranges a-b, other characters literal. A pattern matches when each of its positions matches the corresponding number digit (the number may be longer). Score: literal digit 2, class 1, X 0; the highest score wins and ties keep the earlier rule. No match gives "unknown".

Why this case matters

Numbering-plan tables decide routing, rating and number type from prefixes; specificity and range errors misroute whole blocks of numbers.

1 / The failure

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

N = 1
observations = []
def solve(x):
    num, rules = x
    def match(p, s):
        i = k = 0
        while k < len(p):
            if i >= len(s):
                return True
            if p[k] == 'X':
                ok = s[i].isdigit()
                k += 1
            elif p[k] == '[':
                end = p.index(']', k)
                body, ok, j = p[k + 1:end], False, 0
                while j < len(body):
                    if j + 2 < len(body) and body[j + 1] == '-':
                        ok = ok or body[j] <= s[i] <= body[j + 2]
                        j += 3
                    else:
                        ok = ok or s[i] == body[j]
                        j += 1
                k = end + 1
            else:
                ok = s[i] == p[k]
                k += 1
            if not ok:
                return False
            i += 1
        return True
    def score(p):
        total, depth = 0, 0
        for c in p:
            if c == '[':
                depth += 1
            elif c == ']':
                depth -= 1
                total += 1
            elif c.isdigit() and depth == 0:
                total += 2
        return total
    best = None
    for p, label in rules:
        if match(p, num):
            sc = score(p)
            if best is None or sc > best[0]:
                best = (sc, label)
    return best[1] if best else 'unknown'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['class beats wildcard', ['72555012', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555012', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555012', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555012', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555012', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555012', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555023', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555023', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555023', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555023', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555023', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555023', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555034', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555034', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555034', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555034', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555034', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555034', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555045', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555045', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555045', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555045', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555045', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555045', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555056', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555056', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555056', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555056', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555056', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555056', [['7X', 'generic']]], 'unknown']]]
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
class beats wildcardmobilemobilePassed
literal beats classspecialspecialPassed
range upper end inclusivemobilemobilePassed
range lower end inclusivemobilemobilePassed
number shorter than the patterngenericunknownFailed
tie keeps the first listed rulelisted-firstlisted-firstPassed
range then literal in one classclass-rangeclass-rangePassed
literal then range in one classclass-literalclass-literalPassed
no rule matchesunknownunknownPassed

SHA-256 / 8f3de3825fa4de55ac54f2af9c6a667417da2d04265c2309bea603b16573178a

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    num, rules = x
    def match(p, s):
        i = k = 0
        while k < len(p):
            if i >= len(s):
                return i > 0
            if p[k] == 'X':
                ok = s[i].isdigit()
                k += 1
            elif p[k] == '[':
                end = p.index(']', k)
                body, ok, j = p[k + 1:end], False, 0
                while j < len(body):
                    if j + 2 < len(body) and body[j + 1] == '-':
                        ok = ok or body[j] <= s[i] <= body[j + 2]
                        j += 3
                    else:
                        ok = ok or s[i] == body[j]
                        j += 1
                k = end + 1
            else:
                ok = s[i] == p[k]
                k += 1
            if not ok:
                return False
            i += 1
        return True
    def score(p):
        total, depth = 0, 0
        for c in p:
            if c == '[':
                depth += 1
            elif c == ']':
                depth -= 1
                total += 1
            elif c.isdigit() and depth == 0:
                total += 2
        return total
    best = None
    for p, label in rules:
        if match(p, num):
            sc = score(p)
            if best is None or sc > best[0]:
                best = (sc, label)
    return best[1] if best else 'unknown'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['class beats wildcard', ['72555012', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555012', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555012', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555012', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555012', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555012', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555023', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555023', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555023', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555023', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555023', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555023', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555034', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555034', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555034', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555034', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555034', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555034', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555045', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555045', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555045', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555045', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555045', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555045', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555056', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555056', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555056', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555056', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555056', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555056', [['7X', 'generic']]], 'unknown']]]
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
class beats wildcardmobilemobilePassed
literal beats classspecialspecialPassed
range upper end inclusivemobilemobilePassed
range lower end inclusivemobilemobilePassed
number shorter than the patterngenericunknownFailed
tie keeps the first listed rulelisted-firstlisted-firstPassed
range then literal in one classclass-rangeclass-rangePassed
literal then range in one classclass-literalclass-literalPassed
no rule matchesunknownunknownPassed

SHA-256 / 8bfe81e23404d3976ca9d2558f2011182595a95275d5176fb8fb1f46568d6e13

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    num, rules = x
    def match(p, s):
        i = k = 0
        while k < len(p):
            if i >= len(s):
                return False
            if p[k] == 'X':
                ok = s[i].isdigit()
                k += 1
            elif p[k] == '[':
                end = p.index(']', k)
                body, ok, j = p[k + 1:end], False, 0
                while j < len(body):
                    if j + 2 < len(body) and body[j + 1] == '-':
                        ok = ok or body[j] <= s[i] <= body[j + 2]
                        j += 3
                    else:
                        ok = ok or s[i] == body[j]
                        j += 1
                k = end + 1
            else:
                ok = s[i] == p[k]
                k += 1
            if not ok:
                return False
            i += 1
        return True
    def score(p):
        total, depth = 0, 0
        for c in p:
            if c == '[':
                depth += 1
            elif c == ']':
                depth -= 1
                total += 1
            elif c.isdigit() and depth == 0:
                total += 2
        return total
    best = None
    for p, label in rules:
        if match(p, num):
            sc = score(p)
            if best is None or sc > best[0]:
                best = (sc, label)
    return best[1] if best else 'unknown'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['class beats wildcard', ['72555012', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555012', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555012', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555012', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555012', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555012', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555012', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555023', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555023', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555023', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555023', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555023', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555023', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555023', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555034', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555034', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555034', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555034', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555034', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555034', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555034', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555045', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555045', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555045', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555045', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555045', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555045', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555045', [['7X', 'generic']]], 'unknown']], [['class beats wildcard', ['72555056', [['7X', 'generic'], ['7[0-4]', 'mobile']]], 'mobile'], ['literal beats class', ['72555056', [['7[0-4]', 'mobile'], ['72', 'special']]], 'special'], ['range upper end inclusive', ['74555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['range lower end inclusive', ['70555056', [['7[0-4]', 'mobile'], ['7X', 'generic']]], 'mobile'], ['number shorter than the pattern', ['7', [['7X', 'generic']]], 'unknown'], ['tie keeps the first listed rule', ['88555056', [['X8', 'listed-first'], ['8X', 'listed-second']]], 'listed-first'], ['range then literal in one class', ['2555056', [['[0-35]X', 'class-range']]], 'class-range'], ['literal then range in one class', ['5555056', [['[50-3]X', 'class-literal']]], 'class-literal'], ['no rule matches', ['6555056', [['7X', 'generic']]], 'unknown']]]
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
class beats wildcardmobilemobilePassed
literal beats classspecialspecialPassed
range upper end inclusivemobilemobilePassed
range lower end inclusivemobilemobilePassed
number shorter than the patternunknownunknownPassed
tie keeps the first listed rulelisted-firstlisted-firstPassed
range then literal in one classclass-rangeclass-rangePassed
literal then range in one classclass-literalclass-literalPassed
no rule matchesunknownunknownPassed

SHA-256 / 1f76afe85952c4a5a0ee9ba5a5aff4620aa9559d96b258b26daa31e775cc3366

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

Case digest / 34f255af59c01c72ffd223eb2376ed03740be82860c9f54e5f8c3f87929a723a