fix(producer): credit looping short videos in coverage gate (#2665)#2732
Merged
Conversation
#2606 taught the video-coverage gate that a non-looping short video holds its final decoded frame across the tail, so the delivered source frames are enough to cover the authored slot. But that fix gated the credit on '!video.loop' — a looping short video was still measured as unique-source-frames / slot-frames and aborted at ratio << threshold. Reproduced on 0.7.64 with the reporter's exact composition (3s clip in a 10s slot, loop attribute): render aborts with 'captured 90 of expected 300 frames (coverage 30.0%)'. Same source without loop renders clean via #2606's freeze credit. This is the mainline 'loop a short clip to fill a longer scene' case, the reason loop exists. Fix: extend #2606's source-credit to loops symmetrically — the delivered set (all N source frames) covers every repeat within the slot, so expectedFrames = min(slotFrames, sourceFrames) for both hold and loop. Fail-loud preserved for a genuinely-broken loop (extractor truncated below its own source): a 60/90 delivery still aborts at 66.7% < 95% because the delivered set no longer covers the full source period the loop reuses. Missing extractions still require the full slot. Test updates: - 'still requires the full authored slot for looping clips' locked in the buggy behavior; replaced with 'credits a looping short clip against the source portion' which asserts the correct 90/90/1.0. - Added 'still fails when a looping clip's source extraction is truncated' as the new fail-loud floor. Fixes #2665. Regression window: 0.7.60 (#2606's original ship) through 0.7.67 (current). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
miguel-heygen
approved these changes
Jul 22, 2026
miguel-heygen
left a comment
Collaborator
There was a problem hiding this comment.
Clean fix at exact head 052c7835ee30c048520fd68f85538cf72a3ddb86.
Strengths
packages/producer/src/services/render/videoFrameCoverage.ts:143–:161correctly aligns the denominator with the extractor contract: held tails and loops both reuse a complete unique-source frame set, while missing entries and unusable source durations still fail closed against the full slot.packages/producer/src/services/render/videoFrameCoverage.test.ts:158–:176pins both sides of the regression: 90/90 complete loop coverage passes, while a truncated 60/90 loop still trips the 95% fail-loud gate.
Verification
- Traced both local extraction and distributed reconstruction:
framePathsis rebuilt from the unique extracted files in both paths, so source-duration credit is the correct invariant. - Focused suite: 23/23 pass.
git diff --checkpasses. GitHub Format, Lint, Producer unit/integration, Build, Typecheck, runtime contract, and CLI smoke are green; remaining required general Test/Windows lanes are still running, not failing.
Verdict: APPROVE
Reasoning: The change removes the loop-only false positive without weakening detection for missing or truncated extraction, and the regression proof covers both success and failure semantics.
— Magi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #2665. Regression window: 0.7.60 (#2606's original ship) → 0.7.67 (current).
#2606 taught the video-coverage gate that a non-looping short video holds its final decoded frame across the tail, so the delivered source frames are enough to cover the authored slot. But that fix gated the credit on
!video.loop— a looping short video was still measured asunique-source-frames / slot-framesand aborted withcaptured 90 of expected 300 frames (coverage 30.0%).This is the mainline "loop a short clip to fill a longer scene" case — the reason
loopexists. Same source withoutlooprenders clean via #2606's freeze credit.Fix: extend #2606's source-credit to loops symmetrically. The delivered set (all N source frames) covers every repeat within the slot, so
expectedFrames = min(slotFrames, sourceFrames)for both hold and loop. Inpackages/producer/src/services/render/videoFrameCoverage.ts:Fail-loud preserved. A genuinely-broken loop (extractor truncated below its own source) still aborts: a 60/90 delivery on a 300-frame slot with a 3s source reports
expectedFrames=90, capturedFrames=60, ratio=0.667→ below 0.95 → throws. Missing extractions (!entry) continue to require the full slot.Testing
bunx vitest run packages/producer/src/services/render/videoFrameCoverage.test.ts— 23 tests pass.still requires the full authored slot for looping clips— assertedratio ≈ 0.3) with a correctness test assertingexpectedFrames=90, capturedFrames=90, ratio=1.0for a looping 3s clip in a 10s slot.still fails when a looping clip's source extraction is truncated(60/90 delivery aborts at 66.7% < 95%) as the new fail-loud floor.packages/producer/src/services/render/videoFrameCoverage.ts:150-154.— Rames Jusso