FAILURE MAP
← Case archive

FA-80186 / Typography line breaking / Open access

Ties and discretionary hyphens: unbroken discretionary rendering · case 01

Hyphens show inside words that were not broken.

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

ROOT CAUSE

Discretionary opportunities render a hyphen even when the line continues.

THE FAILURE

Discretionary opportunities render a hyphen even when the line continues.

Unsuccessful approach: Treating discretionaries as spaces splits words visibly.

Case contract

Input [source, width]. Runs of spaces are one breakable space; "~" is an unbreakable space rendered " "; "\-" is a discretionary hyphen: invisible unless the line breaks there, then rendered "-"; "|" is an invisible break opportunity. Greedy: append the next chunk if the line (without a potential hyphen) fits, else break at that opportunity. Return rendered lines.

Why this case matters

Line breaking decides where paragraphs wrap on screen and in print; a wrong decision point shifts every following line.

1 / The failure

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

N = 1
observations = []
def solve(x):
    src, width = x
    chunks = ['']
    ops = []
    i = 0
    while i < len(src):
        c = src[i]
        if src.startswith('\\-', i):
            ops.append('d')
            chunks.append('')
            i += 2
            continue
        if c == ' ':
            j = i
            while j < len(src) and src[j] == ' ':
                j += 1
            ops.append('s')
            chunks.append('')
            i = j
            continue
        if c == '|':
            ops.append('z')
            chunks.append('')
            i += 1
            continue
        chunks[-1] += ' ' if c == '~' else c
        i += 1
    line = chunks[0]
    lines = []
    for op, ch in zip(ops, chunks[1:]):
        sep = ' ' if op == 's' else ('-' if op == 'd' else '')
        if len(line + sep + ch) <= width:
            line = line + sep + ch
        else:
            lines.append(line + ('-' if op == 'd' else ''))
            line = ch
    lines.append(line)
    return lines
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('regression: unbroken discretionary rendering', ['ink a a ~hy\\-phen  x com\\-po\\-si\\-tion x', 6], ['ink a', 'a  hy-', 'phen x', 'compo-', 'sition', 'x']), ('regression: unbroken discretionary rendering', ['Dr.~Knuth  url|path a Dr.~Knuth b|c b|c com\\-po\\-si\\-tion', 15], ['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'composition']), ('regression: unbroken discretionary rendering', ['b|c  set  type set Dr.~Knuth com\\-po\\-si\\-tion Dr.~Knuth  x', 15], ['bc set type set', 'Dr. Knuth compo-', 'sition', 'Dr. Knuth x']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['word  10~pt 10~pt 10~pt url|path ink', 13], ['word 10 pt', '10 pt 10 pt', 'urlpath ink']), ('control layout', ['type  set Dr.~Knuth word word b|c', 12], ['type set', 'Dr. Knuth', 'word word bc'])], [('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion ~com\\-po\\-si\\-tion  hy\\-phen x  url|path ~a a  com\\-po\\-si\\-tion', 15], ['composition', ' composition hy-', 'phen x urlpath', ' a a composi-', 'tion']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion b|c b|c x x word x', 15], ['composition bc', 'bc x x word x']), ('regression: unbroken discretionary rendering', ['word url|path ~hy\\-phen set a', 14], ['word urlpath', ' hyphen set a']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion ~ink word url|path x set', 10], ['composi-', 'tion  ink', 'word url', 'path x set']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['b|c b|c  type ~hy\\-phen Dr.~Knuth ~ink x a', 15], ['bc bc type  hy-', 'phen Dr. Knuth', ' ink x a']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc'])], [('regression: unbroken discretionary rendering', ['hy\\-phen  ~a 10~pt  type  type url|path 10~pt  type', 9], ['hyphen  a', '10 pt', 'type type', 'urlpath', '10 pt', 'type']), ('regression: unbroken discretionary rendering', ['ink x hy\\-phen ~x set  type', 12], ['ink x hyphen', ' x set type']), ('regression: unbroken discretionary rendering', ['type 10~pt com\\-po\\-si\\-tion com\\-po\\-si\\-tion type type', 12], ['type 10 pt', 'composition', 'composition', 'type type']), ('regression: unbroken discretionary rendering', ['type x url|path 10~pt hy\\-phen', 14], ['type x urlpath', '10 pt hyphen']), ('double space collapses', ['type  set', 20], ['type set']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc']), ('control layout', ['a x set', 16], ['a x set'])], [('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion  x x hy\\-phen  Dr.~Knuth Dr.~Knuth com\\-po\\-si\\-tion type  set', 14], ['composition x', 'x hyphen', 'Dr. Knuth', 'Dr. Knuth com-', 'position type', 'set']), ('regression: unbroken discretionary rendering', ['a set word  hy\\-phen  hy\\-phen word url|path', 15], ['a set word hy-', 'phen hyphen', 'word urlpath']), ('regression: unbroken discretionary rendering', ['Dr.~Knuth  hy\\-phen word  10~pt Dr.~Knuth  word', 11], ['Dr. Knuth', 'hyphen word', '10 pt', 'Dr. Knuth', 'word']), ('regression: unbroken discretionary rendering', ['hy\\-phen ink Dr.~Knuth com\\-po\\-si\\-tion hy\\-phen', 9], ['hyphen', 'ink', 'Dr. Knuth', 'composi-', 'tion hy-', 'phen']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('control layout', ['a  b|c set', 11], ['a bc set']), ('control layout', ['a ink x 10~pt  b|c a', 16], ['a ink x 10 pt bc', 'a'])], [('regression: unbroken discretionary rendering', ['url|path x  ~hy\\-phen word com\\-po\\-si\\-tion com\\-po\\-si\\-tion x', 14], ['urlpath x  hy-', 'phen word com-', 'position compo-', 'sition x']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion  ink ~Dr.~Knuth url|path  url|path  ink b|c  word  10~pt', 5], ['compo-', 'si-', 'tion', 'ink', ' Dr. Knuth', 'url', 'path', 'url', 'path', 'ink b', 'c', 'word', '10 pt']), ('regression: unbroken discretionary rendering', ['ink  hy\\-phen  ~10~pt  url|path word ink  url|path  ~type b|c', 15], ['ink hyphen', ' 10 pt urlpath', 'word ink url', 'path  type bc']), ('regression: unbroken discretionary rendering', ['b|c Dr.~Knuth a hy\\-phen a type ~a Dr.~Knuth', 8], ['bc', 'Dr. Knuth', 'a hyphen', 'a type', ' a', 'Dr. Knuth']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc']), ('control layout', ['Dr.~Knuth ~a url|path', 11], ['Dr. Knuth', ' a urlpath'])]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(args), 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
discretionary taken['the com-', 'po-si-', 'tion']['the compo-', 'sition']Failed
regression: unbroken discretionary rendering['ink a', 'a hy-', 'phen x', 'com-po-', 'si-', 'tion x']['ink a', 'a hy-', 'phen x', 'compo-', 'sition', 'x']Failed
regression: unbroken discretionary rendering['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'com-po-si-tion']['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'composition']Failed
regression: unbroken discretionary rendering['bc set type set', 'Dr. Knuth com-', 'po-si-tion', 'Dr. Knuth x']['bc set type set', 'Dr. Knuth compo-', 'sition', 'Dr. Knuth x']Failed
tie keeps title with name['see', 'Dr. Knuth', 'now']['see', 'Dr. Knuth', 'now']Passed
double space collapses['type set']['type set']Passed
control layout['word 10 pt', '10 pt 10 pt', 'urlpath ink']['word 10 pt', '10 pt 10 pt', 'urlpath ink']Passed
control layout['type set', 'Dr. Knuth', 'word word bc']['type set', 'Dr. Knuth', 'word word bc']Passed

SHA-256 / 3e31353ef924f22a665b14fabb2e6db7a1eaafdf229b05752f9ffb466bb24071

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    src, width = x
    chunks = ['']
    ops = []
    i = 0
    while i < len(src):
        c = src[i]
        if src.startswith('\\-', i):
            ops.append('d')
            chunks.append('')
            i += 2
            continue
        if c == ' ':
            j = i
            while j < len(src) and src[j] == ' ':
                j += 1
            ops.append('s')
            chunks.append('')
            i = j
            continue
        if c == '|':
            ops.append('z')
            chunks.append('')
            i += 1
            continue
        chunks[-1] += ' ' if c == '~' else c
        i += 1
    line = chunks[0]
    lines = []
    for op, ch in zip(ops, chunks[1:]):
        sep = ' ' if op != 'z' else ''
        if len(line + sep + ch) <= width:
            line = line + sep + ch
        else:
            lines.append(line + ('-' if op == 'd' else ''))
            line = ch
    lines.append(line)
    return lines
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('regression: unbroken discretionary rendering', ['ink a a ~hy\\-phen  x com\\-po\\-si\\-tion x', 6], ['ink a', 'a  hy-', 'phen x', 'compo-', 'sition', 'x']), ('regression: unbroken discretionary rendering', ['Dr.~Knuth  url|path a Dr.~Knuth b|c b|c com\\-po\\-si\\-tion', 15], ['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'composition']), ('regression: unbroken discretionary rendering', ['b|c  set  type set Dr.~Knuth com\\-po\\-si\\-tion Dr.~Knuth  x', 15], ['bc set type set', 'Dr. Knuth compo-', 'sition', 'Dr. Knuth x']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['word  10~pt 10~pt 10~pt url|path ink', 13], ['word 10 pt', '10 pt 10 pt', 'urlpath ink']), ('control layout', ['type  set Dr.~Knuth word word b|c', 12], ['type set', 'Dr. Knuth', 'word word bc'])], [('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion ~com\\-po\\-si\\-tion  hy\\-phen x  url|path ~a a  com\\-po\\-si\\-tion', 15], ['composition', ' composition hy-', 'phen x urlpath', ' a a composi-', 'tion']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion b|c b|c x x word x', 15], ['composition bc', 'bc x x word x']), ('regression: unbroken discretionary rendering', ['word url|path ~hy\\-phen set a', 14], ['word urlpath', ' hyphen set a']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion ~ink word url|path x set', 10], ['composi-', 'tion  ink', 'word url', 'path x set']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['b|c b|c  type ~hy\\-phen Dr.~Knuth ~ink x a', 15], ['bc bc type  hy-', 'phen Dr. Knuth', ' ink x a']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc'])], [('regression: unbroken discretionary rendering', ['hy\\-phen  ~a 10~pt  type  type url|path 10~pt  type', 9], ['hyphen  a', '10 pt', 'type type', 'urlpath', '10 pt', 'type']), ('regression: unbroken discretionary rendering', ['ink x hy\\-phen ~x set  type', 12], ['ink x hyphen', ' x set type']), ('regression: unbroken discretionary rendering', ['type 10~pt com\\-po\\-si\\-tion com\\-po\\-si\\-tion type type', 12], ['type 10 pt', 'composition', 'composition', 'type type']), ('regression: unbroken discretionary rendering', ['type x url|path 10~pt hy\\-phen', 14], ['type x urlpath', '10 pt hyphen']), ('double space collapses', ['type  set', 20], ['type set']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc']), ('control layout', ['a x set', 16], ['a x set'])], [('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion  x x hy\\-phen  Dr.~Knuth Dr.~Knuth com\\-po\\-si\\-tion type  set', 14], ['composition x', 'x hyphen', 'Dr. Knuth', 'Dr. Knuth com-', 'position type', 'set']), ('regression: unbroken discretionary rendering', ['a set word  hy\\-phen  hy\\-phen word url|path', 15], ['a set word hy-', 'phen hyphen', 'word urlpath']), ('regression: unbroken discretionary rendering', ['Dr.~Knuth  hy\\-phen word  10~pt Dr.~Knuth  word', 11], ['Dr. Knuth', 'hyphen word', '10 pt', 'Dr. Knuth', 'word']), ('regression: unbroken discretionary rendering', ['hy\\-phen ink Dr.~Knuth com\\-po\\-si\\-tion hy\\-phen', 9], ['hyphen', 'ink', 'Dr. Knuth', 'composi-', 'tion hy-', 'phen']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('control layout', ['a  b|c set', 11], ['a bc set']), ('control layout', ['a ink x 10~pt  b|c a', 16], ['a ink x 10 pt bc', 'a'])], [('regression: unbroken discretionary rendering', ['url|path x  ~hy\\-phen word com\\-po\\-si\\-tion com\\-po\\-si\\-tion x', 14], ['urlpath x  hy-', 'phen word com-', 'position compo-', 'sition x']), ('regression: unbroken discretionary rendering', ['com\\-po\\-si\\-tion  ink ~Dr.~Knuth url|path  url|path  ink b|c  word  10~pt', 5], ['compo-', 'si-', 'tion', 'ink', ' Dr. Knuth', 'url', 'path', 'url', 'path', 'ink b', 'c', 'word', '10 pt']), ('regression: unbroken discretionary rendering', ['ink  hy\\-phen  ~10~pt  url|path word ink  url|path  ~type b|c', 15], ['ink hyphen', ' 10 pt urlpath', 'word ink url', 'path  type bc']), ('regression: unbroken discretionary rendering', ['b|c Dr.~Knuth a hy\\-phen a type ~a Dr.~Knuth', 8], ['bc', 'Dr. Knuth', 'a hyphen', 'a type', ' a', 'Dr. Knuth']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['Dr.~Knuth ~Dr.~Knuth ~b|c', 15], ['Dr. Knuth', ' Dr. Knuth  bc']), ('control layout', ['Dr.~Knuth ~a url|path', 11], ['Dr. Knuth', ' a urlpath'])]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(args), 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
discretionary taken['the com-', 'po si-', 'tion']['the compo-', 'sition']Failed
regression: unbroken discretionary rendering['ink a', 'a hy-', 'phen x', 'com po-', 'si-', 'tion x']['ink a', 'a hy-', 'phen x', 'compo-', 'sition', 'x']Failed
regression: unbroken discretionary rendering['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'com po si tion']['Dr. Knuth url', 'path a', 'Dr. Knuth bc bc', 'composition']Failed
regression: unbroken discretionary rendering['bc set type set', 'Dr. Knuth com-', 'po si tion', 'Dr. Knuth x']['bc set type set', 'Dr. Knuth compo-', 'sition', 'Dr. Knuth x']Failed
tie keeps title with name['see', 'Dr. Knuth', 'now']['see', 'Dr. Knuth', 'now']Passed
double space collapses['type set']['type set']Passed
control layout['word 10 pt', '10 pt 10 pt', 'urlpath ink']['word 10 pt', '10 pt 10 pt', 'urlpath ink']Passed
control layout['type set', 'Dr. Knuth', 'word word bc']['type set', 'Dr. Knuth', 'word word bc']Passed

SHA-256 / b209126e650abe0c289986df00fafd9a46dedde621629b056608ace2370b0dfb

HELD IN THE MEMBER ARCHIVE

The verified repair and its recorded checks are member-only.

This mechanism has 8 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 typesetting model with integer widths and a stipulated rule set; it does not claim conformance to any engine. 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:49:51.559512+00:00.

Case digest / bf04d595ab17fa2c70fae715fdeb9c69f271dd7ec6d641757c0758008f48260a