FAILURE MAP
← Case archive

FA-80191 / Typography line breaking / Open access

Ties and discretionary hyphens: hyphen at taken discretionary · case 01

Words broken at a discretionary lose their hyphen.

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

ROOT CAUSE

The pre-break text of the discretionary is not emitted at the break.

THE FAILURE

The pre-break text of the discretionary is not emitted at the break.

Unsuccessful approach: Appending a hyphen at every non-space break also hyphenates invisible breaks.

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 len(line + sep + ch) <= width:
            line = line + sep + ch
        else:
            lines.append(line)
            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: hyphen at taken discretionary', ['a com\\-po\\-si\\-tion word', 9], ['a composi-', 'tion word']), ('partial-repair probe', ['url|path word a b|c a Dr.~Knuth  url|path  word ~10~pt', 8], ['urlpath', 'word a b', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']), ('partial-repair probe', ['word type x ink url|path set  ~a', 10], ['word type', 'x ink url', 'path set', ' a']), ('double space collapses', ['type  set', 20], ['type set']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['type word  Dr.~Knuth  b|c', 15], ['type word', 'Dr. Knuth bc']), ('control layout', ['ink  a ~a Dr.~Knuth Dr.~Knuth  type b|c', 12], ['ink a  a', 'Dr. Knuth', 'Dr. Knuth', 'type bc'])], [('regression: hyphen at taken discretionary', ['word hy\\-phen set word ~set Dr.~Knuth  word  word', 8], ['word hy-', 'phen set', 'word', ' set', 'Dr. Knuth', 'word', 'word']), ('regression: hyphen at taken discretionary', ['Dr.~Knuth Dr.~Knuth ~url|path a a  ~hy\\-phen  ~Dr.~Knuth  hy\\-phen com\\-po\\-si\\-tion', 14], ['Dr. Knuth', 'Dr. Knuth  url', 'path a a  hy-', 'phen', ' Dr. Knuth hy-', 'phen composi-', 'tion']), ('partial-repair probe', ['x com\\-po\\-si\\-tion b|c', 15], ['x composition b', 'c']), ('partial-repair probe', ['set ink ink word url|path Dr.~Knuth hy\\-phen word', 11], ['set ink ink', 'word url', 'path', 'Dr. Knuth', 'hyphen word']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['Dr.~Knuth hy\\-phen a type ~a type', 7], ['Dr. Knuth', 'hyphen', 'a type', ' a type']), ('control layout', ['ink  a ~a Dr.~Knuth Dr.~Knuth  type b|c', 12], ['ink a  a', 'Dr. Knuth', 'Dr. Knuth', 'type bc'])], [('regression: hyphen at taken discretionary', ['set  url|path a com\\-po\\-si\\-tion x', 12], ['set urlpath', 'a composi-', 'tion x']), ('regression: hyphen at taken discretionary', ['10~pt ink hy\\-phen word', 12], ['10 pt ink hy-', 'phen word']), ('partial-repair probe', ['ink 10~pt ink b|c hy\\-phen ~set url|path word', 16], ['ink 10 pt ink bc', 'hyphen  set url', 'path word']), ('regression: hyphen at taken discretionary', ['ink  com\\-po\\-si\\-tion a  url|path a  ink url|path  url|path ~10~pt', 6], ['ink', 'compo-', 'sition', 'a url', 'path a', 'ink', 'url', 'path', 'url', 'path', ' 10 pt']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('control layout', ['a  Dr.~Knuth  Dr.~Knuth  ink', 11], ['a Dr. Knuth', 'Dr. Knuth', 'ink']), ('control layout', ['a  x a Dr.~Knuth ~type set hy\\-phen  ~type', 16], ['a x a Dr. Knuth', ' type set hyphen', ' type'])], [('regression: hyphen at taken discretionary', ['x  10~pt a ~Dr.~Knuth b|c  Dr.~Knuth ~hy\\-phen  set  com\\-po\\-si\\-tion', 16], ['x 10 pt a', ' Dr. Knuth bc', 'Dr. Knuth  hy-', 'phen set composi-', 'tion']), ('regression: hyphen at taken discretionary', ['x set  type com\\-po\\-si\\-tion a ~ink b|c hy\\-phen url|path', 13], ['x set type', 'composition a', ' ink bc hy-', 'phen urlpath']), ('partial-repair probe', ['10~pt b|c  ~Dr.~Knuth  b|c  set', 12], ['10 pt bc', ' Dr. Knuth b', 'c set']), ('partial-repair probe', ['Dr.~Knuth url|path b|c set url|path 10~pt', 10], ['Dr. Knuth', 'urlpath bc', 'set url', 'path 10 pt']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['hy\\-phen a ~a ~b|c set ~ink', 8], ['hyphen a', ' a  bc', 'set  ink']), ('control layout', ['Dr.~Knuth set 10~pt ink ink', 10], ['Dr. Knuth', 'set 10 pt', 'ink ink'])], [('regression: hyphen at taken discretionary', ['hy\\-phen  ~com\\-po\\-si\\-tion 10~pt word  b|c', 15], ['hyphen  composi-', 'tion 10 pt word', 'bc']), ('regression: hyphen at taken discretionary', ['a Dr.~Knuth x ~set word type  ~com\\-po\\-si\\-tion', 6], ['a', 'Dr. Knuth', 'x  set', 'word', 'type', ' compo-', 'sition']), ('partial-repair probe', ['word set  a  x ~set ~set  b|c x', 6], ['word', 'set a', 'x  set', ' set b', 'c x']), ('regression: hyphen at taken discretionary', ['url|path ink  set Dr.~Knuth hy\\-phen set type x', 5], ['url', 'path', 'ink', 'set', 'Dr. Knuth', 'hy-', 'phen', 'set', 'type', 'x']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['ink com\\-po\\-si\\-tion Dr.~Knuth', 16], ['ink composition', 'Dr. Knuth']), ('control layout', ['a b|c x b|c', 15], ['a bc x bc'])]]
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 compo', 'sition']['the compo-', 'sition']Failed
regression: hyphen at taken discretionary['a composi', 'tion word']['a composi-', 'tion word']Failed
partial-repair probe['urlpath', 'word a b', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']['urlpath', 'word a b', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']Passed
partial-repair probe['word type', 'x ink url', 'path set', ' a']['word type', 'x ink url', 'path set', ' a']Passed
double space collapses['type set']['type set']Passed
tie keeps title with name['see', 'Dr. Knuth', 'now']['see', 'Dr. Knuth', 'now']Passed
control layout['type word', 'Dr. Knuth bc']['type word', 'Dr. Knuth bc']Passed
control layout['ink a a', 'Dr. Knuth', 'Dr. Knuth', 'type bc']['ink a a', 'Dr. Knuth', 'Dr. Knuth', 'type bc']Passed

