mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
* feat(skills): add brief contract — interaction modes + shared intake fields across workflows New hyperframes-core/references/brief-contract.md, the shared intake contract every creation workflow now runs its brief against: - §1 interaction mode: collaborative (default) vs autonomous, ongoing vs one-time signals, mode set once and carried forward, and a gate taxonomy (preference / checkpoint / quality / routing) — autonomous skips waiting, never verification - §2 field registry: destination→aspect derivation (feed → 1:1, Shorts/TikTok → 9:16, else 16:9), message, angle, length, audience, language, narration — each workflow binds fields as ask or state - §3 question rules: one round with one question per asked field (native question UI mandatory when available, recommended option first with a receipt), never drop a question as inferable, and a mode legend advertised in the intro text instead of asked Wired into the surfaces: - hyperframes router: detect mode at entry, derive aspect from destination instead of stating 16:9 - product-launch-video / pr-to-video / faceless-explainer: ask/state binding tables at Step 0; Step 3/6 checkpoint-gate branches (autonomous posts a heads-up with a preview hint before render) - website-to-video: local mode definition now defers to the contract - music-to-video, general-video, embedded-captions, talking-head-recut, slideshow, motion-graphics: mode semantics wired per gate type - storyboard-format: new optional 'mode' frontmatter key - pr-to-video: length tier is a ceiling, not a floor — a one-headline PR recommends inside the 30–90s sweet spot regardless of diff size * feat(skills): story spine + mode-first brief across creation workflows Story — the reverse-iceberg feedback: - New hyperframes-creative/references/story-spine.md, three rules for the narrated workflows: the hook speaks the viewer's outcome language, the value claim lands by beat 2 (implementation is the footnote of the story, not the spine), and the storyboard is presented as a proposal — 'This video tells [audience] that [message]' plus a per-frame why: drawn from narrativeRole - pr-to-video: feature-reveal reordered promise-first (impact leads, diff/mechanism follow as evidence); hooks ban file/function names; fix-explainer, refactor-walkthrough, changelog unchanged - product-launch-video / faceless-explainer hook rules aligned to the spine; website-to-video's beat summary gains the echo line + why:; general-video points at the spine from its plan step Brief — hardened after live-test drift: - Mode is now the first question (Collaborative recommended vs Autonomous), its own round, skipped when the request carries a signal; autonomous asks nothing further until one final preview-or-render question before render - Step 0 rewritten as a literal two-round question script in each shot-sequence workflow (website-to-video's editorial register, channel-agnostic); brief-contract.md §3 reduced to invariants so the procedure lives in exactly one place * feat(skills): split type minimums by viewing context typography.md: full-screen viewing keeps body 20px / headline 60px; in-feed destinations (X / LinkedIn / Instagram — brief-contract's destination field) scale to body >=32px, headline >=90px, data labels >=24px. First-pass values, to be calibrated against real renders. * feat(skills): storyboard proposal as a table + credits close by default - story-spine § 3: the proposal presents frames as a markdown table (frame · beat · on screen · why) instead of dense paragraphs; the three shot-sequence workflows and website-to-video's beat summary reference the same shape - pr-to-video: the credits close is now the default ending — every PR video ends on a contributors frame (committers by commit count, 1-6 avatars), with no taste judgment; the only skip is when no avatar was fetched, and the user can cut the frame in the proposal * fix(skills): address review nits on the brief/story contracts - embedded-captions: the identity procedure now states both sides of the preference gate inline (user picks; autonomous picks with a stated why) - website-to-video step-2-brief: note that its mode section is the workflow's application of brief-contract.md, not a second definition - brief-contract: resuming a project reads mode from STORYBOARD.md frontmatter — a recorded mode counts as set, closing the write-only gap * docs(skills): add a non-code receipts example to the brief contract Review nit (jrusso1020, #2058): the receipts example in § 3 was PR-video-shaped only. A destination-shaped example joins it so the rule reads as workflow-neutral.
215 lines
26 KiB
Markdown
215 lines
26 KiB
Markdown
# Story design — PR → narrative
|
||
|
||
Use this reference in Step 3 to write `STORYBOARD.md` and `SCRIPT.md` for a **PR-to-video** — a code change (the diff, commits, files, +/− stats, and the people behind it) turned into an explainer. There is **no website and no captured assets**; the PR was ingested into `capture/extracted/` in Step 1.
|
||
|
||
This file defines the story: what the video explains, in what order, and why each frame exists. It does not define layout, effects, animation, or file syntax. For exact storyboard syntax follow `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/script-format.md`.
|
||
|
||
## Read first
|
||
|
||
1. `hyperframes.json` — locked brief: angle (archetype), audience, length, aspect, language.
|
||
2. `frame.md` — tone, type, design system (the shipped preset is **claude**: warm editorial, a serif that thinks, scarce coral, a navy code surface).
|
||
3. `capture/extracted/visible-text.txt` — the assembled PR brief: title, meta (`base ← head · +N/−M across F files`), people, body, commits, changed files, and a budget-bounded set of **representative diff hunks**. This is your source of **information**.
|
||
4. `capture/diff.patch` — the full unified diff, for deeper hunk selection than the brief's excerpt.
|
||
5. `capture/extracted/people.json` — contributors (author / committers / reviewers / commenters), bot-filtered, each with an avatar in `assets/<login>.png` (for the credits close).
|
||
|
||
## Output
|
||
|
||
- `STORYBOARD.md` — the explanation plan, one frame per beat.
|
||
- `SCRIPT.md` — the locked narration, only for spoken frames.
|
||
|
||
Every frame includes the required storyboard-format fields plus the narrative metadata below.
|
||
|
||
## Core rule
|
||
|
||
A diff is a list of edits. A video is a guided act of understanding.
|
||
|
||
Do **not** narrate the diff file-by-file or read the PR description aloud — that is the single most common failure. **Explain the change** — and where the change has a runtime behavior, **show that behavior in motion** (a `mechanism` beat — see "Show the behavior" below), don't just display the lines that changed. Reorder, merge, omit, compress: surface the one change that matters and drop the incidental churn (lockfile bumps, formatting, generated files) unless it _is_ the story. Scene order comes from narrative design, not from the diff's file order or the commit list.
|
||
|
||
**Value before evidence** (`../hyperframes-creative/references/story-spine.md`): the viewer-facing payoff — what the change unlocks, fixes, or speeds up — lands by the second beat; the diff and the mechanism are the **evidence** for that claim, never the opening. Implementation is the footnote of the story, not the spine.
|
||
|
||
Default to a **plain, technical, unhurried developer voice** — accurate, specific, no hype, no marketing gloss. You are explaining a real change to engineers; respect their time and intelligence. `frame.md` (claude) tunes the voice toward considered and literary; it does not change the structure.
|
||
|
||
## PR archetypes
|
||
|
||
Choose **one** archetype (or name a compound). Each is a complete path through understanding a change — do not splice phases from different archetypes.
|
||
|
||
- **Changelog** — "here's what shipped." Hook naming the headline → **2–4 roughly co-equal change items** → ship/wrap. Best for release PRs, multi-change PRs, "what's new in vN." Items are parallel → `cut` / `push-slide` between them. Rule-of-three is strongest when changes compress. An item with a visible behavior can be a `mechanism` mini-demo instead of a bare `diff`.
|
||
- **Feature-reveal** — "here's what you can do now." Hook (the outcome the feature unlocks, in user language) → the payoff made concrete (`impact` — what now works) → name it (`change`) → the new code typing on (`diff`) → **animate what it does (`mechanism`)** → close (a callback to the promise). Best for a PR that adds **one notable feature**. The promise leads and the code proves it: `diff` and `mechanism` are the evidence for the opening claim — the viewer should already care before the first line of code appears.
|
||
- **Fix-explainer** — "this was broken; here's the fix." Symptom/bug (`problem`) → **animate the broken behavior (`mechanism`)** → the fix as a before→after (`diff`) → **the behavior now working (`mechanism`)** or the result (`impact`). Best for bugfix PRs. Seeing the bug _happen_ and then _not_ happen is the turn — a stronger shape (tension → turn → relief) than the diff alone.
|
||
- **Refactor-walkthrough** — "same behavior, better shape." Hook (the smell / the why) → old shape vs new shape (`before_after`) → **the structure untangling, same inputs → same outputs (`mechanism`)** → payoff (`evidence` — lines removed, perf delta, files touched). Best for refactors, perf, cleanups, migrations. A `mechanism` animation _proves_ "same behavior, better shape" far better than asserting it.
|
||
|
||
**Choosing:** one notable new capability → feature-reveal; a bug fix → fix-explainer; a behavior-preserving cleanup/perf/migration → refactor-walkthrough; many co-equal changes / a release → changelog. Tie-breakers: a feature that also fixes a bug → feature-reveal with the fix as one body beat; a fix that needed a small refactor → fix-explainer (the fix is the headline). **Compound:** write `arc` as `"<outer> with <inner>"`, e.g. `"feature-reveal with changelog"`. Outer = the macro arc the viewer rides; inner = the body rhythm.
|
||
|
||
## PR-native frame types
|
||
|
||
Set each frame's `type` to one of these PR-native values. (The storyboard parser keeps `type` verbatim; it is a narrative + pacing label, not a hard enum.) Each maps to a claude frame treatment and a typical visual — so the type, the design, and the visual stay aligned end to end. Note `mechanism` is the **show-the-behavior** beat (an invented animated diagram), distinct from `diff` (show the code).
|
||
|
||
| `type` | The frame's job | claude treatment (frame.md) | typical visual (see code-vocabulary.md) |
|
||
| -------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
||
| `hook` | The high-leverage opening 3–5s | Cover | — (or `code-3d-extrude` for a hero code moment) |
|
||
| `problem` | The bug / smell / pain / why-care the PR resolves | Statement or Pull-quote | `code-highlight` (spotlight the offending line) |
|
||
| `change` | Name the change / the feature / the PR itself | Statement or Cover | — |
|
||
| `diff` | The change body — a before→after, a hunk, new code typed on | **Code Surface** (navy) | `code-diff` / `code-morph` / `code-typing` |
|
||
| `before_after` | Explicit old-shape vs new-shape comparison (refactor/fix) | Code Surface (split / morph) | `code-morph` / `code-diff` |
|
||
| `mechanism` | **Show what the change DOES at runtime** — the request retrying, the cache filling, serial→parallel, the race resolved | invented diagram on cream (hairline ink + one coral active marker) | **invented SVG/GSAP**; `flowchart` / `flowchart-vertical` / `data-chart` where they fit |
|
||
| `impact` | The payoff — what now works, what's now possible; opens the video as the promise (feature-reveal) or lands it as the result | Number / Impact | `number-lockup` (no code block needed) |
|
||
| `evidence` | Concrete grounding — `+N/−M`, a passing test, a benchmark | Number / Impact | `code-diff` red→green / `number-lockup` |
|
||
| `credits` | Shipped-by close — the humans behind the change | Closing | — (avatar row from `assets/<login>.png`) |
|
||
| `cta` | The closing ask — pull it, upgrade, read the PR | Closing | — (coral-callout) |
|
||
|
||
The body of a PR video **alternates `diff` (show the code that changed) with `mechanism` (show what it does at runtime)**, landing on `impact` / `evidence` (the result). A body that is all `diff` reads as code show-and-tell — the `mechanism` beat is what makes the change _legible_ and is the usual cure for a video that feels flat. Every PR has a change, so at least one `diff` (or `change`) frame always exists; most PRs also have a behavior worth animating.
|
||
|
||
## Hook strategy
|
||
|
||
The hook is the highest-leverage 3–5 seconds. Pick one:
|
||
|
||
| Strategy | When | Example |
|
||
| ---------------------- | ----------------------------------- | ---------------------------------------------------------------------- |
|
||
| Shocking statistic | The change quantifies the stakes | "This PR deletes 1,200 lines." / "40% faster cold starts." |
|
||
| Counterintuitive claim | The change contradicts intuition | "We made the client slower — and that fixed it." |
|
||
| Pain validation | The audience already feels the bug | "Every deploy, the same flaky timeout." |
|
||
| Concept announcement | The change has a name worth landing | "Meet retry-with-backoff — flaky networks stop killing your requests." |
|
||
| Before/after teaser | The diff is the whole story | "One line threw. Now it recovers." |
|
||
| Stakes / consequence | The "why care now" is a real cost | "This crash hit every user on a flaky network." |
|
||
| Direct address | The audience is clearly defined | "If you've ever waited on a 5-minute CI run…" |
|
||
|
||
Do not open with a generic repo/company description. Whatever the strategy, the hook speaks the **viewer's outcome language** (story-spine rule 1) — never file / function / identifier names; numbers only when they carry stakes ("40% faster"), not inventory ("23 files changed").
|
||
|
||
## Clarity / rhetoric technique catalog
|
||
|
||
Each frame's `persuasion` is a **named** technique, not "explain the change." Combine when several are active:
|
||
|
||
- **Make-concrete** — Worked example (one real request/input) · Analogy (backoff as "knock, wait longer, knock again") · Concretization (abstract change → one tangible code line)
|
||
- **Reveal-in-order** — Progressive disclosure (the diff one line at a time) · Build-up (the simple call, then the edge case) · Signposting ("before… after…")
|
||
- **Contrast** — Before/after diff · Old shape vs new shape · The bug vs the fix · Two approaches compared
|
||
- **Structure** — Rule of three (three changes) · Numbered enumeration · Question→answer · Frame-then-fill (state the shape, then the code)
|
||
- **Evidence** — `+N/−M` stat · Passing test / green check · Benchmark / perf delta · Causal chain (request → 5xx → retry → success)
|
||
- **Memory & landing** — Callback (return to the hook's bug) · Distillation (the change in one line) · Generalization (this fix → the principle)
|
||
|
||
## Emotional beats
|
||
|
||
`beat` is one word or a short compound (e.g. "Recognition and relief"). Avoid generic "positive". A PR video rides a comprehension arc:
|
||
|
||
- **Negative valley** — _open the gap_ (`hook`/`problem`): curiosity · frustration · recognition · concern · "ugh, that bug"
|
||
- **Pivot** — _orient_ (`change`): clarity · orientation · anticipation · focus
|
||
- **Build** — _build understanding_ (`diff`/`before_after`/`impact`/`evidence`): comprehension · "aha" · confidence · momentum · conviction · relief (for a fix)
|
||
- **Resolution** — _land_ (`credits`/`cta`): satisfaction · resolve · "ship it" · inevitability
|
||
|
||
Compound beats are often strongest: "Recognition _and_ relief" (a fix), "Curiosity _and_ confidence" (a feature).
|
||
|
||
## The body is a sequence
|
||
|
||
A PR video's core is **2–5 body frames**, each advancing one change / one before→after / one item, building cumulatively. **Alternate `diff` (the code) with `mechanism` (the behavior)** — don't stack code surfaces:
|
||
|
||
- **changelog:** a `diff` (or a `mechanism` mini-demo) per change item; parallel → default `cut` / `push-slide`.
|
||
- **feature-reveal:** `impact` (the promise, concrete) → `change` (name it) → `diff` (the code, often typing/morphing on) → `mechanism` (animate it working) → a closing callback to the promise.
|
||
- **fix-explainer:** `problem` (symptom) → `mechanism` (the bug happening) → `diff` (cause + fix, before→after) → `impact` (result, or a `mechanism` of it working).
|
||
- **refactor-walkthrough:** `before_after` structure → `mechanism` (the structure untangling, behavior preserved) → an `evidence` numbers beat.
|
||
|
||
## Continuity across frames
|
||
|
||
This framework builds **one frame per worker** — there is no multi-frame "continue run." A sequence reads as one continuous shot through two storyboard-level levers, both yours:
|
||
|
||
1. **A consistent stage** — consecutive body frames share one composition idea (the same navy code window filling in, the same before|after split, the same counter advancing), stated in each frame's `scene` so Step 4 and the workers keep the stage stable.
|
||
2. **A consistent transition** — pick one seam type for a run (`crossfade` for a soft code reveal, `push-slide` for the next change item) and repeat it.
|
||
|
||
When a single element genuinely _transforms_ between two ideas (the failing test flips green, the old function becomes the new one), keep it **within one frame** as a development beat (entrance → transform → settle) — the worker owns that motion (a `code-diff` or `code-morph` block). Note the intent in `scene` / narrative; Step 4 wraps it in a time-coded shot sequence around the block (a `code-diff` / `code-morph`).
|
||
|
||
## Transitions
|
||
|
||
Use only registry transition names in `transition_in`:
|
||
|
||
`cut | crossfade | blur-crossfade | push-slide LEFT | push-slide RIGHT | push-slide UP | push-slide DOWN | zoom-through | squeeze`
|
||
|
||
Pick 2–3 for the whole video and repeat. Frame 1 is `cut` (no previous frame). Match the seam to the narrative: ordered change items → a consistent `push-slide`; a soft reveal / into-the-cause → `crossfade` / `blur-crossfade`; zooming into a code line or pulling back to the file tree → `zoom-through`; a clean new change item → `cut`.
|
||
|
||
## The diff is the centerpiece
|
||
|
||
Code beats live on the **navy code surface** (claude's Code Surface treatment) — but the body is **not** all code (pair them with `mechanism` beats, next section). Plan the code beats deliberately:
|
||
|
||
- **Feature 2–4 real diff hunks**, named in each frame's `scene` — each a small, legible snippet (~4–12 lines), **never a whole file**. Pull them from `capture/diff.patch` / the brief's "Representative diff."
|
||
- Name **which code animation block** the frame wants in `scene` (the Step-4 visual phase and the worker read it). See `code-vocabulary.md` for the full map; the short version: before→after = `code-diff`; refactor/rename continuity = `code-morph`; new code written on = `code-typing`; spotlight one line = `code-highlight`; walk a long file = `code-scroll`; a hero reveal = `code-3d-extrude` / `code-particle-assemble`.
|
||
- Numbers (`+1,204 / −318`, files touched, perf delta) belong on an `impact` / `evidence` frame as a `number-lockup`, **not** read aloud in narration.
|
||
|
||
## Show the behavior — the mechanism beat (not just the diff)
|
||
|
||
A diff shows **what changed in the code**. It does **not** show **what the change does** — and "what it does" is usually the more memorable, more explanatory beat. The single biggest reason a PR video feels flat is that every body frame is a code surface or a number: it _tells_ (here are the lines, here is the stat) but never _shows_ (here is the request actually recovering).
|
||
|
||
A **`mechanism` frame animates the runtime behavior** the PR changes — built as an **invented animated diagram** (SVG / HTML / GSAP on claude's cream ground: hairline-ink nodes / edges / lanes, one coral marker on the active or changed element), where **the build _is_ the teaching** — each part appears on beat, the flow plays out across the shot. It is **not** a code block and **not** a headline. Reach for the `flowchart` / `flowchart-vertical` / `data-chart` registry blocks where they fit; otherwise invent it (visual-design.md's diagram / abstract-graphics register).
|
||
|
||
Plan **at least one `mechanism` beat** for any PR with a visible runtime behavior (most feature and fix PRs have one). What to animate, by what the change touches:
|
||
|
||
| The change touches… | Animate (the behavior, not the code) |
|
||
| ------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||
| Retry / backoff / resilience | a request lifecycle: fire → 500 → wait (delay growing) → retry → 200 |
|
||
| Caching / memoization | two lanes racing: cold (slow, hits the DB) vs cached (fast, hits the cache) |
|
||
| Concurrency / parallelism | a serial single lane reshaping into parallel lanes |
|
||
| Race / ordering bug | the broken behavior first (items dropped, two writers colliding), then the fixed flow |
|
||
| Performance | two timelines / bars racing, the new one finishing first (a `data-chart` fits) |
|
||
| Refactor / migration | a tangled call-graph untangling into a clean one; same inputs → same outputs |
|
||
| New endpoint / pipeline / state | data flowing through the new path; a state machine lighting up step by step (`flowchart-vertical`) |
|
||
|
||
Name the mechanism in the frame's `scene` ("animate the request retrying: fire → 500 → backoff → 200, invented SVG flow") so Step 4 and the worker build it. The `diff` frame and the `mechanism` frame are **complementary** — the diff is the proof in code, the mechanism is the proof in motion; alternate them rather than stacking code surfaces.
|
||
|
||
## The close: a credits / shipped-by scene
|
||
|
||
A PR is shipped by people, and every PR video closes with a `credits` frame naming them. `capture/extracted/people.json` lists real contributors (bot-filtered), and Step 1 downloaded each avatar to `assets/<login>.png` (the `avatarFetched: true` entries — confirm with `ls assets/`). `reviewDecision` (e.g. `APPROVED`) is honest grounding.
|
||
|
||
> **The PR `author` only opened the PR — not necessarily who wrote the code.** A teammate often authors most commits. Lead the credits with `committer`s by `commitCount`, not the opener.
|
||
|
||
The `credits` frame is an avatar row with names + roles + an "approved" check. On that frame only, set `asset_candidates` to 1–6 entries of `assets/<login>.png — <login>, <role>` (commit authors by `commitCount` first, then reviewers; only `avatarFetched: true` logins). The body stays code-only — avatars appear **only** on this close, never decorating a diff frame. The frame sits in the Step 3 proposal like any other, so the user can cut it there; skip it yourself only when no avatar was fetched.
|
||
|
||
Every other frame has **no** `asset_candidates` (the visuals are invented downstream from `scene` + the diff).
|
||
|
||
## Per-frame length budget — ≤ 9 s, word count is the real measurement
|
||
|
||
The largest quality bug in PR videos is **scripts that talk too long**. TTS runs at **~2.2 words/second**, so a 45-word "7-second" script is really 20 seconds, and the visual phase has to pad the tail with idle drift (the video reads as "shimmering"). Budget by word count:
|
||
|
||
| Bound | Words (@2.2 wps) | Duration | When |
|
||
| -------------------------- | ---------------- | ------------ | ----------------------------------------------------------------------------- |
|
||
| **Soft target — default** | **≤ 19** | **≤ 9 s** | Every frame aims here; the cut stays alive. |
|
||
| **Exception — ≤ 2 frames** | ≤ 26 | ≤ 12 s | The main `diff` (the one change you must explain) or a causal-chain `change`. |
|
||
| **Hard cap** | > 26 | > 12 s | Trim or split. |
|
||
| **Whole-film target** | ≤ ~400 | up to ~3 min | Sweet spot ~30–90 s (≤ ~155 words); the body carries the load. |
|
||
|
||
Estimate while writing: `duration ≈ ceil(word_count / 2.2)`. 29 words → 13s (trim); 17 words → 8s; 12 words → 6s. Trim techniques: cut the lead-in clause ("Until now, the agent shipped…" → "The agent shipped…"); move numbers off-script onto a counter; split only when the halves carry distinct beats (cause then effect). **Silent frames are allowed and common** — a diff typing on, a before→after morph, a counter running. Set `voiceover` empty, omit from `SCRIPT.md`, and make `narrativeRole` carry it. A complex change does not need a long script; it needs a careful one — if you can't headline the change in 19 words, the headline isn't sharp yet.
|
||
|
||
**Write each line as discrete cues, not one run-on breath.** Step 5 reveals each on-screen piece _when the voiceover names it_ (the anti-PowerPoint mechanism). A line with clear phrase boundaries — "Three retries — then it backs off — then it gives up clean" — hands the shot its reveal cadence for free; a single long clause leaves the frame nothing to pace to.
|
||
|
||
## Frame template
|
||
|
||
```md
|
||
## Frame N — Short name
|
||
|
||
- scene: one clear visual idea — name the hunk/file + the code-\* block ("the request() retry block, ~6 lines, code-diff")
|
||
- voiceover: "spoken guide text, or empty"
|
||
- duration: ceil(word_count / 2.2) seconds
|
||
- transition_in: crossfade
|
||
- status: outline
|
||
- src: compositions/frames/NN-short-name.html
|
||
- type: diff
|
||
- persuasion: Before/after contrast
|
||
- beat: comprehension
|
||
- blueprint: <candidate id from the role→blueprint menu, or omit — a code beat usually omits it (the code-\* block is the shape)>
|
||
|
||
narrativeRole: What this frame does in the viewer's understanding (its job, not what's on screen).
|
||
keyMessage: The one thing the viewer should understand after this frame (one sentence).
|
||
```
|
||
|
||
The `credits` frame additionally carries an `asset_candidates:` line (see the credits section); no other frame does.
|
||
|
||
## Final checklist
|
||
|
||
- One archetype is named (compound only when explicit); the sequence is narrative-driven, not diff-order-driven.
|
||
- The opening uses a named hook strategy; you do not read the PR description aloud.
|
||
- The hook is in viewer-outcome language (no file / function / identifier names), and the video's `message` lands by beat 2 (story-spine).
|
||
- Each frame has one job; the body builds cumulatively, **alternating `diff` (the code) with `mechanism` (the behavior)** + `impact` / `evidence` — not a single isolated body frame, and not an unbroken stack of code surfaces.
|
||
- Every frame has `type` (PR-native), `persuasion` (a named technique), and `beat` (specific). The emotional arc matches the archetype (fix = frustration → relief; feature = curiosity → confidence).
|
||
- Each `voiceover` is phrase-segmented into cues (each a piece Step 5 can reveal on), not one run-on clause; a candidate `blueprint:` is tagged from the role→blueprint menu where a proven shape fits (a code beat usually omits it — the `code-*` block is the shape).
|
||
- **2–4 real diff hunks** featured, each a small legible snippet (not a whole file), each naming its `code-*` block in `scene`.
|
||
- **At least one `mechanism` beat** animates what the change _does_ at runtime (an invented diagram, or a `flowchart` / `data-chart`), named in its `scene` — unless the PR genuinely has no visible behavior (a pure docs / config bump). The body is not an unbroken run of code surfaces.
|
||
- Transitions use only registry names and repeat 2–3 types; frame 1 is `cut`.
|
||
- The video closes with a `credits` frame (skipped only when no avatar was fetched); `asset_candidates` is absent on every other frame (1–6 `assets/<login>.png` entries on the close, `avatarFetched: true` only).
|
||
- Each `script` fits the budget — ≤ 19 words / ≤ 9 s default, ≤ 2 frames at the ≤ 26 / ≤ 12 s exception; `duration = ceil(word_count / 2.2)`, not a guess.
|
||
- `SCRIPT.md` contains only locked spoken narration; silent frames are intentional and omitted from it.
|