0024 — reel plan: VO-driven timing + pre-build sanity¶
Status: IMPLEMENTED
Date: 2026-06-29
Source: REPORT-keryx-in-anger-afmpeg-reel.md (request G, pinch point #5).
Related: 0023 (take workflow), the reelcmd.ReelTimingFromVO helper (keryx !76).
1. Goal¶
There is no way to see the VO-driven total or per-card timing before a build. The
build log shows only an inaccurate fallback duration (the report saw 69s logged for a
~90s reel), so sizing the music bed meant summing ffprobe durations across vo/*.mp3 in
python. Expose the real timing as a command.
2. What already exists¶
reelcmd.ReelTimingFromVO (extracted in keryx !76) computes per-card durations + the
assembled reel.Timeline (Starts, VODelaysMS, XFadeOffsets, Total) by probing the
promoted vo/NN.mp3 clips — the same maths reel build and music gen --takes use. This
spec surfaces it; the computation already exists and is unit-tested.
3. Design¶
A read-only keryx reel plan --workspace <slug> command (no generation, no render):
- Resolves the render backend (for
Probe) exactly asbuilddoes, probes the promoted VO, and runsReelTimingFromVO. - Reports, per card: index, on-screen
dur,start,vo_delay, and the source of the duration (VO clip vs. storyboarddurfallback). - Reports the VO-driven total and the implied music-bed length (what
music gen --takeswould request) so the bed can be sized confidently. - Pre-build sanity (the bit that turns it from a duration printout into a pre-flight):
flags lines with no promoted VO (→ silent fallback or "takes generated, none selected"),
overlay cards missing
media, a missing cover, and whether a music bed is present. - Text (human table) +
--output json(machine-readable for the studio / CI).
It degrades gracefully: with no VO promoted it reports the silent-render durations (from
storyboard dur) and says so, rather than erroring.
3.5 The build log (verified 2026-07-02)¶
The root annoyance was reel build logging an inaccurate duration (69s for a ~90s
reel). On investigation this is already fixed on current main: reel build logs
Video.DurationSec, which the renderer sets to totalDuration(timeline) = sum(durs) −
(n−1)·xfade, and the timeline's durs come from the shared ReelTimingFromVO when VO is
promoted. So the logged number is the VO-driven total — the 69s was the stale installed
binary (the same "stale binary" pattern as the rest of the field report), which logged a
storyboard-dur fallback. The deliverable here is therefore a regression guard, not a
code change: pin that build times the reel via the VO-driven computation (not the dur
fallback), so the accurate log can't silently regress.
4. Requirements¶
- R-GEN-40 (MUST)
reel plan --workspace <slug>prints the VO-driven total and per-card timing (dur/start/vo_delay) without generating or rendering anything. - R-GEN-41 (MUST) The total matches what
reel buildwould assemble (sharedReelTimingFromVO); the reported bed length matches whatmusic gen --takesrequests. - R-GEN-41b (MUST)
reel buildlogs the accurate VO-driven total (via the sharedReelTimingFromVO), not the fallback duration. Already satisfied on currentmain(verified 2026-07-02); held by a regression guard that pins build's timing basis to the VO-driven durations rather than the storyboarddur. - R-GEN-42 (SHOULD)
reel planflags pre-build gaps: lines missing VO, unpromoted VO takes, overlay cards missing media, missing cover, absent music bed. - R-GEN-43 (SHOULD)
--output jsonemits the timing + gaps for the studio/CI.
5. Testing (TDD)¶
- Given promoted VO (fake renderer probe),
reel planreports the same total asReelTimingFromVO/build; per-cardstart/vo_delaymatchreel.BuildTimeline. - No-VO workspace → silent-fallback durations reported + flagged (no error).
reel build's logged total equalsReelTimingFromVO(not the fallback) for a VO-promoted workspace (R-GEN-41b).- Gap flags: a missing-media overlay card and an unpromoted-takes line are surfaced.
- JSON shape stable (golden).
6. Resolved decisions¶
Reviewed with Matt 2026-06-29:
- D1 (was Q1) — name:
reel plan(pre-flight framing, pairs withreel make). - D2 (was Q2) — breadth: timing + asset-gap flags (§3.4) — the gap pre-flight is
what makes it worth running before build, and the data is already at hand (
R-GEN-42). - D3 (was Q3) — probe backend: mirror
build—planresolvesproviders.renderand probes through it (a default-ffmpegsetup needs the binary; anafmpegsetup probes in-memory, zero-config since !75). Documented, not configurable separately.