SHA-256 / de9d20626b21bc8dde5676581adf40ed3fa330cef948a43f2b93c54852b84eab

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 == 's' else ''
        if len(line + sep + ch) <= width:
            line = line + sep + ch
        else:
            lines.append(line + ('-' if op != 's' 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: hyphen at taken discretionary', ['a com\\-po\\-si\\-tion word', 9], ['a composi-', 'tion word']), ('partial-repair probe', ['url|path word a b|c a Dr.~Knuth  url|path  word ~10~pt', 8], ['urlpath', 'word a b', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']), ('partial-repair probe', ['word type x ink url|path set  ~a', 10], ['word type', 'x ink url', 'path set', ' a']), ('double space collapses', ['type  set', 20], ['type set']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['type word  Dr.~Knuth  b|c', 15], ['type word', 'Dr. Knuth bc']), ('control layout', ['ink  a ~a Dr.~Knuth Dr.~Knuth  type b|c', 12], ['ink a  a', 'Dr. Knuth', 'Dr. Knuth', 'type bc'])], [('regression: hyphen at taken discretionary', ['word hy\\-phen set word ~set Dr.~Knuth  word  word', 8], ['word hy-', 'phen set', 'word', ' set', 'Dr. Knuth', 'word', 'word']), ('regression: hyphen at taken discretionary', ['Dr.~Knuth Dr.~Knuth ~url|path a a  ~hy\\-phen  ~Dr.~Knuth  hy\\-phen com\\-po\\-si\\-tion', 14], ['Dr. Knuth', 'Dr. Knuth  url', 'path a a  hy-', 'phen', ' Dr. Knuth hy-', 'phen composi-', 'tion']), ('partial-repair probe', ['x com\\-po\\-si\\-tion b|c', 15], ['x composition b', 'c']), ('partial-repair probe', ['set ink ink word url|path Dr.~Knuth hy\\-phen word', 11], ['set ink ink', 'word url', 'path', 'Dr. Knuth', 'hyphen word']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['Dr.~Knuth hy\\-phen a type ~a type', 7], ['Dr. Knuth', 'hyphen', 'a type', ' a type']), ('control layout', ['ink  a ~a Dr.~Knuth Dr.~Knuth  type b|c', 12], ['ink a  a', 'Dr. Knuth', 'Dr. Knuth', 'type bc'])], [('regression: hyphen at taken discretionary', ['set  url|path a com\\-po\\-si\\-tion x', 12], ['set urlpath', 'a composi-', 'tion x']), ('regression: hyphen at taken discretionary', ['10~pt ink hy\\-phen word', 12], ['10 pt ink hy-', 'phen word']), ('partial-repair probe', ['ink 10~pt ink b|c hy\\-phen ~set url|path word', 16], ['ink 10 pt ink bc', 'hyphen  set url', 'path word']), ('regression: hyphen at taken discretionary', ['ink  com\\-po\\-si\\-tion a  url|path a  ink url|path  url|path ~10~pt', 6], ['ink', 'compo-', 'sition', 'a url', 'path a', 'ink', 'url', 'path', 'url', 'path', ' 10 pt']), ('discretionary taken', ['the com\\-po\\-si\\-tion', 9], ['the compo-', 'sition']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('control layout', ['a  Dr.~Knuth  Dr.~Knuth  ink', 11], ['a Dr. Knuth', 'Dr. Knuth', 'ink']), ('control layout', ['a  x a Dr.~Knuth ~type set hy\\-phen  ~type', 16], ['a x a Dr. Knuth', ' type set hyphen', ' type'])], [('regression: hyphen at taken discretionary', ['x  10~pt a ~Dr.~Knuth b|c  Dr.~Knuth ~hy\\-phen  set  com\\-po\\-si\\-tion', 16], ['x 10 pt a', ' Dr. Knuth bc', 'Dr. Knuth  hy-', 'phen set composi-', 'tion']), ('regression: hyphen at taken discretionary', ['x set  type com\\-po\\-si\\-tion a ~ink b|c hy\\-phen url|path', 13], ['x set type', 'composition a', ' ink bc hy-', 'phen urlpath']), ('partial-repair probe', ['10~pt b|c  ~Dr.~Knuth  b|c  set', 12], ['10 pt bc', ' Dr. Knuth b', 'c set']), ('partial-repair probe', ['Dr.~Knuth url|path b|c set url|path 10~pt', 10], ['Dr. Knuth', 'urlpath bc', 'set url', 'path 10 pt']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('double space collapses', ['type  set', 20], ['type set']), ('control layout', ['hy\\-phen a ~a ~b|c set ~ink', 8], ['hyphen a', ' a  bc', 'set  ink']), ('control layout', ['Dr.~Knuth set 10~pt ink ink', 10], ['Dr. Knuth', 'set 10 pt', 'ink ink'])], [('regression: hyphen at taken discretionary', ['hy\\-phen  ~com\\-po\\-si\\-tion 10~pt word  b|c', 15], ['hyphen  composi-', 'tion 10 pt word', 'bc']), ('regression: hyphen at taken discretionary', ['a Dr.~Knuth x ~set word type  ~com\\-po\\-si\\-tion', 6], ['a', 'Dr. Knuth', 'x  set', 'word', 'type', ' compo-', 'sition']), ('partial-repair probe', ['word set  a  x ~set ~set  b|c x', 6], ['word', 'set a', 'x  set', ' set b', 'c x']), ('regression: hyphen at taken discretionary', ['url|path ink  set Dr.~Knuth hy\\-phen set type x', 5], ['url', 'path', 'ink', 'set', 'Dr. Knuth', 'hy-', 'phen', 'set', 'type', 'x']), ('invisible break', ['url|path|x', 5], ['url', 'pathx']), ('tie keeps title with name', ['see Dr.~Knuth now', 9], ['see', 'Dr. Knuth', 'now']), ('control layout', ['ink com\\-po\\-si\\-tion Dr.~Knuth', 16], ['ink composition', 'Dr. Knuth']), ('control layout', ['a b|c x b|c', 15], ['a bc x bc'])]]
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 compo-', 'sition']['the compo-', 'sition']Passed
regression: hyphen at taken discretionary['a composi-', 'tion word']['a composi-', 'tion word']Passed
partial-repair probe['urlpath', 'word a b-', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']['urlpath', 'word a b', 'c a', 'Dr. Knuth', 'urlpath', 'word', ' 10 pt']Failed
partial-repair probe['word type', 'x ink url-', 'path set', ' a']['word type', 'x ink url', 'path set', ' a']Failed
double space collapses['type set']['type set']Passed
tie keeps title with name['see', 'Dr. Knuth', 'now']['see', 'Dr. Knuth', 'now']Passed
control layout['type word', 'Dr. Knuth bc']['type word', 'Dr. Knuth bc']Passed
control layout['ink a a', 'Dr. Knuth', 'Dr. Knuth', 'type bc']['ink a a', 'Dr. Knuth', 'Dr. Knuth', 'type bc']Passed

SHA-256 / c3e2d56adb4be6bb6fbc42b39498989dd1f6e733170f2a28c88e108644c6432f

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

Case digest / d8788d3a1c76a904b6982fff6c4174954393fd5e9b7215ff64f22a584d178817