FAILURE MAP
← Case archive

FA-96766 / Phone number formatting / Open access

Pattern-driven phone input mask: pending literals are written for partial input · case 01

Four digits in a pairs pattern render "55 50 " with a trailing space.

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

ROOT CAUSE

Buffered literals are flushed even when the pattern stopped early for lack of digits.

VERIFIED REPAIR

Flush trailing literals only when the whole pattern was consumed.

Unsuccessful approach: Never flushing trailing literals drops the closing bracket of a completed pattern.

Case contract

Input [digits, pattern]. Each X consumes the next digit; "\" makes the next pattern character a literal; other characters are literals. Literals are buffered and written only in front of the next digit, so a partial input never ends in a literal; when the whole pattern was consumed the trailing literals are written too. More digits than X slots returns None.

Why this case matters

Input masks drive what users see while typing; stray punctuation, lost literals or silently dropped digits produce numbers that differ from what was typed.

1 / The failure

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

N = 1
observations = []
def solve(x):
    digits, pattern = x
    out, i, k, pend = [], 0, 0, ''
    while k < len(pattern):
        ch = pattern[k]
        if ch == '\\' and k + 1 < len(pattern):
            pend += pattern[k + 1]
            k += 2
            continue
        if ch == 'X':
            if i >= len(digits):
                break
            out.append(pend + digits[i])
            pend = ''
            i += 1
        else:
            pend += ch
        k += 1
    if i < len(digits):
        return None
    out.append(pend)
    return ''.join(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['complete ten digits', ['5550113456', '(XXX) XXX-XXXX'], '(555) 011-3456'], ['three digits keep the bracket pending', ['555', '(XXX) XXX-XXXX'], '(555'], ['six digits', ['555011', '(XXX) XXX-XXXX'], '(555) 011'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['555', '(XXX)'], '(555)'], ['escaped letter X', ['5550', 'E\\XT XXXX'], 'EXT 5550'], ['one digit too many', ['55501134561', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['5550113456', 'XX XX XX XX XX'], '55 50 11 34 56'], ['pairs pattern partial', ['5550', 'XX XX XX XX XX'], '55 50']], [['complete ten digits', ['6550123456', '(XXX) XXX-XXXX'], '(655) 012-3456'], ['three digits keep the bracket pending', ['655', '(XXX) XXX-XXXX'], '(655'], ['six digits', ['655012', '(XXX) XXX-XXXX'], '(655) 012'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['655', '(XXX)'], '(655)'], ['escaped letter X', ['6550', 'E\\XT XXXX'], 'EXT 6550'], ['one digit too many', ['65501234562', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['6550123456', 'XX XX XX XX XX'], '65 50 12 34 56'], ['pairs pattern partial', ['6550', 'XX XX XX XX XX'], '65 50']], [['complete ten digits', ['7550133456', '(XXX) XXX-XXXX'], '(755) 013-3456'], ['three digits keep the bracket pending', ['755', '(XXX) XXX-XXXX'], '(755'], ['six digits', ['755013', '(XXX) XXX-XXXX'], '(755) 013'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['755', '(XXX)'], '(755)'], ['escaped letter X', ['7550', 'E\\XT XXXX'], 'EXT 7550'], ['one digit too many', ['75501334563', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['7550133456', 'XX XX XX XX XX'], '75 50 13 34 56'], ['pairs pattern partial', ['7550', 'XX XX XX XX XX'], '75 50']], [['complete ten digits', ['8550143456', '(XXX) XXX-XXXX'], '(855) 014-3456'], ['three digits keep the bracket pending', ['855', '(XXX) XXX-XXXX'], '(855'], ['six digits', ['855014', '(XXX) XXX-XXXX'], '(855) 014'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['855', '(XXX)'], '(855)'], ['escaped letter X', ['8550', 'E\\XT XXXX'], 'EXT 8550'], ['one digit too many', ['85501434564', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['8550143456', 'XX XX XX XX XX'], '85 50 14 34 56'], ['pairs pattern partial', ['8550', 'XX XX XX XX XX'], '85 50']], [['complete ten digits', ['9550153456', '(XXX) XXX-XXXX'], '(955) 015-3456'], ['three digits keep the bracket pending', ['955', '(XXX) XXX-XXXX'], '(955'], ['six digits', ['955015', '(XXX) XXX-XXXX'], '(955) 015'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['955', '(XXX)'], '(955)'], ['escaped letter X', ['9550', 'E\\XT XXXX'], 'EXT 9550'], ['one digit too many', ['95501534565', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['9550153456', 'XX XX XX XX XX'], '95 50 15 34 56'], ['pairs pattern partial', ['9550', 'XX XX XX XX XX'], '95 50']]]
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
complete ten digits(555) 011-3456(555) 011-3456Passed
three digits keep the bracket pending(555) (555Failed
six digits(555) 011-(555) 011Failed
nothing typed(Failed
pattern ending in a literal(555)(555)Passed
escaped letter XEXT 5550EXT 5550Passed
one digit too manyNoneNonePassed
pairs pattern complete55 50 11 34 5655 50 11 34 56Passed
pairs pattern partial55 50 55 50Failed

SHA-256 / 2b8bf669d4dac7c3c1e5df8d68b79760ccdcc4c4858f1e151da1f90f8d1e5c0f

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    digits, pattern = x
    out, i, k, pend = [], 0, 0, ''
    while k < len(pattern):
        ch = pattern[k]
        if ch == '\\' and k + 1 < len(pattern):
            pend += pattern[k + 1]
            k += 2
            continue
        if ch == 'X':
            if i >= len(digits):
                break
            out.append(pend + digits[i])
            pend = ''
            i += 1
        else:
            pend += ch
        k += 1
    if i < len(digits):
        return None
    return ''.join(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['complete ten digits', ['5550113456', '(XXX) XXX-XXXX'], '(555) 011-3456'], ['three digits keep the bracket pending', ['555', '(XXX) XXX-XXXX'], '(555'], ['six digits', ['555011', '(XXX) XXX-XXXX'], '(555) 011'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['555', '(XXX)'], '(555)'], ['escaped letter X', ['5550', 'E\\XT XXXX'], 'EXT 5550'], ['one digit too many', ['55501134561', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['5550113456', 'XX XX XX XX XX'], '55 50 11 34 56'], ['pairs pattern partial', ['5550', 'XX XX XX XX XX'], '55 50']], [['complete ten digits', ['6550123456', '(XXX) XXX-XXXX'], '(655) 012-3456'], ['three digits keep the bracket pending', ['655', '(XXX) XXX-XXXX'], '(655'], ['six digits', ['655012', '(XXX) XXX-XXXX'], '(655) 012'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['655', '(XXX)'], '(655)'], ['escaped letter X', ['6550', 'E\\XT XXXX'], 'EXT 6550'], ['one digit too many', ['65501234562', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['6550123456', 'XX XX XX XX XX'], '65 50 12 34 56'], ['pairs pattern partial', ['6550', 'XX XX XX XX XX'], '65 50']], [['complete ten digits', ['7550133456', '(XXX) XXX-XXXX'], '(755) 013-3456'], ['three digits keep the bracket pending', ['755', '(XXX) XXX-XXXX'], '(755'], ['six digits', ['755013', '(XXX) XXX-XXXX'], '(755) 013'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['755', '(XXX)'], '(755)'], ['escaped letter X', ['7550', 'E\\XT XXXX'], 'EXT 7550'], ['one digit too many', ['75501334563', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['7550133456', 'XX XX XX XX XX'], '75 50 13 34 56'], ['pairs pattern partial', ['7550', 'XX XX XX XX XX'], '75 50']], [['complete ten digits', ['8550143456', '(XXX) XXX-XXXX'], '(855) 014-3456'], ['three digits keep the bracket pending', ['855', '(XXX) XXX-XXXX'], '(855'], ['six digits', ['855014', '(XXX) XXX-XXXX'], '(855) 014'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['855', '(XXX)'], '(855)'], ['escaped letter X', ['8550', 'E\\XT XXXX'], 'EXT 8550'], ['one digit too many', ['85501434564', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['8550143456', 'XX XX XX XX XX'], '85 50 14 34 56'], ['pairs pattern partial', ['8550', 'XX XX XX XX XX'], '85 50']], [['complete ten digits', ['9550153456', '(XXX) XXX-XXXX'], '(955) 015-3456'], ['three digits keep the bracket pending', ['955', '(XXX) XXX-XXXX'], '(955'], ['six digits', ['955015', '(XXX) XXX-XXXX'], '(955) 015'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['955', '(XXX)'], '(955)'], ['escaped letter X', ['9550', 'E\\XT XXXX'], 'EXT 9550'], ['one digit too many', ['95501534565', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['9550153456', 'XX XX XX XX XX'], '95 50 15 34 56'], ['pairs pattern partial', ['9550', 'XX XX XX XX XX'], '95 50']]]
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
complete ten digits(555) 011-3456(555) 011-3456Passed
three digits keep the bracket pending(555(555Passed
six digits(555) 011(555) 011Passed
nothing typedPassed
pattern ending in a literal(555(555)Failed
escaped letter XEXT 5550EXT 5550Passed
one digit too manyNoneNonePassed
pairs pattern complete55 50 11 34 5655 50 11 34 56Passed
pairs pattern partial55 5055 50Passed

SHA-256 / d58ef13a69fad83b625b24458ecd13163673dd3160f0e69ce1635b55a8bf95a6

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    digits, pattern = x
    out, i, k, pend = [], 0, 0, ''
    while k < len(pattern):
        ch = pattern[k]
        if ch == '\\' and k + 1 < len(pattern):
            pend += pattern[k + 1]
            k += 2
            continue
        if ch == 'X':
            if i >= len(digits):
                break
            out.append(pend + digits[i])
            pend = ''
            i += 1
        else:
            pend += ch
        k += 1
    if i < len(digits):
        return None
    if k >= len(pattern):
        out.append(pend)
    return ''.join(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['complete ten digits', ['5550113456', '(XXX) XXX-XXXX'], '(555) 011-3456'], ['three digits keep the bracket pending', ['555', '(XXX) XXX-XXXX'], '(555'], ['six digits', ['555011', '(XXX) XXX-XXXX'], '(555) 011'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['555', '(XXX)'], '(555)'], ['escaped letter X', ['5550', 'E\\XT XXXX'], 'EXT 5550'], ['one digit too many', ['55501134561', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['5550113456', 'XX XX XX XX XX'], '55 50 11 34 56'], ['pairs pattern partial', ['5550', 'XX XX XX XX XX'], '55 50']], [['complete ten digits', ['6550123456', '(XXX) XXX-XXXX'], '(655) 012-3456'], ['three digits keep the bracket pending', ['655', '(XXX) XXX-XXXX'], '(655'], ['six digits', ['655012', '(XXX) XXX-XXXX'], '(655) 012'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['655', '(XXX)'], '(655)'], ['escaped letter X', ['6550', 'E\\XT XXXX'], 'EXT 6550'], ['one digit too many', ['65501234562', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['6550123456', 'XX XX XX XX XX'], '65 50 12 34 56'], ['pairs pattern partial', ['6550', 'XX XX XX XX XX'], '65 50']], [['complete ten digits', ['7550133456', '(XXX) XXX-XXXX'], '(755) 013-3456'], ['three digits keep the bracket pending', ['755', '(XXX) XXX-XXXX'], '(755'], ['six digits', ['755013', '(XXX) XXX-XXXX'], '(755) 013'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['755', '(XXX)'], '(755)'], ['escaped letter X', ['7550', 'E\\XT XXXX'], 'EXT 7550'], ['one digit too many', ['75501334563', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['7550133456', 'XX XX XX XX XX'], '75 50 13 34 56'], ['pairs pattern partial', ['7550', 'XX XX XX XX XX'], '75 50']], [['complete ten digits', ['8550143456', '(XXX) XXX-XXXX'], '(855) 014-3456'], ['three digits keep the bracket pending', ['855', '(XXX) XXX-XXXX'], '(855'], ['six digits', ['855014', '(XXX) XXX-XXXX'], '(855) 014'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['855', '(XXX)'], '(855)'], ['escaped letter X', ['8550', 'E\\XT XXXX'], 'EXT 8550'], ['one digit too many', ['85501434564', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['8550143456', 'XX XX XX XX XX'], '85 50 14 34 56'], ['pairs pattern partial', ['8550', 'XX XX XX XX XX'], '85 50']], [['complete ten digits', ['9550153456', '(XXX) XXX-XXXX'], '(955) 015-3456'], ['three digits keep the bracket pending', ['955', '(XXX) XXX-XXXX'], '(955'], ['six digits', ['955015', '(XXX) XXX-XXXX'], '(955) 015'], ['nothing typed', ['', '(XXX) XXX-XXXX'], ''], ['pattern ending in a literal', ['955', '(XXX)'], '(955)'], ['escaped letter X', ['9550', 'E\\XT XXXX'], 'EXT 9550'], ['one digit too many', ['95501534565', '(XXX) XXX-XXXX'], None], ['pairs pattern complete', ['9550153456', 'XX XX XX XX XX'], '95 50 15 34 56'], ['pairs pattern partial', ['9550', 'XX XX XX XX XX'], '95 50']]]
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
complete ten digits(555) 011-3456(555) 011-3456Passed
three digits keep the bracket pending(555(555Passed
six digits(555) 011(555) 011Passed
nothing typedPassed
pattern ending in a literal(555)(555)Passed
escaped letter XEXT 5550EXT 5550Passed
one digit too manyNoneNonePassed
pairs pattern complete55 50 11 34 5655 50 11 34 56Passed
pairs pattern partial55 5055 50Passed

SHA-256 / f8306aef227a4142d80f6d1a1338405bd8b2b733653b7a052c0afca57c97078d

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

Case digest / 45dd706eceec549f930b8250653b4658456124e71d46a20e2104f92597ae4819