feat(cli): measure a composition against a reference with compare --against - #3103
Open
miguel-heygen wants to merge 5 commits into
Open
feat(cli): measure a composition against a reference with compare --against#3103miguel-heygen wants to merge 5 commits into
miguel-heygen wants to merge 5 commits into
Conversation
…gainst lint and check only ever audit a composition against its own rules, so a scene that renders nothing like the artifact it reproduces still passes both. compare --against adds the outward-looking gate: per-time SSIM (via ffmpeg's ssim filter), ink bounding-box deltas, a reference-over-replica contact sheet, a red/cyan deviation overlay, and --fail-under to exit non-zero on a measured floor. Skills now route reference-bearing briefs to it, and the entry skill says to read only the routed workflow's SKILL.md.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…st floor The documented caveat was wrong. Probing which reference frame the live seek lands on shows it lands exactly on the right one (peak SSIM at frame 210 for t=7, falling off on both sides), so there is no seek drift to work around. The real floor is the decode gap: a replica is a live browser paint, a reference is a decoded compressed video. Flat graphics self-compare at 0.998-0.999; photographic video sits near 0.93 at high quality and 0.89 at draft, and most of that is a uniform level shift rather than a structural error. Adds meanSignedDiff so that shift is a number instead of an eyeball call on the overlay, printed as 'diff X% (bias +Y%)', and replaces the caveat with the measured per-content floors.
ssimFfmpegArgs was a one-caller function returning a literal array, plus a test asserting the array contains its own inputs; the args now sit at the call site. --labels did nothing on the --against route either: the sheet rows are already labelled reference and replica, and the only other use was a progress line.
The parse mirrored the older unknown-typed style, so every field was type-tested at runtime. Citty hands string flags back as strings and boolean flags as booleans, so ReferenceCompareCliArgs states that and the helpers narrow string | undefined instead.
…ls manifest The reference-path assertion compared against a literal POSIX path, which Windows resolves to D:\tmp. It now resolves the expectation the same way the parser does. The skills manifest also missed the hyperframes-cli reference-gate edit.
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.
What
Adds
hyperframes compare --against <reference>: measure one composition against an external reference video or still, at up to 8 sampled times.Each run emits three things:
--out),<out>-overlay-NN.png): agreement grey, reference-only ink red, replica-only ink cyan,ssim,meanAbsDiff,meanSignedDiff, and ink bounding-box deltasdw/dh/dcx/dcy/scale.--fail-under <ssim>turns it into a gate (exit 1 when the worst sampled SSIM is below the threshold). Existing multi-variantcomparebehaviour is unchanged.Why
Every gate we ship is self-referential.
lintandcheckaudit a composition against its own rules, so a scene that is inverted, mis-scaled, or missing its hero element passes both under a green check. When the brief comes with a reference artifact (a video being rebuilt or recut, an approved cut, a design still, the previous accepted render), nothing in the CLI compares the build to it, and agents end up hand-rolling a one-off ffmpeg measuring rig, or eyeballing composites.How
packages/cli/src/utils/referenceDiff.ts: pure frame math (ink bounding box from a median-luma threshold, bounds deviation, mean absolute and mean signed difference, red/cyan overlay buffer, ffmpeg SSIM args + log parsing). No I/O, so it unit-tests without a browser or a built workspace.packages/cli/src/capture/compareAgainstReference.ts: orchestration. Reference frames via ffmpeg, replica frames from one browser session seeked withAUDIT_SEEK_OPTIONS(the producer bridge, the same seek targetrenderdrives), replicas normalized to reference dimensions so the sheet, the overlay and SSIM all read the same pixels.ssimfilter rather than a reimplementation. No new dependencies:sharpand ffmpeg are already required./hyperframes-cligains a "ground the gate in a reference" section,references/compare-and-batch.mdgains the full contract with a read-the-numbers guide,/remotion-to-hyperframespoints at it for mid-build iteration between full renders, and the/hyperframesrouter now says to read only the routed workflow'sSKILL.mdand none of its siblings.The floor, measured rather than assumed
A replica is a live browser paint; a reference is a decoded compressed video. That decode gap is a floor no correction goes below, and it depends on content. Compared against their own renders:
--quality high, ~0.89 atdraftmeanSignedDiffexists to make that legible: on the video-backed case, 1.6 of a 2.1% mean absolute difference is uniform bias, not structural error. The CLI prints it asdiff 2.1% (bias +1.6%).An earlier revision of this branch documented the video-backed floor as seek drift. That was wrong and is corrected here: probing which reference frame the live seek lands on shows it lands exactly on the right one (SSIM peaks at frame 210 for t=7 and falls off on both sides, 0.881 / 0.893 / 0.888). The residual is encode loss plus browser-versus-FFmpeg colour conversion.
Test plan
Unit tests cover the pure math and the argument parsing (ink bounds, bounds deviation, mean absolute and signed difference, overlay channel mapping, SSIM log parsing,
--at/--fail-undervalidation and caps).End to end, against real renders:
registry/examples/nyt-graph(graphics only) rendered at--quality high, compared against its own render at--at 0,3,7,11,14: SSIM 0.9987 to 0.9999, ink deltas all zero.Same project against an unrelated video: SSIM 0.2892,
meanAbsDiff62.5%.Gate exit codes verified directly: mismatch +
--fail-under 0.9exits 1, match +--fail-under 0.9exits 0.registry/examples/kinetic-type(video-backed) against its own high-quality render: 0.9353 / 0.9316 / 0.9301 with bias +1.6% / +1.1% / +1.0%, and against the draft render 0.8901 / 0.8930 / 0.8955. Repeat runs are bit-identical, so the number is stable.Frame-landing probe (above) run against neighbouring reference frames 200 through 220.
packages/clisuite: 2501 passed, 2 failed. Both failures are the pre-existingtranscribe.test.tscases that also fail onmain.Unit tests added/updated
Manual testing performed
Documentation updated (if applicable)