FA-78286 / Subtitle cue timing / Open access
Recommended cue duration on frame grid: base allowance · case 01
Short subtitles get barely any screen time.
ROOT CAUSE
The fixed base allowance is omitted from the duration formula.
VERIFIED REPAIR
Add the base allowance to the per-character time.
Unsuccessful approach: Taking the larger of base and character time drops the base for longer lines.
Case contract
rules [base,per_char,min,max]. Count characters other than spaces and line breaks; duration = base + count*per_char, clamped to [min,max]; then rounded up to whole frames at fps [N,D]. Return [frames, ceil(frames*1000*D/N)].
Why this case matters
Subtitle timing defects shift, hide or overlap captions that viewers depend on for comprehension and accessibility.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(text, fps, rules):
base,per,lo,hi=rules
n=len(text.replace(' ','').replace('\n',''))
d=n*per
d=max(lo,min(hi,d))
num,den=fps
fr=-((-d*num)//(1000*den))
return [fr,-((-fr*1000*den)//num)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: base allowance', ['A much longer subtitle line to read', [25, 1], [500, 60, 1000, 2000]], [50, 2000]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair probe: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('partial repair variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', [' ', [25, 1], [700, 100, 1000, 2000]], [25, 1000]), ('normal control', ['Two\nlines here', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', ['Hi', [24, 1], [0, 70, 800, 7000]], [20, 834])], [('regression: base allowance', ['A much longer subtitle line to read', [24000, 1001], [300, 50, 1200, 1500]], [36, 1502]), ('regression variant: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair probe: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30, 1], [300, 50, 1200, 1500]], [45, 1500]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hello world', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [24000, 1001], [0, 70, 800, 7000]], [20, 835]), ('normal control', ['Two\nlines here', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202])], [('regression: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('regression variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [300, 50, 1200, 1500]], [45, 1502]), ('partial repair probe: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('partial repair variant: base allowance', ['Hello world', [24000, 1001], [700, 100, 1000, 2000]], [41, 1711]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['Hello world', [30000, 1001], [0, 70, 800, 7000]], [24, 801]), ('normal control', ['ok', [30, 1], [500, 60, 1000, 6000]], [30, 1000]), ('normal control', [' ', [24, 1], [700, 100, 1000, 2000]], [24, 1000])], [('regression: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('regression variant: base allowance', ['Hello world', [30, 1], [500, 60, 1000, 6000]], [33, 1100]), ('partial repair probe: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair variant: base allowance', ['Two\nlines here', [24, 1], [500, 60, 1000, 6000]], [30, 1250]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hi', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202]), ('normal control', ['ok', [24, 1], [700, 100, 1000, 2000]], [24, 1000]), ('normal control', [' ', [25, 1], [500, 60, 1000, 6000]], [25, 1000])], [('regression: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('partial repair probe: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [500, 60, 1000, 6000]], [68, 2269]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['ok', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [30, 1], [0, 70, 800, 7000]], [24, 800]), ('normal control', ['Two\nlines here', [24, 1], [0, 70, 800, 7000]], [21, 875])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: base allowance | [44, 1760] | [50, 2000] | Failed |
| regression variant: base allowance | [30, 1001] | [33, 1102] | Failed |
| partial repair probe: base allowance | [30, 1000] | [51, 1700] | Failed |
| partial repair variant: base allowance | [30, 1001] | [51, 1702] | Failed |
| boundary control | [25, 1000] | [25, 1000] | Passed |
| boundary control | [26, 868] | [26, 868] | Passed |
| normal control | [25, 1000] | [25, 1000] | Passed |
| normal control | [30, 1200] | [30, 1200] | Passed |
| normal control | [20, 834] | [20, 834] | Passed |
SHA-256 / 2e19eba27f06a1272e076d27f6e2fa68f2ac1f74e60a57e97fd4f33ff97341a0
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(text, fps, rules):
base,per,lo,hi=rules
n=len(text.replace(' ','').replace('\n',''))
d=max(base,n*per)
d=max(lo,min(hi,d))
num,den=fps
fr=-((-d*num)//(1000*den))
return [fr,-((-fr*1000*den)//num)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: base allowance', ['A much longer subtitle line to read', [25, 1], [500, 60, 1000, 2000]], [50, 2000]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair probe: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('partial repair variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', [' ', [25, 1], [700, 100, 1000, 2000]], [25, 1000]), ('normal control', ['Two\nlines here', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', ['Hi', [24, 1], [0, 70, 800, 7000]], [20, 834])], [('regression: base allowance', ['A much longer subtitle line to read', [24000, 1001], [300, 50, 1200, 1500]], [36, 1502]), ('regression variant: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair probe: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30, 1], [300, 50, 1200, 1500]], [45, 1500]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hello world', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [24000, 1001], [0, 70, 800, 7000]], [20, 835]), ('normal control', ['Two\nlines here', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202])], [('regression: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('regression variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [300, 50, 1200, 1500]], [45, 1502]), ('partial repair probe: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('partial repair variant: base allowance', ['Hello world', [24000, 1001], [700, 100, 1000, 2000]], [41, 1711]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['Hello world', [30000, 1001], [0, 70, 800, 7000]], [24, 801]), ('normal control', ['ok', [30, 1], [500, 60, 1000, 6000]], [30, 1000]), ('normal control', [' ', [24, 1], [700, 100, 1000, 2000]], [24, 1000])], [('regression: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('regression variant: base allowance', ['Hello world', [30, 1], [500, 60, 1000, 6000]], [33, 1100]), ('partial repair probe: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair variant: base allowance', ['Two\nlines here', [24, 1], [500, 60, 1000, 6000]], [30, 1250]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hi', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202]), ('normal control', ['ok', [24, 1], [700, 100, 1000, 2000]], [24, 1000]), ('normal control', [' ', [25, 1], [500, 60, 1000, 6000]], [25, 1000])], [('regression: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('partial repair probe: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [500, 60, 1000, 6000]], [68, 2269]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['ok', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [30, 1], [0, 70, 800, 7000]], [24, 800]), ('normal control', ['Two\nlines here', [24, 1], [0, 70, 800, 7000]], [21, 875])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: base allowance | [44, 1760] | [50, 2000] | Failed |
| regression variant: base allowance | [30, 1001] | [33, 1102] | Failed |
| partial repair probe: base allowance | [30, 1000] | [51, 1700] | Failed |
| partial repair variant: base allowance | [30, 1001] | [51, 1702] | Failed |
| boundary control | [25, 1000] | [25, 1000] | Passed |
| boundary control | [26, 868] | [26, 868] | Passed |
| normal control | [25, 1000] | [25, 1000] | Passed |
| normal control | [30, 1200] | [30, 1200] | Passed |
| normal control | [20, 834] | [20, 834] | Passed |
SHA-256 / 897b1d975993b8928f34bab8eb6616fd01a29e524ecdfd47810151e18fa08de1
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(text, fps, rules):
base,per,lo,hi=rules
n=len(text.replace(' ','').replace('\n',''))
d=base+n*per
d=max(lo,min(hi,d))
num,den=fps
fr=-((-d*num)//(1000*den))
return [fr,-((-fr*1000*den)//num)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: base allowance', ['A much longer subtitle line to read', [25, 1], [500, 60, 1000, 2000]], [50, 2000]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair probe: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('partial repair variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', [' ', [25, 1], [700, 100, 1000, 2000]], [25, 1000]), ('normal control', ['Two\nlines here', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', ['Hi', [24, 1], [0, 70, 800, 7000]], [20, 834])], [('regression: base allowance', ['A much longer subtitle line to read', [24000, 1001], [300, 50, 1200, 1500]], [36, 1502]), ('regression variant: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair probe: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30, 1], [300, 50, 1200, 1500]], [45, 1500]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hello world', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [24000, 1001], [0, 70, 800, 7000]], [20, 835]), ('normal control', ['Two\nlines here', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202])], [('regression: base allowance', ['Hello world', [30, 1], [700, 100, 1000, 2000]], [51, 1700]), ('regression variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [300, 50, 1200, 1500]], [45, 1502]), ('partial repair probe: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('partial repair variant: base allowance', ['Hello world', [24000, 1001], [700, 100, 1000, 2000]], [41, 1711]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['Hello world', [30000, 1001], [0, 70, 800, 7000]], [24, 801]), ('normal control', ['ok', [30, 1], [500, 60, 1000, 6000]], [30, 1000]), ('normal control', [' ', [24, 1], [700, 100, 1000, 2000]], [24, 1000])], [('regression: base allowance', ['Two\nlines here', [30, 1], [700, 100, 1000, 2000]], [57, 1900]), ('regression variant: base allowance', ['Hello world', [30, 1], [500, 60, 1000, 6000]], [33, 1100]), ('partial repair probe: base allowance', ['Hello world', [30000, 1001], [500, 60, 1000, 6000]], [33, 1102]), ('partial repair variant: base allowance', ['Two\nlines here', [24, 1], [500, 60, 1000, 6000]], [30, 1250]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('normal control', ['Hi', [30000, 1001], [300, 50, 1200, 1500]], [36, 1202]), ('normal control', ['ok', [24, 1], [700, 100, 1000, 2000]], [24, 1000]), ('normal control', [' ', [25, 1], [500, 60, 1000, 6000]], [25, 1000])], [('regression: base allowance', ['Two\nlines here', [24000, 1001], [700, 100, 1000, 2000]], [46, 1919]), ('regression variant: base allowance', ['Hello world', [30000, 1001], [700, 100, 1000, 2000]], [51, 1702]), ('partial repair probe: base allowance', ['Hello world', [24000, 1001], [500, 60, 1000, 6000]], [27, 1127]), ('partial repair variant: base allowance', ['A much longer subtitle line to read', [30000, 1001], [500, 60, 1000, 6000]], [68, 2269]), ('boundary control', ['Hi', [25, 1], [500, 60, 1000, 6000]], [25, 1000]), ('boundary control', ['Two\nlines here', [30000, 1001], [0, 70, 800, 7000]], [26, 868]), ('normal control', ['ok', [25, 1], [300, 50, 1200, 1500]], [30, 1200]), ('normal control', [' ', [30, 1], [0, 70, 800, 7000]], [24, 800]), ('normal control', ['Two\nlines here', [24, 1], [0, 70, 800, 7000]], [21, 875])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: base allowance | [50, 2000] | [50, 2000] | Passed |
| regression variant: base allowance | [33, 1102] | [33, 1102] | Passed |
| partial repair probe: base allowance | [51, 1700] | [51, 1700] | Passed |
| partial repair variant: base allowance | [51, 1702] | [51, 1702] | Passed |
| boundary control | [25, 1000] | [25, 1000] | Passed |
| boundary control | [26, 868] | [26, 868] | Passed |
| normal control | [25, 1000] | [25, 1000] | Passed |
| normal control | [30, 1200] | [30, 1200] | Passed |
| normal control | [20, 834] | [20, 834] | Passed |
SHA-256 / aedbb4acaf1d1e375790a0a68b443dba88aaf6479d2396541dd67759c6a1ecd4
Verification & scope
A deterministic bounded teaching model with a stipulated toy contract; it does not claim conformance to any subtitle 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:49:33.994071+00:00.
Case digest / 64f4bcd7ff789dbc71288af8623837b342cf42777765b5656761c23e516c8162