Skip to content

Capture real evidence at the HTML preview gate#5

Merged
leochenpm merged 1 commit into
sync/design-layer-refinementsfrom
sync/preview-evidence-gate
Jul 16, 2026
Merged

Capture real evidence at the HTML preview gate#5
leochenpm merged 1 commit into
sync/design-layer-refinementsfrom
sync/preview-evidence-gate

Conversation

@leochenpm

Copy link
Copy Markdown
Collaborator

Stacked on #4 (which is stacked on #3). Merge in order; GitHub retargets automatically.

Why

The HTML preview gate exists to catch design problems before paying for an mp4 render. But snapshot only ever captured --at 0 — a single opening frame. One frame cannot show a scene that reads as a generic slide, starts blank, or drops its hero visual, so the gate could approve a composition whose problems only surfaced after a full render. The gate was asking for a judgement it never supplied the evidence for.

What

Evidence (render.ts, composition-qa.ts)

  • snapshot now captures the hook frame, one midpoint per scene, and the payoff frame in a single hyperframes call — --at accepts a timestamp list, so this is one browser session, not N.
  • Returns first_frame, contact_sheet (HyperFrames writes a grid view itself — no need to build one), and labelled frames. path/bytes are unchanged for existing callers.
  • Scene times come from the existing loadSceneMap + loadCompositionMeta. If metadata can't be read, it degrades to the old single-frame capture instead of failing.
  • New pure helpers buildPreviewSamplePlan / matchPreviewFrames, bounded by PREVIEW_MAX_FRAMES = 8 (each capture is a real browser seek; hook and payoff always survive the thinning).

Discipline (skills)

  • stage-compose step 5: read the contact sheet as a design checkpoint, make one localized repair before opening the gate, show the sheet as the primary artifact.
  • frontend-design: judge every returned frame, not frame 0 alone.

Two bugs found while building this

  • The old "newest PNG wins" rule. Fine for one frame; with several timestamps the newest file is the payoff, so first_frame would have silently been the closing frame. Results are now joined on the frame-<NN>- index prefix hyperframes actually writes.
  • The payoff frame seeked past the end. duration - 0.05 rounds up to duration at hyperframes' one-decimal seek granularity (11.95 → 12.0). Caught by a unit test; times are now rounded before clamping to the last seekable tenth.

Verification

  • pnpm build + typecheck green. pnpm test 137 passed / 8 skipped (+9 new; 128 was the baseline).
  • Driven end-to-end against real hyperframes 0.7.21, not just unit tests: a real hyperframes init composition with a 3-scene map returned 5 correctly-labelled frames (hook-frame 0s, intro-mid 1s, body-mid 3s, outro-mid 5s, payoff-frame 9.9s) plus the contact sheet. Verified first_frame is byte-identical to frame-00 (the hook), and that a shorter rerun leaves no stale frames behind.
  • Filename parsing has fixtures for the real shape and for rejected look-alikes (frame-0-at-0.0s.jpg, my-frame-00-..., ...png.bak).

Scope note

The plan's PR3 bundled 7 upstream commits. Most of them (32691008a, 40d0477c1, 4ea928a7a, 173f9fed8) implement the upstream host's native composition.* state machine — durable production state, approve_plan/approve_preview/approve_draft ops, and chat-UI artifact publishing. None of that exists here and porting the prompts alone would describe gates this repo does not have. What was genuinely portable is the preview-evidence half, which needed code before the prompt could be honest — that's this PR.

The preview gate existed to catch design problems before paying for an
mp4 render, but `snapshot` only ever captured `--at 0`. Reviewing one
opening frame cannot show a scene that reads as a generic slide, starts
blank, or drops its hero visual, so the gate could approve compositions
whose problems only surfaced after a full render.

Capture the hook frame, one midpoint per scene, and the payoff frame in
a single hyperframes call (`--at` takes a timestamp list), and return
`first_frame`, `contact_sheet`, and labelled `frames`. Scene times come
from the existing scene-map loader; a composition whose metadata cannot
be read still captures the opening frame as before.

Map results by the `frame-<NN>-` index prefix that hyperframes writes,
not by mtime: with several timestamps the newest PNG is the payoff, so
the old "newest wins" rule would have silently returned the closing
frame as the first frame. Stale frames from a longer previous run are
pruned so a shorter run cannot inherit them.

Then require the critique the evidence enables: stage-compose reads the
contact sheet as a design checkpoint and makes one localized repair
before opening the gate; frontend-design judges every frame, not just
frame 0.

Hand re-mapped from the upstream Orkas video-studio agent (eed7038ac,
6fe7aedbb), translated to this repo's ovs/HyperFrames flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant