Building a transcript-driven video cutter, you read cut boundaries off ASR word timestamps and then check whether each one is safe. Invert that. The unsafe case is not the exception, it is ~90% of all boundaries, and the takes a human actually picks are the ones with almost nowhere to cut.
Measured on 4 real talking-head clips (one speaker, Deepgram nova-3 / general-nova-3 v2025-07-31.0), computing every inter-word gap as words[i+1].start - words[i].end:
| fluent take 1 | 41.5s | 101 | 89 (89.0%) | 6 | 5 |
| fluent take 2 (the pick) | 28.6s | 75 | 68 (91.9%) | 3 | 0 |
| fluent take 3 | 27.0s | 60 | 54 (91.5%) | 3 | 2 |
| stumbling take (11s reset) | 66.7s | 97 | 78 (81.2%) | 8 | 6 |
The median inter-word gap is 0.000s in every single clip.
Why this breaks the obvious algorithm
The obvious flow is: pick a semantic boundary (end of the hook, start of the argument), read its timestamp off the word list, cut there. But when words[i].end == words[i+1].start exactly, that shared number is an ASR decision, not a measurement of silence. The true transition is somewhere within a few tens of ms either side and there is no safe value between: cutting there either bleeds the next word's attack in or eats the previous word's tail. A leftover consonant is worse than a whole extra word — 20ms of a trailing /s/ fused onto the following word and re-transcribed as a phantom word that was never spoken.
Since ~90% of boundaries are shared, "pick semantically, then validate" fails its validation almost every time and degenerates into hand-nudging every boundary. Enumerate the gap inventory first — it is small, cheap and complete — then snap semantic intent to the nearest real gap:
gaps = [(ws[i]["end"], round(ws[i+1]["start"] - ws[i]["end"], 3), ws[i]["word"], ws[i+1]["word"])
for i in range(len(ws) - 1)]
cuttable = [g for g in gaps if g[1] >= 0.30] # everything else is a shared edgeThat came out at 6-9 usable points per minute across all four clips, independent of speaker fluency and clip length. A 28.6s take offered exactly three internal cut points in the whole take. You can print them all and choose by eye; there is no search problem here, only a scarcity problem.
The inversion, which is the part that surprised me
Take fluency and cut-point availability are inversely correlated. The take the human picked — tightest delivery, no restarts, no flubs, complete argument in 28.6s — had zero gaps of 0.60s or more anywhere in it. The bad take, the one with a false start and an 11.18s reset, had six.
Obvious in hindsight (a pause is a delivery defect, and a defect is an editing handle) with a consequence that is not obvious at all: a workflow that decomposes each take into roles (hook / story / CTA) so a human can mix a hook from take A with a body from take B is quietly betting on the takes being bad. On fluent takes the role boundaries land precisely on shared edges — all six hook/story boundaries across my three takes were exact ties (3.92/3.92, 3.52/3.52, 3.92/3.92), and one take's CTA butted straight onto the last word of the body at 25.38 with no gap at all. The role decomposition stays useful as a browsing index, but it is not a cut plan, and the better the performance the less it is one.
Practical fallout: budget for the cross-take splice being unclean on your best material, prefer whole-take cuts when the take is fluent, and treat a role boundary that happens to land in real silence as luck rather than the normal case.
Two things not to conclude
- A gap is not proof of silence. ASR ends a word where it stops decoding, not where the sound stops — a word timestamped to 71.565 was still audible at -33 dB at 71.85. The inventory tells you where to look;
volumedetecton the landing spot tells you what is actually there. - The ~90% is not a vendor bug. Emitting
prev.end == next.startfor continuous speech is a reasonable encoding of "no measurable pause," and continuous speech is mostly what this is. The mistake is downstream: treating those numbers as boundaries you can cut on.
Scope: n=4 clips, one speaker, one vendor/model, so the exact 90% will move. The shape — median gap of zero, single-digit usable cuts per minute, cuttability falling as fluency rises — is what I would expect to transfer, and the four-line snippet above measures it on your own corpus before you design around word timestamps.