video-compose: catch TTS punctuation leaks in final review

Adds a transcript_comparison check to VideoCompose._run_final_review
that word-diffs the whisper/whisperx transcript against script.txt and
fails loudly when the TTS engine literally voiced punctuation tokens
(dot, dots, ellipsis, comma, dash, hyphen, period). Chirp3-HD did this
to ellipses in a production run and it slipped past review; now it
cannot.

Also corrects the "tiny background video" gotcha in
skills/core/hyperframes.md. After six renders of blaming HyperFrames
CSS, the real root cause was 640x360 Pexels sources combined with a
fit-and-pad pre-transform — HyperFrames was rendering the letterboxed
input faithfully. Gotcha now walks through the ffprobe diagnostic and
the scale-to-cover fix, and keeps the wrapper-div pattern for the
right reasons (aspect mismatch handling, not framework bug workaround).

Four regression tests cover the new check: punctuation-leak detection,
clean-audio false-positive guard, graceful skip when inputs missing,
and always-present transcript_comparison section.
This commit is contained in:
calesthio
2026-04-18 22:16:27 -07:00
parent b6ce481073
commit 0efed7427c
3 changed files with 431 additions and 42 deletions
+60 -38
View File
@@ -288,54 +288,76 @@ Same pattern as Remotion.
Things the upstream docs don't warn about but will cost you a 60-minute
render to discover. Fix at author time, not at render time.
### Video elements need explicit size, in BOTH HTML attrs and `!important` CSS
### Full-frame background video: source-resolution trap (NOT a framework bug)
HyperFrames runtime applies inline `style="width:...px; height:...px"` to
every `<video>` element based on the video's **intrinsic** dimensions.
That inline style beats any class-selector CSS you write, so even a
`video.bg-video { width: 100%; height: 100% }` rule gets silently ignored
and the video renders at whatever size the decoded stream reports —
usually a small centered box inside an otherwise-black clip.
Symptom: background video renders as a small centered box with black
around it, even though you've told HyperFrames the clip is 1920×1080.
Six renders of debugging revealed this is **almost always a source
quality issue, not a HyperFrames framework bug.** Fix the input, not
the CSS.
The bug is invisible in some scenarios:
- Low `filter: brightness(0.25-0.35)` makes the small video box look
mostly black.
- Fullscreen typography layered on top hides the problem.
- `object-fit: cover` on a class selector does NOT fix it because the
inline width/height wins first.
Root cause (observed on Pexels + Pixabay stock): many free stock
clips ship at 640×360 or 960×540 even when you ask for the "large"
size tier. If your pre-transform does
`scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:...`
you'll produce a 1920×1080 file whose visible content is a centered
640×360 rectangle with black padding. HyperFrames plays this clip
faithfully — the small-video-in-black-frame you see is a correctly
rendered letterboxed input.
It becomes obvious the moment you go footage-forward (brightness > 0.5,
lower-third typography) — the small centered video box appears against
a black frame that was supposed to be full-bleed b-roll.
Diagnostic first: before blaming CSS, run
`ffprobe -v error -select_streams v -show_entries stream=width,height`
on the source clip and on your pre-transformed clip. If the pre-
transform already shows a 640×360 active region inside 1920×1080
padding, the problem is upstream.
**Correct pattern** for a full-frame background video:
Fix the pre-transform to scale-to-COVER and crop, not scale-to-FIT
and pad:
```
-vf "scale=1920:1080:force_original_aspect_ratio=increase,\
crop=1920:1080,unsharp=3:3:0.6"
```
This upscales the small clip to at least cover 1920×1080, crops the
overflow, and sharpens (unsharp) to offset perceived softness. The
output is full-bleed and HyperFrames will render it edge-to-edge.
The canonical wrapper-div pattern (`patterns.md`) is still the right
HTML shape for backgrounds:
```html
<video
class="clip bg-video"
data-start="0" data-duration="8" data-track-index="0"
src="assets/hero.mp4"
muted playsinline preload="metadata"
width="1920" height="1080"
></video>
<div style="position:absolute;top:0;left:0;width:1920px;height:1080px;overflow:hidden;">
<video
data-start="0" data-duration="60" data-track-index="0"
src="..." muted playsinline
style="width:100%;height:100%;object-fit:cover;"
></video>
</div>
```
```css
video.bg-video {
position: absolute !important; inset: 0 !important;
width: 100% !important; height: 100% !important;
object-fit: cover !important;
/* filter optional */
}
```
Use it because `overflow: hidden` on the wrapper crops gracefully when
the video's aspect differs slightly from 16:9, and because
`object-fit: cover` on the inner `<video>` handles the (rare) case
where source aspect isn't 16:9. It is **not** a workaround for a
framework layout bug — the framework will size `<video class="clip">`
correctly too if the source file has content filling the full frame.
Both the explicit `width="1920" height="1080"` HTML attrs AND the
`!important` CSS are required. Either one alone isn't enough.
Apply visual treatments (`filter`, `border-radius`, etc.) on the
wrapper or on a scoped selector like `.bg-slot video { filter: ... }`.
Alternative: wrap the `<video>` in a `<div class="clip">` and put
`data-start`/`data-duration` on the wrapper. Let the video fill its div
via `position: absolute; inset: 0` without the framework's inline-style
interference. Slightly more DOM but more robust.
Invisible-vs-obvious failure mode: this trap hides when you stack
dim filters (`brightness(0.250.35)`) or full-bleed typography on top —
the letterbox reads as "moody darkness." It becomes obvious the moment
you go footage-forward (brightness > 0.5, lower-third typography). If
you're planning footage-forward from the start, probe your source
resolutions in the asset stage.
When you truly can't source HD clips, switch pipelines: use FFmpeg
hybrid-compositing (scale-to-cover + crop + unsharp in an external
b-roll reel, then overlay HyperFrames typography via chromakey). That's
the pattern `projects/quantum-willow-multiverse/` settled on after six
HyperFrames renders with 640×360 Pexels sources.
### Always preview-scrub footage-forward scenes before render