refactor(skills): keep hyperframes-core as the HTML contract (#3928)

* refactor(skills): keep hyperframes-core as the HTML contract

Move brief, storyboard, review, production, dispatch, and
frame-worker docs to hyperframes. Slim remaining core references.

* fix(skills): restore camera recipes and root sizing

Restore Zoom, Ken Burns, crop, and clip-path recipes. #root is 100 percent.

* fix(skills): stamp size on the composition root

Runtime sizes the composition root, not html/body. Overlap pin requires is valid.
This commit is contained in:
Miguel Ángel
2026-09-13 21:20:58 -04:00
committed by GitHub
parent e30992fdb8
commit 95bea1631f
58 changed files with 159 additions and 312 deletions
@@ -27,7 +27,7 @@ describe("hyperframes-core contract docs", () => {
it("teaches check as the canonical quality gate", () => {
const skill = read("skills", "hyperframes-core", "SKILL.md");
const brief = read("skills", "hyperframes-core", "references", "brief-contract.md");
const brief = read("skills", "hyperframes", "references", "brief-contract.md");
expect(skill).toContain("`npx hyperframes check`");
expect(brief).toContain("`hyperframes check`");
@@ -99,7 +99,7 @@ audience: <who it's for>
Add one \`## Frame N\` section per beat. Keep the arc tight.
Then run the review loop from the hyperframes-core skill (references/review-loop.md): present the plan as a proposal, offer wireframe sketches on this board, and build on the confirmed layouts.`;
Then run the review loop from the hyperframes skill (references/review-loop.md): present the plan as a proposal, offer wireframe sketches on this board, and build on the confirmed layouts.`;
}
function EmptyState({ path }: { path: string }) {
@@ -6,7 +6,7 @@ import type { FrameStatus } from "@hyperframes/core/storyboard";
* The board's per-frame comment boxes write one batch file on submit; the
* consuming agent revises exactly the frames named, deletes the file, and
* re-presents. The file shape is a cross-repo contract — keep it in sync with
* skills/hyperframes-core/references/storyboard-format.md § Frame comments.
* skills/hyperframes/references/storyboard-format.md § Frame comments.
*/
export const FRAME_COMMENTS_PATH = ".hyperframes/frame-comments.json";
@@ -241,7 +241,7 @@ test("creator editing recipes are copyable, owned, mathematical, and limitation-
/final-source[\s\S]{0,100}subcomp[\s\S]{0,100}visual pose/i,
/arbitrary mid-source[\s\S]{0,120}preprocess/i,
/distinct tracks[\s\S]{0,120}overlap[\s\S]{0,120}opposing/i,
/same-track overlap[\s\S]{0,80}invalid/i,
/same-track overlap is valid/i,
/inner wrapper[\s\S]{0,100}not[\s\S]{0,50}(clip element|timed clip)/i,
/source cuts[\s\S]{0,80}hyperframes-core/i,
],
+16 -16
View File
@@ -2,11 +2,11 @@
"source": "heygen-com/hyperframes",
"skills": {
"embedded-captions": {
"hash": "57a5caaa35ff79c8",
"hash": "4023b0b051ba1e75",
"files": 142
},
"faceless-explainer": {
"hash": "3f84e9cf9c2468ca",
"hash": "7ff529b85f588ffc",
"files": 24
},
"figma": {
@@ -14,12 +14,12 @@
"files": 2
},
"general-video": {
"hash": "a7e50541d5f01d98",
"hash": "ab15530f09bec21c",
"files": 4
},
"hyperframes": {
"hash": "e77a36c488b4f80c",
"files": 17
"hash": "d0a114a8f894758e",
"files": 26
},
"hyperframes-animation": {
"hash": "3947516ec450f3aa",
@@ -30,15 +30,15 @@
"files": 7
},
"hyperframes-cli": {
"hash": "06b31056db460774",
"hash": "f1593f498b988a0c",
"files": 11
},
"hyperframes-core": {
"hash": "e15156c04011c4eb",
"files": 20
"hash": "d9c1fae7818d5ba8",
"files": 11
},
"hyperframes-creative": {
"hash": "f3b201ca29eae92a",
"hash": "7b6b7a78102da71b",
"files": 78
},
"hyperframes-keyframes": {
@@ -50,23 +50,23 @@
"files": 12
},
"media-use": {
"hash": "28948e640a5d133a",
"hash": "08a544dfaca9e846",
"files": 158
},
"motion-graphics": {
"hash": "de8ec5464028ec67",
"hash": "32641ae2b94c4a8f",
"files": 23
},
"music-to-video": {
"hash": "be70b499dd72169b",
"hash": "82039f8aa152a904",
"files": 132
},
"pr-to-video": {
"hash": "b93575a96d213ff5",
"hash": "bbb3db51176b7e44",
"files": 30
},
"product-launch-video": {
"hash": "b1597ed153cafcc7",
"hash": "d87f7ae140e88403",
"files": 30
},
"remotion-to-hyperframes": {
@@ -74,11 +74,11 @@
"files": 70
},
"slideshow": {
"hash": "3d03ada897faebd2",
"hash": "71174bff2d869f18",
"files": 2
},
"talking-head-recut": {
"hash": "50d1f9c928514826",
"hash": "7018bf017e8606d2",
"files": 28
}
}
+1 -1
View File
@@ -57,7 +57,7 @@ Rail-surface identities build exactly this (rail = `rail.html`, embed = the clim
**One front-end, three engines behind.** The user picks an IDENTITY from [CATALOG.md](CATALOG.md) (35 entries: 10 classic + 25 themed); the engine, compiler and authoring file are derived by lookup from the catalog row. **Never surface "Standard vs Cinematic vs Theme" as a question** — those are backend names (a product has one UX even with several engines). The catalog encodes everything routing needs: reading surface, voice, recommend-for, scene needs, adjacency notes for the genuinely-close pairs (loud↔ordnance, neon↔neonsign, cream↔stardust).
The identity pick is a **preference gate** (`../hyperframes-core/references/brief-contract.md` § 1): in autonomous mode ("surprise me" / "decide for me"), pick from your shortlist yourself and state the one-line why instead of asking.
The identity pick is a **preference gate** (`../hyperframes/references/brief-contract.md` § 1): in autonomous mode ("surprise me" / "decide for me"), pick from your shortlist yourself and state the one-line why instead of asking.
Procedure: probe the clip → shortlist 2–3 identities from the catalog → recommend ONE with a one-line why → **the user picks** (autonomous mode: you pick, stating the why) → author that identity's file. Identities are engine-locked (no cross combos; opening one is a validation event — see dna/README.md).
+14 -14
View File
@@ -13,7 +13,7 @@ Use this skill to turn a body of text into an explainer video: pick a design sys
> **The front door is `/hyperframes`.** You are the orchestrator. Run each step, verify its gate, and only then continue. This skill is for **explaining a topic from text, with no product and no website to capture**. Any other intent, a bare "make a video", or any uncertainty → read `/hyperframes` first — the intent layer owns every route decision, and a fresh creation arriving here without `BRIEF.md` goes through it anyway (Setup's opening rule).
You are the orchestrator. Work in `videos/<project>/`. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes-core/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch one sub-agent per frame. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
You are the orchestrator. Work in `videos/<project>/`. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch one sub-agent per frame. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
Workflow: Step 0 setup → `hyperframes.json`; Step 1 brief → `capture/extracted/`; Step 2 design system → `frame.md`; Step 3 storyboard/script → `STORYBOARD.md` and `SCRIPT.md`; Step 3.1 audio → `audio_meta.json`; Step 4 visual design → enriched `STORYBOARD.md`; Step 5 frames → `compositions/frames/NN-*.html` and `index.html`; Step 6 final render → `renders/video.mp4`.
@@ -31,7 +31,7 @@ Initialize only if `hyperframes.json` is missing. Name `<project>` from the topi
After init, let `<PROJECT_ROOT>` be `videos/<project>` and run every subsequent relative-path command with that directory as its working directory. In the commands below, `.` means `<PROJECT_ROOT>`; never write `.media`, `capture`, or output files in the caller directory.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes-core/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Show sign-in status before proceeding past Setup** — run `npx hyperframes auth status` and relay its output verbatim. It reports whether voice/BGM will use HeyGen or local engines and, when signed out, how to sign in. Apply one branch:
@@ -83,11 +83,11 @@ A faceless explainer usually has **no brand colors/fonts** (`tokens.json` colors
Goal: Turn the text into an approved frame-by-frame teaching plan.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes-core/references/storyboard-format.md`, and `../hyperframes-core/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes/references/storyboard-format.md`, and `../hyperframes/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Use `story-design.md` for the explainer structure (concept / how-to / listicle / story), hook strategy, clarity techniques, emotional beats, the type-enum mapping, and `VO_MODE`. The video's sequence comes from **narrative design, not the input text's paragraph order** — reorder, merge, omit, compress. As a **soft guide**, consult the role→blueprint menu in `../hyperframes-animation/blueprints-index.md`: for each beat, write the voiceover in the shape its candidate blueprint implies and tag that candidate `blueprint:` id when one fits. Teaching truth still decides which beats exist — never force a beat to fit a blueprint, and never invent a beat just because a proven shape is available. Faceless visuals are invented downstream, so frames do **not** carry an asset inventory: leave `asset_candidates` empty unless the user supplied a real `public/<basename>` image. Use the exact required fields from the storyboard and script references.
After drafting, run the review loop's plan pass — `../hyperframes-core/references/review-loop.md` § 1: open the board (don't ask whether to), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
After drafting, run the review loop's plan pass — `../hyperframes/references/review-loop.md` § 1: open the board (don't ask whether to), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
**Gate:** `STORYBOARD.md` exists, every frame has the required narrative fields, `SCRIPT.md` exists when narration is needed, and the user approved the frame-by-frame plan (autonomous: the summary was posted as a heads-up).
@@ -117,7 +117,7 @@ If there is no narration and no `SCRIPT.md`, skip voice generation. BGM may stil
Goal: Add the visual direction, layout intent, and motion choices to each storyboard frame.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes-core/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
Edit `STORYBOARD.md` in place. Do not create another storyboard. Use `frame.md` as source of truth for color, type, layout feel, and style.
@@ -145,15 +145,15 @@ Wait for Step 3.1 audio to finish if audio was started. Then sync durations and
Duration sync is mechanical: real voice duration wins; silent frames keep estimates; never hand-edit synced durations.
Before dispatch, read `../hyperframes-core/references/subagent-dispatch.md`. Build the per-frame packets and the worker role payload:
Before dispatch, read `../hyperframes/references/subagent-dispatch.md`. Build the per-frame packets and the worker role payload:
`node <SKILL_DIR>/scripts/frame-packets.mjs --project "$PROJECT_DIR" --storyboard "$PROJECT_DIR/STORYBOARD.md"`
The builder writes one bounded packet per frame under `.hyperframes/frame-packets/` (the frame's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes-core/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch one sub-agent per frame, in parallel if possible; otherwise run workers in waves. Each worker gets exactly one frame: its prompt carries `_role.md` and that frame's packet — paste both in full, or hand the two file paths for the worker to read first (equivalent; the worker starts from exactly those two documents either way) — plus a dispatch context with `PROJECT_DIR`, `frame_id`, whether the frame has a **confirmed sketch** on disk (the worker dresses that layout rather than redrawing it — frame-worker core § When a confirmed sketch exists), canvas size, and caption status + keep-out band if captions are enabled.
The builder writes one bounded packet per frame under `.hyperframes/frame-packets/` (the frame's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch one sub-agent per frame, in parallel if possible; otherwise run workers in waves. Each worker gets exactly one frame: its prompt carries `_role.md` and that frame's packet — paste both in full, or hand the two file paths for the worker to read first (equivalent; the worker starts from exactly those two documents either way) — plus a dispatch context with `PROJECT_DIR`, `frame_id`, whether the frame has a **confirmed sketch** on disk (the worker dresses that layout rather than redrawing it — frame-worker core § When a confirmed sketch exists), canvas size, and caption status + keep-out band if captions are enabled.
Workers read only their packet and `frame.md`; they never open `STORYBOARD.md` or the skill documents (the packet inlines what was selected upstream). Each worker writes only `compositions/frames/NN-*.html`. Workers must never edit `STORYBOARD.md`.
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes-core/references/frame-worker-core.md`.)
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes/references/frame-worker-core.md`.)
As each worker returns, the orchestrator marks that frame as `animated` in `STORYBOARD.md`.
@@ -191,7 +191,7 @@ If a command fails, surface stderr and stop — don't pile on recovery commands.
**Known false-positive — do not chase it.** `check` may report a handful of `text_box_overflow` findings of ~1–4px on the **caption** highlight words (selector `#caption-word-*` / `.caption-line`). The caption pill uses a deliberately snug `line-height` (set once in `scripts/captions.mjs`) and has **no `overflow:hidden`**, so a heavy display glyph's ink spills a few px into the pill's own padding — nothing is actually clipped. Treat these as expected and proceed. Do **not** inflate the caption `line-height` (it balloons the pill, which is worse). Only act on a `text_box_overflow` when it names a **frame** element (`#el-NN-*`), not a caption word.
After checks pass, pause for user review — the review loop's final look (`../hyperframes-core/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
After checks pass, pause for user review — the review loop's final look (`../hyperframes/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
Preview: `npx hyperframes preview --background`
@@ -217,19 +217,19 @@ The reusable, domain-agnostic shot shapes live in `../hyperframes-animation/blue
| Read | When |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `[../hyperframes-core/references/brief-contract.md](../hyperframes-core/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes/references/brief-contract.md](../hyperframes/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes-creative/references/story-spine.md](../hyperframes-creative/references/story-spine.md)` | Step 3: story doctrine — hook language, value-before-evidence, proposal shape, source-traceable visuals. |
| `[../hyperframes-creative/frame-presets/](../hyperframes-creative/frame-presets/)` | Step 2: choose and adopt a frame preset. |
| `[../hyperframes-creative/references/design-spec.md](../hyperframes-creative/references/design-spec.md)` | Step 2: apply brand tokens correctly. |
| `[references/story-design.md](references/story-design.md)` | Step 3: plan the explainer story. |
| `[../hyperframes-animation/blueprints-index.md](../hyperframes-animation/blueprints-index.md)` | Step 3: role→blueprint menu. Step 4: pick the shot shape. |
| `[../hyperframes-core/references/storyboard-format.md](../hyperframes-core/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes-core/references/script-format.md](../hyperframes-core/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../hyperframes/references/storyboard-format.md](../hyperframes/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes/references/script-format.md](../hyperframes/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../media-use/audio/references/tts.md](../media-use/audio/references/tts.md)` | Step 3.1: choose or understand TTS providers and voices. |
| `[references/visual-design.md](references/visual-design.md)` | Step 4: write the frame's shot sequence (+ Layout vocabulary). |
| `[references/motion-language.md](references/motion-language.md)` | Step 4: the motion vocabulary + the motion doctrine. |
| `[references/cut-catalog.md](references/cut-catalog.md)` | Step 4-5: the cut catalog (worker builds within-frame seams). |
| `[../hyperframes-animation/rules-index.md](../hyperframes-animation/rules-index.md)` + `[../hyperframes-animation/rules/](../hyperframes-animation/rules/)` | Step 5: local rule recipe bodies for the cited motions. |
| `[../hyperframes-core/references/frame-worker-core.md](../hyperframes-core/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[../hyperframes/references/frame-worker-core.md](../hyperframes/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[sub-agents/frame-worker.md](sub-agents/frame-worker.md)` | Step 5: the workflow's frame-worker delta. |
| `[../hyperframes-core/references/subagent-dispatch.md](../hyperframes-core/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
| `[../hyperframes/references/subagent-dispatch.md](../hyperframes/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
@@ -2,7 +2,7 @@
Use this reference in Step 3 to write `STORYBOARD.md` and `SCRIPT.md` for a faceless explainer — a topic, concept, how-to, listicle, or narrative explainer built from text, with **no product, no website, and no captured assets**.
This file defines the story: what the video teaches, in what order, and why each frame exists. It does not define layout, visual effects, animation, or final markdown schemas. For exact file syntax, follow `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/script-format.md`.
This file defines the story: what the video teaches, in what order, and why each frame exists. It does not define layout, visual effects, animation, or final markdown schemas. For exact file syntax, follow `../hyperframes/references/storyboard-format.md` and `../hyperframes/references/script-format.md`.
## Read first
@@ -1,18 +1,18 @@
#!/usr/bin/env node
// Thin wrapper over the shared packet builder in hyperframes-core — this file only
// Thin wrapper over the shared packet builder in hyperframes — this file only
// pins the paths that are specific to this workflow skill. The logic (frame
// splitting, rule citation, packet bounds, `_role.md` assembly) has one owner:
// ../../hyperframes-core/scripts/lib/frame-packets-core.mjs
// ../../hyperframes/scripts/lib/frame-packets-core.mjs
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as core from "../../hyperframes-core/scripts/lib/frame-packets-core.mjs";
import * as core from "../../hyperframes/scripts/lib/frame-packets-core.mjs";
const SKILL_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const CONFIG = {
animationDir: resolve(SKILL_DIR, "../hyperframes-animation"),
corePath: resolve(SKILL_DIR, "../hyperframes-core/references/frame-worker-core.md"),
corePath: resolve(SKILL_DIR, "../hyperframes/references/frame-worker-core.md"),
deltaPath: resolve(SKILL_DIR, "sub-agents/frame-worker.md"),
};
@@ -5,7 +5,7 @@
// ship standalone: installed via `npx skills add`, a skill's scripts can't reach
// the monorepo's core package, and the core export points at .ts source that
// `node` (which runs these scripts) can't load. CANONICAL contract = the core
// parser + skills/hyperframes-core/references/storyboard-format.md; keep this in
// parser + skills/hyperframes/references/storyboard-format.md; keep this in
// lockstep. Behavior: never throws, accepts freeform narrative, recognizes
// Frame/Beat/Scene headings at H2/H3, preserves unknown keys verbatim under
// `extra` (keys lowercased). Pure node — no deps.
@@ -1,6 +1,6 @@
# Frame worker — faceless-explainer delta
> The shared law is the core contract above (the packet builder prepends `../hyperframes-core/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a faceless-explainer frame; you run N-up, **one frame each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
> The shared law is the core contract above (the packet builder prepends `../hyperframes/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a faceless-explainer frame; you run N-up, **one frame each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
## Your `focal:` / `roles:` — invented elements
+7 -7
View File
@@ -20,7 +20,7 @@ A successful no-op means the skill is current. Surface an update failure instead
## 1. Apply cross-cutting source adapters
- **Media:** For any audio, image, icon, logo, voice, grade, LUT, treatment/effect, caption, or media-operation need, load `/media-use` and follow `../media-use/references/resolve.md` (resolve, adopt, reuse) and `../media-use/references/setup-providers.md` (providers, auth). Vague footage feedback and named styles use `../media-use/references/media-treatments.md` before editing; do not improvise supported media effects with CSS/SVG/opacity. Before the first authenticated provider action, run `npx hyperframes auth status` and relay its output verbatim. If signed out, apply the gate in `../hyperframes-core/references/brief-contract.md`: collaborative waits for sign-in or an explicit offline choice; autonomous states the status and continues through an available offline provider. Surface a blocker when no offline provider can satisfy a required capability. Local adoption alone does not require an auth gate.
- **Media:** For any audio, image, icon, logo, voice, grade, LUT, treatment/effect, caption, or media-operation need, load `/media-use` and follow `../media-use/references/resolve.md` (resolve, adopt, reuse) and `../media-use/references/setup-providers.md` (providers, auth). Vague footage feedback and named styles use `../media-use/references/media-treatments.md` before editing; do not improvise supported media effects with CSS/SVG/opacity. Before the first authenticated provider action, run `npx hyperframes auth status` and relay its output verbatim. If signed out, apply the gate in `../hyperframes/references/brief-contract.md`: collaborative waits for sign-in or an explicit offline choice; autonomous states the status and continues through an available offline provider. Surface a blocker when no offline provider can satisfy a required capability. Local adoption alone does not require an auth gate.
- **Figma:** If any input is a `figma.com` URL, run `/figma` first. Build from its exported assets, tokens, components, or storyboard frames. Do not use raw Figma connector calls because they skip SVG sanitization, media provenance, and brand-token binding.
These adapters do not change the workflow selected by `/hyperframes`.
@@ -42,11 +42,11 @@ For a new project, choose a kebab-case directory name from the brief and scaffol
npx hyperframes init "videos/<project>" --non-interactive --example=blank --skill=general-video
```
Then write `BRIEF.md` at the project root using `../hyperframes-core/references/brief-format.md`. In an existing project, the root is the directory containing `hyperframes.json`. Record only the confirmed preference-backed fields named by the brief format, using `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes <PROJECT_ROOT>`; never record inferred defaults. Here `<MEDIA_DIR>` is the installed `/media-use` skill directory and `<PROJECT_ROOT>` is the directory containing `hyperframes.json`. If the intent layer adopted a recipe, apply it now with `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes <PROJECT_ROOT> --name <name>` and do not ask again.
Then write `BRIEF.md` at the project root using `../hyperframes/references/brief-format.md`. In an existing project, the root is the directory containing `hyperframes.json`. Record only the confirmed preference-backed fields named by the brief format, using `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes <PROJECT_ROOT>`; never record inferred defaults. Here `<MEDIA_DIR>` is the installed `/media-use` skill directory and `<PROJECT_ROOT>` is the directory containing `hyperframes.json`. If the intent layer adopted a recipe, apply it now with `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes <PROJECT_ROOT> --name <name>` and do not ask again.
## 3. Interpret the run shape
Use only the canonical terms from `../hyperframes-core/references/brief-contract.md`:
Use only the canonical terms from `../hyperframes/references/brief-contract.md`:
| Field | Meaning | Effect |
| -------------- | ------------------------------------- | ----------------------------------------------------------------------------------- |
@@ -88,9 +88,9 @@ These reads are mandatory when their condition matches:
| Any composition HTML or scene layout | `/hyperframes-core`; use `references/determinism-rules.md` for its layout contract |
| Any non-trivial creation or visual treatment | `/hyperframes-creative` → `references/house-style.md` and `references/video-composition.md` |
| Any motion, animation, or scene transition | `/hyperframes-animation`; follow its routing to the matching rules, adapters, blueprints, or transition references |
| `storyboard: yes` | `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/review-loop.md` |
| `storyboard: yes` | `../hyperframes/references/storyboard-format.md` and `../hyperframes/references/review-loop.md` |
| Any media asset or operation, including narration, BGM, SFX, captions, grading, or transforms | `/media-use`; for framework playback and placement also read `/hyperframes-core` → `references/variables-and-media.md` |
| Multi-scene assembly | `../hyperframes-core/references/production-loop.md` |
| Multi-scene assembly | `../hyperframes/references/production-loop.md` |
| `flow: companion`, before the first plan | `/hyperframes-creative` → `references/story-spine.md` and `references/house-style.md`; the nearest genre lens and the full `../hyperframes/references/capability-menu.md` — the ceiling treatment is designed from these, not recalled |
| A companion capability offer, capture, beat grid, generative video, map, publishing, or cross-workflow capability | `../hyperframes/references/capability-menu.md` |
| A design spec exists, before final approval | `/hyperframes-creative` → `references/design-adherence.md` |
@@ -110,7 +110,7 @@ Use this dependency order. Skip a stage only when its input is absent.
`node <SKILL_DIR>/scripts/frame-packets.mjs --project "$PROJECT_DIR" --storyboard "$PROJECT_DIR/STORYBOARD.md"`
The builder writes one bounded packet per scene under `.hyperframes/frame-packets/` (the scene's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes-core/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch the workers — 2–3 scene packets each, all in one wave (`../hyperframes-core/references/subagent-dispatch.md`); each worker's prompt carries `_role.md` and its packets — paste them in full, or hand the file paths for the worker to read first (equivalent either way) — plus a dispatch context with `PROJECT_DIR`, its `frame_id`s, and canvas size. WAIT on every scene's `compositions/<frame_id>.html` + `compositions/<frame_id>.motion.json`. Workers read only their packets and the design truth file; they never open `STORYBOARD.md` or the skill documents. With no delegation channel, fall back serially: process one packet at a time in this context, still working from the packet alone.
The builder writes one bounded packet per scene under `.hyperframes/frame-packets/` (the scene's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch the workers — 2–3 scene packets each, all in one wave (`../hyperframes/references/subagent-dispatch.md`); each worker's prompt carries `_role.md` and its packets — paste them in full, or hand the file paths for the worker to read first (equivalent either way) — plus a dispatch context with `PROJECT_DIR`, its `frame_id`s, and canvas size. WAIT on every scene's `compositions/<frame_id>.html` + `compositions/<frame_id>.motion.json`. Workers read only their packets and the design truth file; they never open `STORYBOARD.md` or the skill documents. With no delegation channel, fall back serially: process one packet at a time in this context, still working from the packet alone.
5. **Merge motion sidecars.** Collect the workers' `compositions/<frame_id>.motion.json` files and carry their durations and exit/entry vectors into assembly; where the doctrine chain (`/motion-doctrine`) is installed, translate them into the project ledger before stamping seams.
6. **Assemble.** Mount scenes, media, transitions, captions, and audio using the production loop. Real voice duration overrides estimates. When a music bed plays under any voice track, carve the bed before verifying: `/hyperframes-audio` → `scripts/carve.mjs --comp index.html`. A volume duck alone does not finish the mix.
@@ -162,4 +162,4 @@ A run is complete only when:
- the user approves the final Studio preview before render;
- the rendered file is verified when a render was requested.
After final approval, offer once to freeze the run as a recipe, following `../hyperframes-core/references/review-loop.md` § 4.
After final approval, offer once to freeze the run as a recipe, following `../hyperframes/references/review-loop.md` § 4.
@@ -1,14 +1,14 @@
#!/usr/bin/env node
// Thin wrapper over the shared packet builder in hyperframes-core — this file pins
// Thin wrapper over the shared packet builder in hyperframes — this file pins
// this workflow's paths plus its one behavioral difference: design truth resolves
// frame.md → design.md → DESIGN.md (general-video § 6 order). Everything else has
// one owner: ../../hyperframes-core/scripts/lib/frame-packets-core.mjs
// one owner: ../../hyperframes/scripts/lib/frame-packets-core.mjs
import { existsSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as core from "../../hyperframes-core/scripts/lib/frame-packets-core.mjs";
import * as core from "../../hyperframes/scripts/lib/frame-packets-core.mjs";
const SKILL_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -22,7 +22,7 @@ function designTruthLine(projectDir) {
const CONFIG = {
animationDir: resolve(SKILL_DIR, "../hyperframes-animation"),
corePath: resolve(SKILL_DIR, "../hyperframes-core/references/frame-worker-core.md"),
corePath: resolve(SKILL_DIR, "../hyperframes/references/frame-worker-core.md"),
deltaPath: resolve(SKILL_DIR, "sub-agents/frame-worker.md"),
designTruthLine,
};
@@ -1,6 +1,6 @@
# Frame worker — general-video delta
> The shared law is the core contract above (the packet builder prepends `../../hyperframes-core/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a general-video scene; you run N-up, **one scene each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
> The shared law is the core contract above (the packet builder prepends `../../hyperframes/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a general-video scene; you run N-up, **one scene each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
## Your scene is invented, not captured
+1 -1
View File
@@ -62,7 +62,7 @@ Do not confuse these states:
| Storyboard board | Before composition checks, only when `storyboard: yes` | Review plan cards and wireframe sketches. Open `?view=storyboard#project/<name>`. |
| Final composition preview | After `check` passes | Review the assembled timeline before render. Open `#project/<name>`. |
The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes-core/references/review-loop.md`.
The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes/references/review-loop.md`.
## Sub-composition smoke test
+19 -24
View File
@@ -1,36 +1,28 @@
---
name: hyperframes-core
description: The HyperFrames composition contract — build one renderable project. Use for composition structure, the `data-*` timing attributes, `class="clip"`, tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Also covers Tailwind projects and the STORYBOARD.md / SCRIPT.md plan formats. Read before writing composition HTML.
description: The HyperFrames composition contract — build one renderable project. Use for composition structure, the `data-*` timing attributes, `class="clip"`, tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
---
# HyperFrames Core
HyperFrames renders video from HTML. A composition is an HTML file whose DOM declares timing with `data-*` attributes, whose animation runtime is seekable, and whose media playback is owned by the framework.
This skill is the **technical contract** — how to build one hyperframes project. The body below is the build guide; per-topic detail lives in `references/` (index next), read on demand. Other concerns live in the sibling domain skills — `hyperframes-animation`, `hyperframes-creative`, `media-use`, `hyperframes-cli`, `hyperframes-registry`. The capability map in `/hyperframes` says what each one covers.
This skill is the **technical contract** — how to build one hyperframes project. The body below is the build guide; per-topic detail lives in `references/` (index next), read on demand. Process docs (brief, storyboard, review, production, dispatch, frame-worker) live in `/hyperframes` → `references/`. Other concerns live in the sibling domain skills — `hyperframes-animation`, `hyperframes-creative`, `media-use`, `hyperframes-cli`, `hyperframes-registry`. The capability map in `/hyperframes` says what each one covers.
## References
| File | Read it to… |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `references/minimal-composition.md` | start from the smallest renderable composition skeleton |
| `references/composition-patterns.md` | choose monolithic vs modular; structure a modular `index.html`; pick a sub-comp archetype |
| `references/data-attributes.md` | look up any `data-*` (root / clip / sub-comp host / legacy aliases); use `class="clip"` |
| `references/tracks-and-clips.md` | understand what `data-track-index` does (and does not) control, z-index, time a clip relative to another |
| `references/creator-editing-recipes.md` | copy truthful cut/trim/reorder/retime/freeze/camera/mask/crossfade/audio editing recipes and their limits |
| `references/sub-compositions.md` | wire a sub-composition (host attrs, `<template>`, per-instance vars) and animate inside it |
| `references/variables-and-media.md` | declare variables; place `<video>`/`<audio>`, set volume, trim |
| `references/determinism-rules.md` | build a seekable timeline; determinism bans; layout / text fit |
| `references/full-screen-motion.md` | author full-frame motion with shared backgrounds |
| `references/storyboard-format.md` | author a `STORYBOARD.md` plan (+ the parsed manifest) |
| `references/review-loop.md` | run the plan → sketch → build review passes on a live board — shared by every storyboard-planning workflow |
| `references/production-loop.md` | take an approved plan to a delivered video — the stage dependencies (audio, frames, assembly, transitions, captions, verify, deliver) a freeform build follows directly |
| `references/brief-contract.md` | the brief's ground rules — mode derivation (collaborative / autonomous), shared field registry, question invariants (the asking itself lives in `/hyperframes` → the intent layer) |
| `references/brief-format.md` | author `BRIEF.md` — the confirmed intent document a workflow's Setup writes and every later step reads |
| `references/script-format.md` | author the optional `SCRIPT.md` locked narration |
| `references/subagent-dispatch.md` | map subagent dispatch verbs (parallel fan-out / background / wait) to your harness |
| `references/frame-worker-core.md` | the shared frame-worker role contract — each narrative workflow's packet builder prepends it to that workflow's `sub-agents/frame-worker.md` delta |
| `references/tailwind.md` | work in a Tailwind v4 project (`init --tailwind`; runtime contract differs from Studio's v3) |
| File | Read it to… |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `references/minimal-composition.md` | start from the smallest renderable composition skeleton |
| `references/composition-patterns.md` | choose monolithic vs modular; structure a modular `index.html`; pick a sub-comp archetype |
| `references/data-attributes.md` | look up any `data-*` (root / clip / sub-comp host / legacy aliases); use `class="clip"` |
| `references/tracks-and-clips.md` | understand what `data-track-index` does (and does not) control, z-index, time a clip relative to another |
| `references/creator-editing-recipes.md` | copy truthful cut/trim/reorder/retime/freeze/camera/mask/crossfade/audio editing recipes and their limits |
| `references/sub-compositions.md` | wire a sub-composition (host attrs, `<template>`, per-instance vars) and animate inside it |
| `references/variables-and-media.md` | declare variables; place `<video>`/`<audio>`, set volume, trim |
| `references/determinism-rules.md` | build a seekable timeline; determinism bans; layout / text fit |
| `references/full-screen-motion.md` | author full-frame motion with shared backgrounds |
| `references/tailwind.md` | work in a Tailwind v4 project (`init --tailwind`; runtime contract differs from Studio's v3) |
For animation runtime specifics (GSAP API, Lottie, Three.js, etc.) go to `hyperframes-animation` → `adapters/<runtime>.md`.
@@ -48,7 +40,7 @@ File shape, host wiring, and the pre-render checklist → `references/sub-compos
### Root must be sized (silent layout bug)
The standalone root needs an explicit **sized box** (`width`/`height` in px), and every ancestor down to a `height:100%` element must have a resolved height — otherwise a flex/`100%` child collapses to ~0 and content piles into the top-left corner. Do not rely on automated gates alone to catch this; inspect a snapshot. Skeleton → `references/minimal-composition.md`.
The standalone root authors `width`/`height: 100%`. Canvas size is `data-width`/`data-height`. The runtime stamps those pixels onto the composition root. Do not hardcode `1920px`/`1080px` on `#root`. Skeleton → `references/minimal-composition.md`.
### One paused timeline
@@ -62,6 +54,9 @@ Rules that `lint` **does** catch, but only after the fact. Write them right the
- Never put `crossorigin` on `<video>`/`<audio>`. `lint` rejects it unconditionally with `media_crossorigin_breaks_preview` (error), including for canvas/WebGL/WebAudio readback. There is no suppression.
- Never give a `<video data-start>` an ancestor that also carries `data-start`. `lint` rejects it with `video_nested_in_timed_element` (error). Time the wrapper **or** the video, not both.
- Every `<audio>` needs an `id`. `lint` rejects it with `media_missing_id`, and an id-less `<audio>` is never picked up by the mixer, so the render is **silent**.
- Never tween a `.clip` with `autoAlpha` or `visibility` — `lint` rejects it with `gsap_animates_clip_element`. Animate a child instead.
- A named CSS `font-family` needs an in-file `@font-face` to a shipped local file, or `lint` fires `font_family_without_font_face`.
- Sub-composition `#root` uses `width`/`height: 100%` (or `inset: 0`), not hardcoded `1920px`/`1080px`. Canvas size is `data-width`/`data-height`.
A lint **error** also switches off the layout and contrast audits: `check` then reports `0 sample(s)` and `0/0 text checks`, which reads like a clean file but means nothing ran. Clear lint errors before you trust those numbers.
@@ -70,7 +65,7 @@ A lint **error** also switches off the layout and contrast audits: `check` then
Surfaced here; full rationale in the linked reference. Do not violate:
- No render-time clocks / unseeded `Math.random` / network / input-state; no `repeat: -1` (use a finite count). → `determinism-rules.md`
- Never tween `display` or raw `visibility` on a clip element. The framework owns clip visibility, and `lint` rejects it. Use GSAP `autoAlpha` or a zero-duration boundary `set`. (Tweening ordinary visual properties on a clip element is fine; what lint forbids is taking over its visibility.) → `determinism-rules.md`
- Never tween `display`, `visibility`, or `autoAlpha` on a `.clip` element. The framework owns clip visibility, and `lint` rejects it (`gsap_animates_clip_element`). Animate a child instead. → `determinism-rules.md`
- No `<br>` in body text; transformed elements must be block-level + sized; pulsing absolute decoratives need peak clearance. → `determinism-rules.md`
- `<video>`/`<audio>` are found by a flat document query, so the framework seeks and decodes them at **any nesting depth** (including inside a sub-comp `<template>` or wrapper). One hard limit: `lint` errors if a `<video data-start>` sits inside another **plain** element that also has `data-start`, and the failure is real (wrong source frames, then the clip vanishes mid-slot), so put the timing on the wrapper or on the video, never both. Sub-composition hosts are exempt: media inside a sub-composition renders correctly. The other caveat is timelines, not placement: a sub-comp timeline can't animate host-root elements. → `variables-and-media.md`
- Keep every `id` unique across the **assembled** page (prefix sub-comp ids with the composition id, `#<id>-hero`) so your own `#id` CSS and `getElementById` calls resolve. Frame injection no longer depends on it: the compiler stamps a document-unique `data-hf-render-id` on every `video[src]`/`audio[src]`/`img[src]`. Media that uses `<source>` children instead of a `src` attribute is **not** stamped, so unique ids still matter there. → `composition-patterns.md`
@@ -1,6 +1,6 @@
# Composition Patterns
How to architect a project — when to inline everything in one HTML, when to split into sub-compositions, what the `index.html` orchestrator looks like at scale, and the common sub-composition archetypes seen in real projects. Pair with `minimal-composition.md` (single-file shape) and `sub-compositions.md` (mechanics of a sub-comp file).
How to architect a project: the `index.html` orchestrator at scale, and the common sub-composition archetypes. Pair with `minimal-composition.md` (single-file shape) and `sub-compositions.md` (mechanics of a sub-comp file).
## Two Architectures
@@ -13,27 +13,6 @@ How to architect a project — when to inline everything in one HTML, when to sp
Both architectures use the same runtime contract — `data-*` attributes + `window.__timelines[id]`. The choice is structural, not behavioral.
### Pick monolithic when
- The whole video is one continuous scene with no hard cuts.
- Scenes share heavy state (one canvas/WebGL context spanning the whole video, a single SVG that morphs across all beats).
- Total scope is small (~200–400 lines of markup + script).
- No scene is reused across projects.
### Pick modular when
- The video has clear scene cuts — each scene is its own segment of the timeline.
- Some scenes are large (>100 lines of markup or significant scripted animation).
- A scene is reusable (kinetic intro, end-card logo lockup, a transition).
- The video has a continuous audio track over multiple visual segments. Keep audio at the root, visual segments as sub-comps.
- You want to author/iterate on scenes in isolation (preview a single sub-comp file directly).
### Refactor between them
Conversion is mechanical and reversible. To lift a monolithic scene into a sub-comp: wrap the scene's markup + scoped CSS + its slice of the parent timeline into a `<template>`, save as `compositions/<scene>.html`, replace the inline content in `index.html` with a slot `<div data-composition-src="compositions/<scene>.html">`, and have the sub-comp register its own timeline at `window.__timelines["<scene>"]`. The parent timeline shrinks accordingly.
If a monolithic project is approaching three or more scene cuts, prefer modularizing _before_ adding the next scene. Mixed projects where some scenes are inline and siblings are in `compositions/` are the hardest to maintain.
## Modular Orchestrator Pattern
When using sub-compositions, `index.html` should be **thin**. Its job is to declare slots, lay them out in time, mount the audio track, and register a (usually empty) root timeline. All scene animation lives inside the sub-comps.
@@ -51,8 +30,8 @@ When using sub-compositions, `index.html` should be **thin**. Its job is to decl
}
#root {
position: relative;
width: 1920px;
height: 1080px;
width: 100%;
height: 100%;
overflow: hidden;
}
/* Sub-comp slots stretch to fill the root. */
@@ -110,7 +89,6 @@ When using sub-compositions, `index.html` should be **thin**. Its job is to decl
</div>
<script>
window.__timelines = window.__timelines || {};
window.__timelines["root"] = gsap.timeline({ paused: true });
</script>
</body>
@@ -162,7 +140,6 @@ The reason to reach for it: a sub-comp timeline **cannot** drive host elements (
<script>
// MAIN timeline drives the host video. Global time: scene starts at 20.
window.__timelines = window.__timelines || {};
const main = window.__timelines["main"];
main.fromTo(
"#final-video",
@@ -182,7 +159,6 @@ The reason to reach for it: a sub-comp timeline **cannot** drive host elements (
style="position:absolute; inset:0; pointer-events:none;"
>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// animate ONLY this sub-comp's own elements here (labels, frame, overlays)
window.__timelines["final-anim"] = tl;
@@ -216,7 +192,6 @@ When several beat-level scenes share continuous state — a chat thread that gro
<div class="phase" id="phase-b">…</div>
<div class="phase" id="phase-c">…</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
tl.set("#phase-a", { opacity: 1 }, 0);
tl.to("#phase-a", { opacity: 0, duration: 0.4 }, 3.0);
@@ -247,17 +222,3 @@ Audio always lives at the host (`index.html`) as a root-level `<audio>` so playb
| Audio at root | `data-track-index` well above visual tracks | `10` while visuals use `1` |
The `-template` suffix on `<template>` is conventional but not required — the runtime extracts contents from whichever `<template>` is in `<body>`, regardless of id. The prefix on inner element ids is the only safeguard against id collisions when multiple sub-comps are mounted into the same host page at once.
## Editing Existing Projects
Before adding or modifying scenes, identify which architecture is in use:
```bash
ls compositions/ 2>/dev/null && echo "modular" || echo "monolithic"
```
- In a **monolithic** project, add new scenes as inline `<section class="clip">` elements with a deliberate `data-start` and a sensible `data-track-index`, and extend the existing single timeline.
- In a **modular** project, match the pattern: add a new file under `compositions/`, add a slot in `index.html`, keep the root timeline thin. Do **not** start inlining new scenes into `index.html` when sibling scenes are sub-comps — the inconsistency is the worst of both worlds.
- If a monolithic project needs a third or fourth scene cut, lift each scene into a sub-comp before adding more. The conversion is mechanical (see "Refactor between them" above).
When picking the slot's `data-start`/`data-duration`, prefer continuing the existing sequencing convention (adjoining starts, deliberate overlaps for cross-fades). Don't introduce a new track index unless you actually need parallel visual layers — most sequential-scene projects use exactly one visual track.
@@ -47,7 +47,7 @@ These recipes keep sound on a separate `<audio>` element with the `<video>` mute
></audio>
```
Timeline math: B starts at A start + duration. Source math: each range starts at `data-media-start`; consumed source = timeline duration × rate. Audio follows: duplicate matching `<audio>` ranges/timing. Owner: `/hyperframes-core`. Limit: adjacent windows only; author the two windows edge to edge.
Timeline math: B starts at A start + duration. Source math: each range starts at `data-media-start`; consumed source = timeline duration × rate. Audio follows: duplicate matching `<audio>` ranges/timing. Owner: `/hyperframes-core`. Limit: adjacent windows only; author the two windows edge to edge. Same-track overlap is valid; both clips paint in CSS order.
## Trim in/out
@@ -314,11 +314,10 @@ Timeline math: overlap placed clips for the 0.5s handoff. Source math: each clip
></audio>
<script>
const tl = gsap.timeline({ paused: true });
tl.set("#b-visual", { autoAlpha: 0 }, 0)
.to("#a-visual", { autoAlpha: 0, duration: 0.5 }, 2.5)
.to("#b-visual", { autoAlpha: 1, duration: 0.5 }, 2.5);
window.__timelines = window.__timelines || {};
window.__timelines.main = tl;
tl.set("#b-visual", { opacity: 0 }, 0)
.to("#a-visual", { opacity: 0, duration: 0.5 }, 2.5)
.to("#b-visual", { opacity: 1, duration: 0.5 }, 2.5);
window.__timelines["main"] = tl;
</script>
```
@@ -14,7 +14,7 @@ Every renderable composition needs one root element:
| `data-fps` | No | Optional frame rate hint. CLI render flags can override output fps. |
| `data-composition-variables` | No | JSON array of variable declarations (on `<html>`). See `variables-and-media.md`. |
\*`data-duration` is optional whenever the runtime can auto-infer duration: a registered GSAP timeline, a finite CSS animation, a finite WAAPI `element.animate()`, or a registered Lottie animation. It is **required** for Three.js (no auto-inference), for infinite/unbounded CSS or WAAPI animations, and for any composition with no GSAP timeline and no animation signal at all. `npx hyperframes lint` enforces this (`root_composition_missing_duration_source`). See `determinism-rules.md` → "Duration Contract For Non-GSAP Runtimes" for the per-runtime breakdown.
\*`data-duration` is optional whenever the runtime can auto-infer duration: a registered GSAP timeline, a finite CSS animation, a finite WAAPI `element.animate()`, or a registered Lottie animation. It is **required** for Three.js (no auto-inference), for infinite/unbounded CSS or WAAPI animations, and for any composition with no GSAP timeline and no animation signal at all. `npx hyperframes lint` enforces this (`root_composition_missing_duration_source`). Per-runtime inference lives in `hyperframes-animation/adapters/`.
The root should be `position: relative`, have explicit pixel dimensions, and hide overflow unless intentionally composing outside the frame.
@@ -22,7 +22,7 @@ The root should be `position: relative`, have explicit pixel dimensions, and hid
**`data-start` is what makes an element a clip.** The runtime collects `[data-start]` and drives visibility off that attribute, so any element carrying it is timed.
`class="clip"` is a **convention, not a requirement**: the runtime never reads it. Keep writing it, because the scaffold's shared `.clip { position: absolute; inset: 0 }` rule is what gives a scene its full-frame box, Studio uses it as an edit hint, and `lint` warns (`timed_element_missing_clip_class`) when a timed element lacks it. If you drop the class you must supply that layout yourself. Omit it on `<video>` and `<audio>`.
`class="clip"` is a **convention, not a requirement**: the runtime never reads it. Keep writing it: drop it and the full-frame box collapses unless you supply that layout yourself, Studio uses it as an edit hint, and `lint` warns (`timed_element_missing_clip_class`) when a timed element lacks it. Omit it on `<video>` and `<audio>`.
**Nesting is allowed.** A timed element inside a wrapper is still timed, and a timed ancestor clamps its descendants: a child cannot be visible while its timed ancestor is hidden. Direct children of the root get automatic layout (see "Root-level clips get automatic layout" below); nested ones do not, so give them their own positioning.
@@ -15,18 +15,7 @@ For GSAP:
- **Do not** call `tl.play()` for render-critical motion.
- **Do not** create empty tweens only to set duration; use `data-duration` on the clip instead.
Use the `hyperframes-animation` skill for tween syntax, position parameters, eases, and performance rules.
### Duration Contract For Non-GSAP Runtimes
The render engine needs a positive total duration before it will capture a single frame — without one, capture fails outright with "Composition has zero duration." A GSAP timeline supplies this automatically. CSS, WAAPI, and Lottie compositions have no timeline object, so the runtime infers duration itself:
- **CSS**: longest `animation-delay` + `animation-duration` × finite `animation-iteration-count` across animated elements (offset by each element's `data-start`). `animation-iteration-count: infinite` cannot be inferred.
- **WAAPI**: longest `element.animate()` effect's `getComputedTiming().endTime`. Infinite `iterations` cannot be inferred.
- **Lottie**: the registered animation's native length (`totalFrames / frameRate`, or the dotLottie player's own `duration`) — always finite regardless of `loop`.
- **Three.js**: **not inferable**. The `three` adapter only forwards time via `hf-seek` — it has no `AnimationClip`/`AnimationMixer` inspection.
`data-duration` on the root `[data-composition-id]` element is therefore optional whenever every non-GSAP animation on the page is finite (CSS/WAAPI with finite iteration counts, or Lottie). It is **required** when: the composition has an infinite/unbounded CSS or WAAPI animation, the composition uses Three.js, or there is no GSAP timeline and no animation signal at all for any adapter to discover. `npx hyperframes lint` enforces exactly this (`root_composition_missing_duration_source`) — see the runtime/adapter-specific docs under `hyperframes-animation/adapters/` for the full contract per runtime.
Use the `hyperframes-animation` skill for tween syntax, position parameters, eases, and performance rules. Non-GSAP duration inference lives in `hyperframes-animation/adapters/`.
## Determinism Rules
@@ -40,7 +29,7 @@ Rendered frames must be reproducible from the requested time. Do **not** use any
Also avoid:
- Tweening `display` or raw `visibility` **on a clip element**: HyperFrames timing owns a clip's visibility, and `lint` rejects it. Use GSAP `autoAlpha` (it interpolates opacity and flips visibility only at the hidden endpoint) or a zero-duration `tl.set(..., { visibility: "hidden" | "visible" })` at an explicit beat boundary for a deterministic hard kill. Animating a clip element's ordinary visual properties (`opacity`, transforms, `filter`, …) is fine and the shipped catalog does it constantly; what is forbidden is taking over its visibility.
- Tweening `display`, raw `visibility`, or `autoAlpha` **on a clip element**: HyperFrames timing owns a clip's visibility, and `lint` rejects it (`gsap_animates_clip_element`). Fade with `opacity`, or tween a child wrapper. Do not tween `class="clip"`.
- There is no fixed allowlist of animatable properties. `lint` enforces a **denylist**, so `filter`, `clipPath`, `strokeDashoffset`, `width`, `height` and similar are all legitimate targets. Prefer transforms and opacity where you have the choice, for performance rather than correctness. The per-runtime detail lives in `hyperframes-animation/adapters/`.
- Animating the same property on the same element from multiple timelines at the same time — GSAP's overwrite behavior is order-dependent and can flip between renders.
@@ -62,9 +51,3 @@ Build the visible end-state in static HTML and CSS first, then animate from/to t
- **Do not** use `<br>` in body text. Forced breaks ignore the actual rendered font width and produce an extra break when the line already wraps naturally, causing overlap. Let text wrap via `max-width`. Exception: short display titles where each word is deliberately on its own line.
- **Transformed elements must be block-level + sized.** `transform`/`scaleX`/`scaleY` is a no-op on an inline `<span>`, and scaling an auto-width (0px) element shows nothing → invisible bars/fills. Give them `display: block`/`inline-block`/flex-item **and** a real `width`/`height` (e.g. `width: 100%` inside a sized parent). _(Silent — automated gates may miss it.)_
- **Absolutely-positioned decoratives that pulse or overshoot** (`yoyo` scale, `back.out`) need clearance at their **peak** size and must not straddle an `overflow: hidden` edge — else they overlap a neighbor or get clipped. Position for the largest frame, not the resting one. _(silent.)_
## Why This Matters
The renderer takes a time value and produces a pixel buffer. There is no notion of "playback" — every frame is a fresh seek. Any state that depends on having reached this frame _through_ a prior frame (timers, accumulated state, event-driven animations) will desync when the renderer samples out of order or in parallel.
If you find yourself reaching for `setTimeout`, `requestAnimationFrame`, or `addEventListener` to drive a visual, rebuild it as a tween on the timeline instead.
@@ -2,10 +2,6 @@
For full-frame motion (continuous backgrounds, color washes, full-bleed visual states that span multiple clips), prefer a **shared background layer + transparent timed content layers** over stacked opaque scene backgrounds.
## Why
Stacking opaque scene divs means every scene change has to repaint the entire frame, every cross-scene visual continuity has to be faked, and every "global" state (a hue shift, a vignette, a film grain) has to be duplicated on every scene. A shared background layer driven by the seekable timeline gives you one continuous visual surface and makes scenes themselves cheap and transparent.
## Pattern
```html
@@ -48,7 +44,6 @@ Stacking opaque scene divs means every scene change has to repaint the entire fr
</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// Drive the shared background from the seekable timeline.
@@ -70,7 +65,3 @@ Stacking opaque scene divs means every scene change has to repaint the entire fr
- **Drive global state from the shared layer.** Hue shifts, vignettes, grain, film-look filters — animate them once on the shared layer, not per-scene.
- **Do not animate visibility on `.clip` elements.** HyperFrames already shows/hides clips based on `data-start` and `data-duration`. Animating `display` / `visibility` on the clip itself races with the framework's own show/hide. Animate a _child wrapper_ inside the clip instead.
- **Verify intentional overflow with snapshots.** Before adding `data-layout-allow-overflow` to silence an inspect warning, run `npx hyperframes snapshot` and confirm the overflow is what you want.
## When Not to Use This Pattern
If scenes really are visually disjoint — hard cuts between distinct color worlds with no continuity — the stacked-opaque pattern is fine. The shared-background pattern is for compositions where the background **is part of the motion language**, not just backdrop.
@@ -19,8 +19,8 @@ The smallest renderable HyperFrames composition — a standalone (top-level) roo
}
#root {
position: relative;
width: 1920px;
height: 1080px;
width: 100%;
height: 100%;
overflow: hidden;
}
.clip {
@@ -48,8 +48,7 @@ So `<template>` is not just a wrapper — it is the **transport container**. If
<template>
<!-- EVERYTHING the runtime needs goes here: styles, markup, scripts -->
<style>
/* Root: style by #root, never a class. (At render the CSS is scoped to
data-composition-id, so a class on the root stops matching — see Pitfall 3.) */
/* Root: style by #root, never a class. lint: subcomposition_root_styled_by_class. See Pitfall 3. */
#root {
position: absolute;
inset: 0;
@@ -63,7 +62,6 @@ So `<template>` is not just a wrapper — it is the **transport container**. If
</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// ... build timeline ...
window.__timelines["data-chart"] = tl;
@@ -77,7 +75,7 @@ Contrast with **standalone** compositions, which put the root directly in `<body
## Common pitfalls that pass static checks but break at render
Static file checks cannot prove the **cross-file mount contract**. These failures appear only when the runtime mounts the sub-composition. Watch for them at author time and verify with the pre-render snapshot checklist below.
Static file checks cannot prove the **cross-file mount contract**. These failures appear only when the runtime mounts the sub-composition.
### Pitfall 1 — `<style>` in `<head>` instead of inside `<template>`
@@ -137,68 +135,7 @@ Static file checks cannot prove the **cross-file mount contract**. These failure
### Pitfall 3 — Styling the root by a class instead of `#root`
```html
<!-- ❌ WRONG — class on the root, stylesheet keyed off it -->
<template>
<style>
.frame {
position: absolute;
inset: 0;
background: #faf9f5;
}
.frame .title {
font-size: 120px;
}
</style>
<div id="root" class="frame" data-composition-id="03-scene" ...>
<div class="title">…</div>
</div>
</template>
<!-- ✅ RIGHT — root styled by #root, descendants by plain selectors -->
<template>
<style>
#root {
position: absolute;
inset: 0;
background: #faf9f5;
}
.title {
font-size: 120px;
}
</style>
<div id="root" data-composition-id="03-scene" ...>
<div class="title">…</div>
</div>
</template>
```
**Why this happens:** when sub-compositions are inlined into one composited render, the compiler **scopes each file's CSS to its own `data-composition-id`** so scenes can't leak styles into each other: every rule `S` becomes `[data-composition-id="<id>"] S` (a _descendant_ selector). A rule whose leftmost selector is the **root's own class** (`.frame`) therefore becomes `[data-composition-id="<id>"] .frame`, which cannot match the scoped element itself. `#root` is special-cased by the scoper and keeps matching the root; plain descendant selectors (`.title`) match normally. The per-scene class namespace is also just redundant, since the `data-composition-id` scope already isolates each scene's styles.
**What actually happens today:** the render no longer drops these rules. Since #1886 the producer preserves the authored root as a `data-hf-inner-root` wrapper _inside_ the scoped element, so a class on the root still matches as a descendant and preview and render agree. The regression is pinned by `packages/producer/tests/sub-comp-class-selector/`.
**So why still use `#root`?** Because `lint` rejects the class form outright (`subcomposition_root_styled_by_class`, **error**), and it is the pattern the registry blocks model (e.g. `apple-money-count`). Treat this as a lint constraint you must satisfy, not as a render bug you are avoiding. If you inherited a composition that styles its root by class, it renders correctly; you still have to convert it to `#root` to get a green `lint`.
### Verification checklist before render
```bash
# For every sub-composition file in compositions/:
# 1) <style> + <script> + main markup all live INSIDE <template>
grep -n "<style\|<script\|<template" compositions/<scene>.html
# → first <style>/<script>/<div data-composition-id> should be AFTER <template>, before </template>
# 2) host data-composition-id == internal data-composition-id == window.__timelines key
grep "data-composition-id" index.html
grep "data-composition-id\|__timelines\[" compositions/<scene>.html
# → all three strings must match exactly per scene
# 3) the root is styled by #root, not by a class on the data-composition-id element
grep -n 'data-composition-id=' compositions/<scene>.html
# → that element should NOT also carry a class="…" that the <style> keys off
# (e.g. `.frame { … }`); scoping drops it. Style the root via #root. See Pitfall 3.
```
For the runtime end-to-end check (a fast `snapshot` pass + per-scene frame eyeball), see the **Visual smoke test** step in `hyperframes-cli`'s Minimum Completion Gate — that is the only gate that catches these three pitfalls.
`lint` still errors (`subcomposition_root_styled_by_class`) if a sub-composition styles the host root's own class. Style the root with `#root`.
## What HyperFrames Does With the Sub-Composition
+1 -20
View File
@@ -2,13 +2,6 @@
HyperFrames `init --tailwind` uses the Tailwind browser runtime pinned by the scaffold. Treat it as Tailwind v4, not Studio's Tailwind v3 setup.
## When To Use
- The project was scaffolded with `npx hyperframes init --tailwind`.
- `index.html` contains `window.__tailwindReady`.
- The task asks for Tailwind utility classes, `@theme`, custom utilities, or v3-to-v4 fixes in a composition.
- Rendered frames have missing Tailwind styles or frame-0 flashes.
## Version Contract
- **Pinned: `@tailwindcss/browser@4.2.4`** (source of truth: `packages/cli/src/commands/init.ts` `TAILWIND_BROWSER_VERSION`).
@@ -44,7 +37,7 @@ Avoid v3-only patterns in browser-runtime compositions:
Do not add `tailwind.config.js` only for composition colors, fonts, spacing, or utilities. Use `@theme` and `@utility`.
**Migrating from v3?** Load an existing JS config explicitly: put `@config "./tailwind.config.js";` inside a `text/tailwindcss` block. v4 does **not** auto-detect v3 config files.
**`@config` / `@plugin` abort the browser compile.** The pinned `@tailwindcss/browser` build does not support JS config or plugins. Theme and utilities stay in a `text/tailwindcss` block.
## Composition Pattern
@@ -109,15 +102,3 @@ npx hyperframes check
# Render proof — frame 0 must NOT flash unstyled content. Preview alone can hide this.
npx hyperframes render . --workers 1 --quality draft --output tailwind-proof.mp4
```
## Quick Debug Checklist
When Tailwind styles don't apply in a render, check in order:
1. Project scaffolded with `npx hyperframes init --tailwind`?
2. `index.html` `<head>` has `<script src="…@tailwindcss/browser@4.2.4…">` (not `cdn.tailwindcss.com`)?
3. `window.__tailwindReady` Promise present in `<head>`?
4. No v3 directives (`@tailwind base/components/utilities`) in the file?
5. Tokens moved from `tailwind.config.js` to `@theme` (or `@config` reference for v3 migration)?
6. Every render-critical class appears as a complete static token (no `bg-${color}-500` style assembly)?
7. Re-run `npx hyperframes check`, then the render proof above.
@@ -58,7 +58,7 @@ document.getElementById("title").textContent = title;
### Two JSON Shapes (Easy to Confuse)
- `data-composition-variables` is an **array of declarations** (the schema): `[{id, type, label, default}, ...]`
- `--variables` and `data-variable-values` are **objects keyed by id** (the values): `{ title: "Q4", accent: "#fff" }`
- `--variables` and `data-variable-values` are **objects keyed by id** (the values): `{"title":"Q4","accent":"#fff"}`
## Media
@@ -21,7 +21,7 @@ Structure is value-first; the **voice** stays whatever the workflow prescribes (
## 3. The storyboard is a proposal, not a listing
When Step 3 presents the plan (a checkpoint gate — `hyperframes-core/references/brief-contract.md` § 1):
When Step 3 presents the plan (a checkpoint gate — `hyperframes/references/brief-contract.md` § 1):
- Open by echoing the strategy line: **"This video tells [audience] that [message]."**
- Present the frames as a markdown table, one row per frame:
@@ -60,7 +60,7 @@ You know these rules but you violate them. Stop.
- **Don't pair two sans-serifs.** You do this constantly — one for headlines, one for body. Cross the boundary: serif + sans, or sans + mono.
- **One expressive font per scene.** You pick two interesting fonts trying to make it "better." One performs, one recedes.
- **Weight contrast must be extreme.** You default to 400 vs 700. Video needs 300 vs 900. The difference must be visible in motion at a glance.
- **Video sizes, not web sizes.** Full-screen viewing (YouTube / website embed): body 20px minimum, headlines 60px+, data labels 16px. **In-feed viewing** (destination = X / LinkedIn / Instagram feed — `hyperframes-core/references/brief-contract.md` § 2): the video plays small inside a scrolling feed, so scale up — body ≥32px, headlines ≥90px, data labels ≥24px (first-pass values; calibrate against real renders). You will try to use 14px. Don't.
- **Video sizes, not web sizes.** Full-screen viewing (YouTube / website embed): body 20px minimum, headlines 60px+, data labels 16px. **In-feed viewing** (destination = X / LinkedIn / Instagram feed — `hyperframes/references/brief-contract.md` § 2): the video plays small inside a scrolling feed, so scale up — body ≥32px, headlines ≥90px, data labels ≥24px (first-pass values; calibrate against real renders). You will try to use 14px. Don't.
## What You Don't Do Without Being Told
+1 -1
View File
@@ -14,7 +14,7 @@ description: >
# HyperFrames entry point
HyperFrames **renders video from HTML** — a composition is an HTML file whose DOM declares timing with `data-*` attributes, whose animation runtime is seekable, and whose media playback is owned by the framework. The full authoring contract lives in `/hyperframes-core`; read it before writing composition HTML.
HyperFrames **renders video from HTML** — a composition is an HTML file whose DOM declares timing with `data-*` attributes, whose animation runtime is seekable, and whose media playback is owned by the framework. The full authoring contract lives in `/hyperframes-core`; read it before writing composition HTML. Brief, storyboard, review, production, dispatch, and frame-worker contracts live in this skill's `references/`.
## 1. Start from project state
@@ -44,4 +44,4 @@ The narrative workflows carry genre-tuned design references — `story-design.md
| a topic / mechanism / concept explainer | `/faceless-explainer` → `references/` |
| a code-change walkthrough | `/pr-to-video` → `references/` (+ `code-vocabulary.md` for code frames) |
`motion-language.md` and `cut-catalog.md` are near-identical across the three — take them from the genre you already resembled, or `/faceless-explainer`'s as the neutral default. **Borrow the shape and the taste, never the machinery**: their scripts and directory rules belong to their pipelines; the generic back half of any build lives in `hyperframes-core/references/production-loop.md`.
`motion-language.md` and `cut-catalog.md` are near-identical across the three — take them from the genre you already resembled, or `/faceless-explainer`'s as the neutral default. **Borrow the shape and the taste, never the machinery**: their scripts and directory rules belong to their pipelines; the generic back half of any build lives in `hyperframes/references/production-loop.md`.
@@ -9,7 +9,7 @@ These reads are mandatory when their condition matches; do not replace them with
| The route is picked, before confirming or interviewing | `routes/<workflow>.md` — the whole file (contract + interview, ~1KB) |
| Triage judged the request unformed, before any concept work | `pitch-round.md` |
| Offering optional capabilities or collecting supplied media | The route-filtered rows in `capability-menu.md` |
| Question rules or field semantics beyond the schema below | `../../hyperframes-core/references/brief-contract.md` |
| Question rules or field semantics beyond the schema below | `brief-contract.md` |
## Adapt orthogonal inputs first
@@ -24,22 +24,22 @@ A GitHub PR URL is not a website source. A named or adopted recipe already carri
- **Remembered defaults.** Let `<MEDIA_DIR>` be the installed `/media-use` skill directory. For an existing project, `<MEMORY_ROOT>` is its root. Before scaffolding, use a deliberately nonexistent probe path with no `.media`, such as `/tmp/hyperframes-intent-memory-<run-id>`; never use the current workspace. Run `node <MEDIA_DIR>/scripts/prefs.mjs get --hyperframes <MEMORY_ROOT> --json`. Make each remembered value the recommended option and name its source. The pre-project probe sees only the personal tier; do not claim project provenance.
- **Recipes.** Run `node <MEDIA_DIR>/scripts/recipe.mjs list --hyperframes <MEMORY_ROOT> --json`. If the user names a recipe, says "like last time," or a recipe matches the probable route, ask whether to adopt it before other brief questions — and make the offer earn the yes: say why it matches and what adopting saves ("this matches your launch-promo recipe — adopting fills destination, aspect, language, and the design spec; you'd confirm the message and the two run-shape questions"). When several match, list them and include "none." An adopted recipe locks the fields it contains; ask only its missing fields and the run-shape questions. It does not remove review or render approval gates.
**2 — Triage the input.** What is the video about — a website (sold or shown), a PR, a topic, a music track, existing footage? And is the request **formed** — the message, the material, and the occasion readable from what the user gave — or **unformed**, a subject with no take on it? Source material doesn't settle this by itself: a site, document, or PR carries its own thesis, but five tellings of that thesis are five different videos — a request whose only shape comes from its source ("make a video about this URL") is formed about the facts and unformed about the telling, and enters the round to pitch the telling. A formed request runs the layer exactly as it always has; nothing below is added for it. An unformed one goes through the pitch round after routing (step 4) and earns one question here, before anything is generated: what is the user already picturing? Their answer seeds the round (`pitch-round.md`). A user who says they don't know video at all gets that reference's decision map instead of a question sequence. For a genuinely exploratory request ("we need a video but I'm not sure what kind"), don't interrogate — establish the subject and what exists to show, one question at a time, then close by **recommending** a route plus how the run will review: a text storyboard first, on a live board, with optional wireframe sketches before the full build (`../../hyperframes-core/references/review-loop.md`). The user hears the process before any workflow starts.
**2 — Triage the input.** What is the video about — a website (sold or shown), a PR, a topic, a music track, existing footage? And is the request **formed** — the message, the material, and the occasion readable from what the user gave — or **unformed**, a subject with no take on it? Source material doesn't settle this by itself: a site, document, or PR carries its own thesis, but five tellings of that thesis are five different videos — a request whose only shape comes from its source ("make a video about this URL") is formed about the facts and unformed about the telling, and enters the round to pitch the telling. A formed request runs the layer exactly as it always has; nothing below is added for it. An unformed one goes through the pitch round after routing (step 4) and earns one question here, before anything is generated: what is the user already picturing? Their answer seeds the round (`pitch-round.md`). A user who says they don't know video at all gets that reference's decision map instead of a question sequence. For a genuinely exploratory request ("we need a video but I'm not sure what kind"), don't interrogate — establish the subject and what exists to show, one question at a time, then close by **recommending** a route plus how the run will review: a text storyboard first, on a live board, with optional wireframe sketches before the full build (`review-loop.md`). The user hears the process before any workflow starts.
**3 — Pick the route** (the route table and ambiguity rules in the SKILL.md), then read `routes/<workflow>.md`. Its Interview section lists the must-have questions to ask now, the **deferred asks** to announce, whether the two run-shape questions apply, and which fields the pitch round may answer.
**4 — The pitch round** — unformed requests only; formed requests and recipe adoptions go straight to the must-haves. Sample five concepts along five genuinely different paths, at least two from the distribution's tail, and present them all before recommending one — pick, mix, and redirect are all answers. Each pitch names the capability or two it rides, in the plain language of `capability-menu.md` — the toolbox experienced as concepts, not listed as a menu. On an autonomous run the same gate runs internally, and the heads-up names the direction chosen and the typical one left behind. The procedure — the sampling gate, the presentation discipline, and the decision map for users new to video — is `pitch-round.md`. The chosen concept answers the route's pitch-eligible fields and lands in `BRIEF.md` under `## Intent`; the capabilities it named are confirmed with it, under `## Customizations`.
**5 — The route's must-haves.** One question per field, recommended option first with its receipt (rules: `../../hyperframes-core/references/brief-contract.md` § 3). Skip a question only when the request already answered it — inference is not an answer, but a chosen pitch is: fields the pitch round settled are locked with the pitch as their receipt. Then announce the route's deferred asks in one line ("after I probe the clip, I'll offer 2–3 caption identities") so the user hears the run's full shape before it starts.
**5 — The route's must-haves.** One question per field, recommended option first with its receipt (rules: `brief-contract.md` § 3). Skip a question only when the request already answered it — inference is not an answer, but a chosen pitch is: fields the pitch round settled are locked with the pitch as their receipt. Then announce the route's deferred asks in one line ("after I probe the clip, I'll offer 2–3 caption identities") so the user hears the run's full shape before it starts.
**6 — The two run-shape questions** — where the route's entry applies them, asked after the must-haves, each on its own:
- **(a) Storyboard?** Review the plan, wireframe sketches, and the finished piece pass by pass on a live board (`../../hyperframes-core/references/review-loop.md`) — recommended for anything beyond a couple of scenes — or skip the board and get one finished video from the confirmed brief.
- **(a) Storyboard?** Review the plan, wireframe sketches, and the finished piece pass by pass on a live board (`review-loop.md`) — recommended for anything beyond a couple of scenes — or skip the board and get one finished video from the confirmed brief.
- **(b) Automation or companion?** **Automation** — the matched workflow's pipeline executes the brief end to end. **Companion** — build it together in `/general-video` with every HyperFrames capability on the table; the route's answers still describe the video, general-video executes them.
These two are **orthogonal — never merge them into one menu.** All four `flow` × `storyboard` combinations are valid user choices (a companion run reviews on the live board too when `storyboard: yes`); a flattened three-option list ("storyboard review / one shot / companion") silently makes companion-with-storyboard unselectable. When a diagram or source material summarizes the outcomes as three branches, that is the derived behavior (`brief-contract.md` § 1), not the question shape. In a form-style question UI, keep (a) and (b) as two separate selects.
Signals replace questions, never add them: an ongoing "just build it" / "surprise me" / "don't ask" locks `flow: automation, storyboard: no`, and every unanswered field becomes a decision with a receipt in the heads-up. A storyboard request, however phrased, locks `storyboard: yes`. Remembered `flow` / `storyboard` values reorder the recommendations — they never make either question disappear. The run's collaborative/autonomous execution mode derives from these two answers — the old first question is never asked; the canonical mapping is `../../hyperframes-core/references/brief-contract.md` § 1.
Signals replace questions, never add them: an ongoing "just build it" / "surprise me" / "don't ask" locks `flow: automation, storyboard: no`, and every unanswered field becomes a decision with a receipt in the heads-up. A storyboard request, however phrased, locks `storyboard: yes`. Remembered `flow` / `storyboard` values reorder the recommendations — they never make either question disappear. The run's collaborative/autonomous execution mode derives from these two answers — the old first question is never asked; the canonical mapping is `brief-contract.md` § 1.
**7 — Nice-to-have: recommend, then show.** Skip this step when the selected route file says to skip the front-door capability offer. Otherwise, once the must-haves are locked, send one offer, not an interrogation — recommendations first, catalog on request. Capabilities the chosen pitch already named are settled with the concept — this step recommends from what the pitch didn't cover, and after a pitch round it is often just the two open asks and the design ask:
@@ -50,17 +50,17 @@ Signals replace questions, never add them: an ongoing "just build it" / "surpris
The full route-filtered slice appears only when the user asks what else is possible. An accepted recommendation is a confirmed answer: when it lands on a preference-backed field (a preset, a voice, a caption identity), it records like any other confirmation, and `/media-use`'s promotion rules make it the next run's recommended default. Capture answers verbatim in `BRIEF.md` under `## Assets`, `## Customizations`, or `## Notes`. One round; silence or "no" moves on.
**8 — Hand off.** Three disciplines close the conversation (invariants: `../../hyperframes-core/references/brief-contract.md` § 3):
**8 — Hand off.** Three disciplines close the conversation (invariants: `brief-contract.md` § 3):
- **One integration check.** Read the combined answers for a consequence no single answer showed — vertical at 90 seconds with a chart-dense concept means charts a phone can't read — and surface it with a proposed adjustment now, not at the sketch pass.
- **Stated and inferred, apart.** Present the locked brief as one summary — deferred asks and the run's shape included — with what the user answered and what was inferred or defaulted as two visibly separate groups, receipts on both. The inferred group is where corrections live; an autonomous heads-up is mostly that group.
- **Revision is not confirmation.** When the user corrects the summary, fold the change in and present it again; never execute an edited-but-unconfirmed brief.
Then enter the workflow (`flow: companion` → `/general-video`; otherwise the matched route), installing it first per the SKILL.md's install step. The workflow's Setup writes `BRIEF.md` from this summary as its **first action after `hyperframes init`** (never before — `init` refuses a non-empty directory), using the canonical frontmatter below and preserving the user's important wording in the body — the chosen pitch, when there is one, under `## Intent`. It then records the preference-backed fields (`../../hyperframes-core/references/brief-format.md` names the subset), and asks no brief question again.
Then enter the workflow (`flow: companion` → `/general-video`; otherwise the matched route), installing it first per the SKILL.md's install step. The workflow's Setup writes `BRIEF.md` from this summary as its **first action after `hyperframes init`** (never before — `init` refuses a non-empty directory), using the canonical frontmatter below and preserving the user's important wording in the body — the chosen pitch, when there is one, under `## Intent`. It then records the preference-backed fields (`brief-format.md` names the subset), and asks no brief question again.
## BRIEF.md frontmatter — the carry-away artifact
The interview's deliverable. Every later "what did the route require?" re-reads this ~1KB file, never this document. One key per confirmed field, canonical normalized values (full shape and body sections: `../../hyperframes-core/references/brief-format.md`):
The interview's deliverable. Every later "what did the route require?" re-reads this ~1KB file, never this document. One key per confirmed field, canonical normalized values (full shape and body sections: `brief-format.md`):
| Key | Meaning | Example |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------- |
@@ -8,4 +8,4 @@ route's own file, together with its input/output/trigger contract:
`routes/pr-to-video.md` (including the PR-size → length table).
The interview procedure itself (the eight steps) is `references/intent-interview.md`.
Field semantics and question rules: `hyperframes-core/references/brief-contract.md` § 2–3.
Field semantics and question rules: `hyperframes/references/brief-contract.md` § 2–3.
+2 -2
View File
@@ -10,7 +10,7 @@ node <SKILL_DIR>/scripts/prefs.mjs record --hyperframes . --key destination --va
node <SKILL_DIR>/scripts/prefs.mjs record --hyperframes . --key style_preset --value pin-and-paper --workflow faceless-explainer
```
Only what the user actually confirmed gets recorded — never an inferred or defaulted value. How workflows consume these (a remembered value becomes the recommended default with a receipt, and never skips a question) is the brief contract's rule: `hyperframes-core/references/brief-contract.md` § 2, Remembered defaults.
Only what the user actually confirmed gets recorded — never an inferred or defaulted value. How workflows consume these (a remembered value becomes the recommended default with a receipt, and never skips a question) is the brief contract's rule: `hyperframes/references/brief-contract.md` § 2, Remembered defaults.
## Recipes — frozen video bundles
@@ -22,7 +22,7 @@ node <SKILL_DIR>/scripts/recipe.mjs list --hyperframes . --workflow product-laun
node <SKILL_DIR>/scripts/recipe.mjs use --hyperframes . --name weekly-promo # also: resolve.mjs --type recipe --entity weekly-promo
```
The freeze is offered once after the final approval (`hyperframes-core/references/review-loop.md` § 4), and the intent layer (`/hyperframes` → `references/intent-interview.md`, step 1) checks for a match before its first question. Adopting a recipe fills the brief, the design spec, and the storyboard skeleton — and unlike preferences it may skip the questions it answers: the bundle was approved as a whole, and adoption itself is the question.
The freeze is offered once after the final approval (`hyperframes/references/review-loop.md` § 4), and the intent layer (`/hyperframes` → `references/intent-interview.md`, step 1) checks for a match before its first question. Adopting a recipe fills the brief, the design spec, and the storyboard skeleton — and unlike preferences it may skip the questions it answers: the bundle was approved as a whole, and adoption itself is the question.
## Files
+1 -1
View File
@@ -11,7 +11,7 @@
* Record one confirmed brief answer into the project tier; the same value
* confirmed in two different projects promotes the key to the user tier.
*
* Consumption rules live in hyperframes-core/references/brief-contract.md § 2
* Consumption rules live in hyperframes/references/brief-contract.md § 2
* (Remembered defaults): a remembered value becomes the recommended option
* with a receipt — it never skips a question.
*/
+1 -1
View File
@@ -19,7 +19,7 @@
* frame.md over the project's, and print the brief values + skeleton path.
*
* When recipes are offered/consumed is the review loop's and the intent
* layer's business — see hyperframes-core/references/review-loop.md § 4 and
* layer's business — see hyperframes/references/review-loop.md § 4 and
* the intent layer's recipe check (hyperframes SKILL.md § 4).
*/
import { parseArgs } from "node:util";
+1 -1
View File
@@ -19,7 +19,7 @@ description: >
> **The front door is `/hyperframes`.** This skill makes a **short, design-led, unnarrated motion graphic** (motion is the message; ~under 10s, no voice-over). Anything longer, narrated, or multi-scene — or any uncertainty → read `/hyperframes` first: the intent layer owns every route decision.
This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes-core/references/brief-contract.md`. When a `BRIEF.md` exists, read it before the director's question.
This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes/references/brief-contract.md`. When a `BRIEF.md` exists, read it before the director's question.
A short design-led motion graphic. **Asset-first**: decide the asset strategy and source real material _before_ designing the shot, then design the shot around what you have, then compose by reusing catalog capabilities. All artifacts go to `PROJECT_DIR = videos/<project-name>/` (created in Step 0); all paths below are relative to it.
+3 -3
View File
@@ -9,7 +9,7 @@ description: "Turn a music track (an audio file, a video to pull audio from, or
Use this skill to turn a **music track** into a beat-synced HyperFrames video. You analyze the track once, lay out the frames, fill in a per-frame plan, and build each frame as a composition. The input is a music track plus optional user images or videos — there is **no narration and no website capture**. Typography and templates are the floor (a complete video needs zero assets); any media the user supplies is cut in on the same beat grid.
You are the **orchestrator**. Work in `videos/<project>/`. Run the steps in order and pass each **Gate** before moving on. Two steps need the user: **Step 3** (plan approval) and **Step 6** (render approval) — both are checkpoint gates per `../hyperframes-core/references/brief-contract.md` (read it before Step 0): in autonomous mode, post the summary as a heads-up and proceed instead of waiting. Do every step yourself except **Step 4**, where you dispatch **one sub-agent per frame**. Keep design and motion rules out of this file — they live in `references/` and the `frame-worker` sub-agent.
You are the **orchestrator**. Work in `videos/<project>/`. Run the steps in order and pass each **Gate** before moving on. Two steps need the user: **Step 3** (plan approval) and **Step 6** (render approval) — both are checkpoint gates per `../hyperframes/references/brief-contract.md` (read it before Step 0): in autonomous mode, post the summary as a heads-up and proceed instead of waiting. Do every step yourself except **Step 4**, where you dispatch **one sub-agent per frame**. Keep design and motion rules out of this file — they live in `references/` and the `frame-worker` sub-agent.
`SKILL_DIR` = this skill directory. `PROJECT_DIR` = `videos/<project-name>/`.
@@ -108,7 +108,7 @@ Fix every `✗` (hard errors: duration mismatch, frames not tiling the track, a
Goal: Build every frame as a self-contained composition file.
Create `compositions/frames/`. Read [`sub-agents/frame-worker.md`](sub-agents/frame-worker.md) and `../hyperframes-core/references/subagent-dispatch.md`. Dispatch **one frame-worker per frame**, in parallel where possible (otherwise in waves). Each worker gets exactly one frame and this context:
Create `compositions/frames/`. Read [`sub-agents/frame-worker.md`](sub-agents/frame-worker.md) and `../hyperframes/references/subagent-dispatch.md`. Dispatch **one frame-worker per frame**, in parallel where possible (otherwise in waves). Each worker gets exactly one frame and this context:
```text
PROJECT_DIR: <abs path>
@@ -191,7 +191,7 @@ Inspect at `t=0`, each frame start, the strongest DROP / SURGE, every `hard_stop
| [`references/motion-primitive-catalog.md`](references/motion-primitive-catalog.md) | Step 3/4: L0 recipes for free-compose |
| [`references/montage.md`](references/montage.md) | Step 3/4: asset treatments (beat-cut / ken-burns) |
| [`sub-agents/frame-worker.md`](sub-agents/frame-worker.md) | Step 4: dispatch + build one frame |
| `../hyperframes-core/references/subagent-dispatch.md` | Step 4: dispatch sub-agents safely |
| `../hyperframes/references/subagent-dispatch.md` | Step 4: dispatch sub-agents safely |
| `../hyperframes-creative/references/design-spec.md` | Step 3: pick the preset (the brand) |
## Directory layout
@@ -5,7 +5,7 @@
// ship standalone: installed via `npx skills add`, a skill's scripts can't reach
// the monorepo's core package, and the core export points at .ts source that
// `node` (which runs these scripts) can't load. CANONICAL contract = the core
// parser + skills/hyperframes-core/references/storyboard-format.md; keep this in
// parser + skills/hyperframes/references/storyboard-format.md; keep this in
// lockstep. Behavior: never throws, accepts freeform narrative, recognizes
// Frame/Beat/Scene headings at H2/H3, preserves unknown keys verbatim under
// `extra` (keys lowercased). Pure node — no deps.
+14 -14
View File
@@ -13,7 +13,7 @@ Use this skill to ingest a GitHub pull request, understand the change, plan a co
> **The front door is `/hyperframes`.** You are the orchestrator. Run each step, verify its gate, and only then continue. This skill is for a **GitHub pull request** (a code change). Any other intent, a bare "make a video", or any uncertainty → read `/hyperframes` first — the intent layer owns every route decision, and a fresh creation arriving here without `BRIEF.md` goes through it anyway (Setup's opening rule).
You are the orchestrator. Work in the resolved external `PROJECT_DIR`, never in the caller repository by default. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes-core/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch a bounded pool of frame workers. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
You are the orchestrator. Work in the resolved external `PROJECT_DIR`, never in the caller repository by default. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch a bounded pool of frame workers. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
Workflow: Step 0 setup → `hyperframes.json`; Step 1 ingest → `capture/extracted/` + `assets/<login>.png`; Step 2 design system → `frame.md`; Step 3 storyboard/script → `STORYBOARD.md` and `SCRIPT.md`; Step 3.1 audio → `audio_meta.json`; Step 4 visual design → enriched `STORYBOARD.md`; Step 5 frames → `compositions/frames/NN-*.html` and `index.html`; Step 6 final render → `renders/video.mp4`.
@@ -46,7 +46,7 @@ Initialize only if `$PROJECT_DIR/hyperframes.json` is missing. Its basename come
Every relative-path command below runs with `$PROJECT_DIR` as its working directory. Examples without an explicit subshell mean `(cd "$PROJECT_DIR" && …)`; never change the caller repository's working tree.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes-core/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Show sign-in status before proceeding past Setup** — run `npx hyperframes auth status` and relay its output verbatim. It reports whether voice/BGM will use HeyGen or local engines and, when signed out, how to sign in. Apply one branch:
@@ -111,11 +111,11 @@ The script copies the code-editorial preset's `FRAME.md` → `frame.md`, remixes
Goal: Turn the PR into an approved frame-by-frame explanation plan.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes-core/references/storyboard-format.md`, and `../hyperframes-core/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes/references/storyboard-format.md`, and `../hyperframes/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Use `story-design.md` for the PR archetype (changelog / feature-reveal / fix-explainer / refactor-walkthrough), the PR-native frame types, hook, persuasion, beats, the per-frame word budget, and the credits close. The sequence comes from **narrative design, not the diff's file order** — explain the change, don't read the diff aloud. As a **soft guide**, consult the role→blueprint menu in `../hyperframes-animation/blueprints-index.md`: for each beat, write the voiceover in the shape its candidate blueprint implies and tag that candidate `blueprint:` id when one fits (story truth still decides which beats exist — never force a beat to fit a shape). Feature 2–4 real diff hunks (from `capture/diff.patch`), each a small legible snippet; name the `code-*` block each wants in the frame's `scene`. Frames carry no `asset_candidates` except the `credits` close (1–6 `assets/<login>.png` avatars). Use the exact required fields from the storyboard and script references.
After drafting, run the review loop's plan pass — `../hyperframes-core/references/review-loop.md` § 1: open the board (don't ask whether to — run the preview from `PROJECT_DIR` in the background), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
After drafting, run the review loop's plan pass — `../hyperframes/references/review-loop.md` § 1: open the board (don't ask whether to — run the preview from `PROJECT_DIR` in the background), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
**Gate:** `STORYBOARD.md` exists, every frame has the required narrative fields, `SCRIPT.md` exists when narration is needed, and the user approved the plan (autonomous: the summary was posted as a heads-up).
@@ -145,7 +145,7 @@ If there is no narration and no `SCRIPT.md`, skip voice generation. BGM may stil
Goal: Add the visual direction, layout intent, and motion choices to each storyboard frame.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes-core/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Stand-ins: for a **code beat**, a plain code panel with the filename and a few real diff lines as text — the `code-*` block wiring belongs to the workers. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Stand-ins: for a **code beat**, a plain code panel with the filename and a few real diff lines as text — the `code-*` block wiring belongs to the workers. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
Edit `STORYBOARD.md` in place. Do not create another storyboard. Use `frame.md` as source of truth for color, type, layout feel, and style.
@@ -177,17 +177,17 @@ Duration sync is mechanical: real voice duration wins; silent frames keep estima
`for b in <each registry block named in the storyboard>; do npx hyperframes add "$b"; done`
Before dispatch, read `../hyperframes-core/references/subagent-dispatch.md`. Build bounded packets and the worker role payload:
Before dispatch, read `../hyperframes/references/subagent-dispatch.md`. Build bounded packets and the worker role payload:
```bash
node <SKILL_DIR>/scripts/frame-packets.mjs --project "$PROJECT_DIR" --storyboard "$PROJECT_DIR/STORYBOARD.md"
```
The packet builder hard-fails a code frame without the upstream-selected `### Source excerpt`, and hard-caps packet bytes. It also writes `_role.md` (`../hyperframes-core/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch **at most three workers total**, balanced across the packet paths; each worker's prompt carries `_role.md` and its assigned packet paths — paste the role in full or hand its path (equivalent; the worker starts from exactly those documents) — and each worker may build multiple assigned frames sequentially, reading the role once. Workers read only their packet(s) and `frame.md`. They never open the full `STORYBOARD.md`, `capture/diff.patch`, or `capture/extracted/visible-text.txt`. Each worker writes only its assigned `compositions/frames/NN-*.html`; workers never edit `STORYBOARD.md`. When a frame has a **confirmed sketch** on disk (collaborative runs — review loop § 3), say so in that worker's dispatch context: the sketch is the existing `compositions/frames/NN-*.html`, and the worker dresses that layout rather than redrawing it (frame-worker core § When a confirmed sketch exists).
The packet builder hard-fails a code frame without the upstream-selected `### Source excerpt`, and hard-caps packet bytes. It also writes `_role.md` (`../hyperframes/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch **at most three workers total**, balanced across the packet paths; each worker's prompt carries `_role.md` and its assigned packet paths — paste the role in full or hand its path (equivalent; the worker starts from exactly those documents) — and each worker may build multiple assigned frames sequentially, reading the role once. Workers read only their packet(s) and `frame.md`. They never open the full `STORYBOARD.md`, `capture/diff.patch`, or `capture/extracted/visible-text.txt`. Each worker writes only its assigned `compositions/frames/NN-*.html`; workers never edit `STORYBOARD.md`. When a frame has a **confirmed sketch** on disk (collaborative runs — review loop § 3), say so in that worker's dispatch context: the sketch is the existing `compositions/frames/NN-*.html`, and the worker dresses that layout rather than redrawing it (frame-worker core § When a confirmed sketch exists).
On a failed frame, re-dispatch **that frame only**, with its existing packet plus the exact validator/lint finding. One retry maximum. Do not replay a whole batch and do not retry without a concrete finding.
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes-core/references/frame-worker-core.md`.)
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes/references/frame-worker-core.md`.)
As each worker returns, mark that frame `animated` in `STORYBOARD.md`.
@@ -225,7 +225,7 @@ If a command fails, surface stderr and stop — don't pile on recovery commands.
**Known false-positive — do not chase it.** `check` may report a handful of `text_box_overflow` errors of ~1–4px on the **caption** highlight words (selector `#caption-word-*` / `.caption-line`). The caption pill uses a deliberately snug `line-height` (set once in `scripts/captions.mjs`) and has **no `overflow:hidden`**, so a heavy display glyph's ink spills a few px into the pill's own padding — nothing is actually clipped. Treat these as expected and proceed. Do **not** inflate the caption `line-height` (it balloons the pill, which is worse). Only act on a `text_box_overflow` when it names a **frame** element (`#el-NN-*`), not a caption word.
After checks pass, pause for user review — the review loop's final look (`../hyperframes-core/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render — open the preview with the command below on a yes.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
After checks pass, pause for user review — the review loop's final look (`../hyperframes/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render — open the preview with the command below on a yes.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
Preview: `npx hyperframes preview "$PROJECT_DIR" --background`
@@ -253,19 +253,19 @@ The reusable, domain-agnostic shot shapes live in `../hyperframes-animation/blue
| Read | When |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `[../hyperframes-core/references/brief-contract.md](../hyperframes-core/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes/references/brief-contract.md](../hyperframes/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes-creative/references/story-spine.md](../hyperframes-creative/references/story-spine.md)` | Step 3: story doctrine — hook language, value-before-evidence, proposal shape, source-traceable visuals. |
| `[references/story-design.md](references/story-design.md)` | Step 3: plan the PR explanation. |
| `[../hyperframes-animation/blueprints-index.md](../hyperframes-animation/blueprints-index.md)` | Step 3: role→blueprint menu. Step 4: pick the shot shape. |
| `[../hyperframes-core/references/storyboard-format.md](../hyperframes-core/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes-core/references/script-format.md](../hyperframes-core/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../hyperframes/references/storyboard-format.md](../hyperframes/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes/references/script-format.md](../hyperframes/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../media-use/audio/references/tts.md](../media-use/audio/references/tts.md)` | Step 3.1: choose or understand TTS providers. |
| `[references/visual-design.md](references/visual-design.md)` | Step 4: write the frame's shot sequence (+ Layout vocabulary). |
| `[references/code-vocabulary.md](references/code-vocabulary.md)` | Step 4 + 5: pick + fill the `code-*` block for a code beat. |
| `[references/motion-language.md](references/motion-language.md)` | Step 4: the motion vocabulary + the motion doctrine. |
| `[references/cut-catalog.md](references/cut-catalog.md)` | Step 4-5: the cut catalog (worker builds within-frame seams). |
| `[../hyperframes-animation/rules-index.md](../hyperframes-animation/rules-index.md)` + `[../hyperframes-animation/rules/](../hyperframes-animation/rules/)` | Step 5: local rule recipe bodies for the cited motions. |
| `[../hyperframes-core/references/frame-worker-core.md](../hyperframes-core/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[../hyperframes/references/frame-worker-core.md](../hyperframes/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[sub-agents/frame-worker.md](sub-agents/frame-worker.md)` | Step 5: the workflow's frame-worker delta. |
| `[../hyperframes-core/references/subagent-dispatch.md](../hyperframes-core/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
| `[../hyperframes/references/subagent-dispatch.md](../hyperframes/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
| `[../hyperframes-creative/frame-presets/code-editorial/FRAME.md](../hyperframes-creative/frame-presets/code-editorial/FRAME.md)` | Step 2: the code-editorial preset (fixed style). |
@@ -2,7 +2,7 @@
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`.
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/references/storyboard-format.md` and `../hyperframes/references/script-format.md`.
## Read first
+4 -4
View File
@@ -1,15 +1,15 @@
#!/usr/bin/env node
// Thin wrapper over the shared packet builder in hyperframes-core — this file pins
// Thin wrapper over the shared packet builder in hyperframes — this file pins
// this workflow's paths plus its two behavioral differences: a code frame must carry
// an upstream-selected `### Source excerpt`, and code frames get a code-vocabulary
// excerpt appended to their packet. Everything else has one owner:
// ../../hyperframes-core/scripts/lib/frame-packets-core.mjs
// ../../hyperframes/scripts/lib/frame-packets-core.mjs
import { existsSync, readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as core from "../../hyperframes-core/scripts/lib/frame-packets-core.mjs";
import * as core from "../../hyperframes/scripts/lib/frame-packets-core.mjs";
const SKILL_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -45,7 +45,7 @@ function codeVocabularySection(block) {
const CONFIG = {
animationDir: resolve(SKILL_DIR, "../hyperframes-animation"),
corePath: resolve(SKILL_DIR, "../hyperframes-core/references/frame-worker-core.md"),
corePath: resolve(SKILL_DIR, "../hyperframes/references/frame-worker-core.md"),
deltaPath: resolve(SKILL_DIR, "sub-agents/frame-worker.md"),
validateFrame,
extraSections: codeVocabularySection,
@@ -5,7 +5,7 @@
// ship standalone: installed via `npx skills add`, a skill's scripts can't reach
// the monorepo's core package, and the core export points at .ts source that
// `node` (which runs these scripts) can't load. CANONICAL contract = the core
// parser + skills/hyperframes-core/references/storyboard-format.md; keep this in
// parser + skills/hyperframes/references/storyboard-format.md; keep this in
// lockstep. Behavior: never throws, accepts freeform narrative, recognizes
// Frame/Beat/Scene headings at H2/H3, preserves unknown keys verbatim under
// `extra` (keys lowercased). Pure node — no deps.
@@ -1,6 +1,6 @@
# Frame worker — PR-to-video delta
> The shared law is the core contract above (the packet builder prepends `../hyperframes-core/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a PR-to-video frame.
> The shared law is the core contract above (the packet builder prepends `../hyperframes/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a PR-to-video frame.
## Batch dispatch — you build a small packet batch
+14 -14
View File
@@ -15,7 +15,7 @@ Use this skill to capture a product, understand its brand, plan a launch video,
> **The front door is `/hyperframes`.** You are the orchestrator. Run each step, verify its gate, and only then continue to the next step. This skill is for a **product being marketed, launched, promoted, or revealed**, including requests such as "promo for our site" when the purpose is promotional. A site tour / showcase ask stays here too: `BRIEF.md` carries the show-it-as-is intent, and the captured screens become the assets the video features. Any other intent, a bare "make a video", or any uncertainty → read `/hyperframes` first — the intent layer owns every route decision, and a fresh creation arriving here without `BRIEF.md` goes through it anyway (Setup's opening rule).
You are the orchestrator. Work in `videos/<project>/`. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes-core/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch one sub-agent per frame. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
You are the orchestrator. Work in `videos/<project>/`. Run steps in order and pass each gate before continuing. User-gated steps are Step 0, Step 3, and Step 6. Read `../hyperframes/references/brief-contract.md` before Step 0 — it defines the gate types and how `BRIEF.md`'s `flow`/`storyboard` derive the mode that governs the Step 3/4/6 gates. Do every step yourself except Step 5, where you dispatch one sub-agent per frame. Do not put design or motion rules here; those live in the frame-worker sub-agent, this skill's local `../hyperframes-animation/rules/` + `../hyperframes-animation/blueprints/`, and `hyperframes-creative`.
Workflow: Step 0 setup -> `hyperframes.json`; Step 1 capture -> `capture/`; Step 2 design system -> `frame.md`; Step 3 storyboard/script -> `STORYBOARD.md` and `SCRIPT.md`; Step 3.1 audio -> `audio_meta.json`; Step 4 visual design -> enriched `STORYBOARD.md`; Step 5 frames -> `compositions/frames/NN-*.html` and `index.html`; Step 6 final render -> `renders/video.mp4`.
@@ -33,7 +33,7 @@ Initialize only if `hyperframes.json` is missing. Name `<project>` from the bran
After init, let `<PROJECT_ROOT>` be `videos/<project>` and run every subsequent relative-path command with that directory as its working directory. In the commands below, `.` means `<PROJECT_ROOT>`; never write `.media`, `capture`, or output files in the caller directory.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes-core/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Write `BRIEF.md` immediately after init** (never before — `init` refuses a non-empty directory): the intent layer's locked brief, shape per `../hyperframes/references/brief-format.md`. Resolve `<MEDIA_DIR>` as the installed `/media-use` skill directory. Then record each preference-backed answer with `node <MEDIA_DIR>/scripts/prefs.mjs record --hyperframes .` (`brief-format.md` names the subset). If the intent layer adopted a recipe, run `node <MEDIA_DIR>/scripts/recipe.mjs use --hyperframes . --name <name>`; it copies its `frame.md` into the project (Step 2 is then skipped) and returns the skeletons Step 3 drafts from. A recipe fills answers, not approvals; the review gates still run.
**Show sign-in status before proceeding past Setup** — run `npx hyperframes auth status` and relay its output verbatim. It reports whether voice/BGM will use HeyGen or local engines and, when signed out, how to sign in. Note the exit code contract: `auth status` **exits 1 when not signed in** (and when the stored credential is rejected) — that non-zero exit is the normal signed-out state, not a command failure, so don't treat it as an error, don't retry it, and don't chain it with `&&`/`set -e` in a way that would abort the workflow. Apply one branch:
@@ -106,11 +106,11 @@ The script does the rest deterministically: copies the preset's `FRAME.md` → `
Goal: Turn the brief and captured material into an approved frame-by-frame story plan.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes-core/references/storyboard-format.md`, and `../hyperframes-core/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Read `../hyperframes-creative/references/story-spine.md` (hook language, value-before-evidence, storyboard-as-proposal, source-traceable visuals), `references/story-design.md`, `../hyperframes-animation/blueprints-index.md`, `../hyperframes/references/storyboard-format.md`, and `../hyperframes/references/script-format.md`. Use them to write `STORYBOARD.md` and, when narration is needed, `SCRIPT.md`. Set the frontmatter `duration:` from the brief's `length` — a rough expectation; assembly reports where the cut lands against it.
Use `story-design.md` for story blueprint, hook, persuasion logic, beats, `VO_MODE`, and asset choices. As a **soft guide**, consult the role→blueprint menu in `../hyperframes-animation/blueprints-index.md`: for each beat, note a candidate blueprint id when one fits. Story truth still decides which beats exist — never force a beat to fit a blueprint, and never invent a beat just because a proven shape is available. Choose each visual frame's `asset_candidates` from `capture/extracted/asset-descriptions.md` (the canonical inventory) — don't browse raw `capture/assets/`. Do not ask the user to pick assets unless that inventory is missing or unusable. Use the exact required fields from the storyboard and script references.
After drafting, run the review loop's plan pass — `../hyperframes-core/references/review-loop.md` § 1: open the board (don't ask whether to), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
After drafting, run the review loop's plan pass — `../hyperframes/references/review-loop.md` § 1: open the board (don't ask whether to), present the plan as a proposal, and ask the two questions — approve or change, and **sketches first** (recommended) or skip. Feedback loops through chat or the board's comments file until approved. This is a **checkpoint gate** (brief contract § 1): in autonomous mode there is no board and nothing to ask — post the same summary as a heads-up and proceed; sketches collapse into the build, and the one preview question comes at Step 6.
**Gate:** `STORYBOARD.md` exists, every visual frame has `asset_candidates`, `SCRIPT.md` exists when narration is needed, and the user approved the frame-by-frame plan (autonomous: the summary was posted as a heads-up).
@@ -140,7 +140,7 @@ If there is no narration and no `SCRIPT.md`, skip voice generation. BGM may stil
Goal: Add the visual direction, layout intent, and motion choices to each storyboard frame.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes-core/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Stand-ins: plain labeled blocks for the captured assets — the real files arrive with Step 5's workers. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
**Sketch the board first (collaborative only).** The moment the plan is approved, run the sketch pass — `../hyperframes/references/review-loop.md` § 2 (don't wait on Step 3.1; sketches don't use timings): wireframe every frame yourself, mark each `built`, pause for the one layout question when the board is full, and revise only the sketches named until the board is confirmed. Stand-ins: plain labeled blocks for the captured assets — the real files arrive with Step 5's workers. Only then write the visual design below onto the confirmed layouts. In autonomous mode, or when the user chose to skip sketches at Step 3, skip this pass — frames go straight from `outline` to `animated` at Step 5.
Edit `STORYBOARD.md` in place. Do not create another storyboard. Use `frame.md` as source of truth for color, type, layout feel, and style.
@@ -176,15 +176,15 @@ Duration sync is mechanical: real voice duration wins; silent frames keep estima
Check the music against the final cut before assembly. A library track can match the requested mood but open on a quiet build that drains the first seconds of a short launch video. Compare the opening with later five-second sections; when a later section has a stronger, musically clean start, trim from there and keep a short fade-in plus a longer fade-out. If frame or narration timing changes, redo this check against the new final duration so the music never ends early or leaves silence at the tail.
Before dispatch, read `../hyperframes-core/references/subagent-dispatch.md`. Build the per-frame packets and the worker role payload:
Before dispatch, read `../hyperframes/references/subagent-dispatch.md`. Build the per-frame packets and the worker role payload:
`node <SKILL_DIR>/scripts/frame-packets.mjs --project "$PROJECT_DIR" --storyboard "$PROJECT_DIR/STORYBOARD.md"`
The builder writes one bounded packet per frame under `.hyperframes/frame-packets/` (the frame's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes-core/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch one sub-agent per frame, in parallel if possible; otherwise run workers in waves. Each worker gets exactly one frame: its prompt carries `_role.md` and that frame's packet — paste both in full, or hand the two file paths for the worker to read first (equivalent; the worker starts from exactly those two documents either way) — plus a dispatch context with `PROJECT_DIR`, `frame_id`, whether the frame has a **confirmed sketch** on disk (the worker dresses that layout rather than redrawing it — frame-worker core § When a confirmed sketch exists), canvas size, and caption status + keep-out band if captions are enabled.
The builder writes one bounded packet per frame under `.hyperframes/frame-packets/` (the frame's exact storyboard block + the blueprint body + every cited rule recipe, inlined) and `_role.md` (`../hyperframes/references/frame-worker-core.md` + this skill's `sub-agents/frame-worker.md`, concatenated verbatim — the complete worker role). Dispatch one sub-agent per frame, in parallel if possible; otherwise run workers in waves. Each worker gets exactly one frame: its prompt carries `_role.md` and that frame's packet — paste both in full, or hand the two file paths for the worker to read first (equivalent; the worker starts from exactly those two documents either way) — plus a dispatch context with `PROJECT_DIR`, `frame_id`, whether the frame has a **confirmed sketch** on disk (the worker dresses that layout rather than redrawing it — frame-worker core § When a confirmed sketch exists), canvas size, and caption status + keep-out band if captions are enabled.
Workers read only their packet and `frame.md`; they never open `STORYBOARD.md` or the skill documents (the packet inlines what was selected upstream). Each worker writes only `compositions/frames/NN-*.html`. Workers must never edit `STORYBOARD.md`.
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes-core/references/frame-worker-core.md`.)
**Full-bleed backgrounds ride on a `class="clip"` layer, never the `#root`.** A frame's ground (color field / gradient / grid) is its own full-duration background clip — a `background` set on the `#root` / `data-composition-id` element is clip-gated to the frame's window and is not a dependable ground, so dark content can land on the black host `body` and render invisible. The video's base ground is painted by the assembler from `frame.md`'s `canvas` color onto the index `#root`. (Full rule + self-check: `../hyperframes/references/frame-worker-core.md`.)
As each worker returns, the orchestrator marks that frame as `animated` in `STORYBOARD.md`.
@@ -220,7 +220,7 @@ Inject transitions, run checks, pause for review, then render.
If a command fails, surface stderr and stop — don't pile on recovery commands. Fix it yourself: the cheapest safe edit to `compositions/frames/NN-*.html`, then rerun the failed check.
After checks pass, pause for user review — the review loop's final look (`../hyperframes-core/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
After checks pass, pause for user review — the review loop's final look (`../hyperframes/references/review-loop.md` § 4): one question, on the Studio that has been open since Step 3 — render now, or what changes? (Autonomous: the one kept question, preview first or render.) Then deliver the MP4 with the contact sheet and the frame ids so revisions can target a single frame.
Preview: `npx hyperframes preview --background`
@@ -244,19 +244,19 @@ The reusable, product-agnostic shot shapes live in `../hyperframes-animation/blu
| Read | When |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `[../hyperframes-core/references/brief-contract.md](../hyperframes-core/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes/references/brief-contract.md](../hyperframes/references/brief-contract.md)` | Gate types, mode derivation from `BRIEF.md`, field semantics. |
| `[../hyperframes-creative/references/story-spine.md](../hyperframes-creative/references/story-spine.md)` | Step 3: story doctrine — hook language, value-before-evidence, proposal shape, source-traceable visuals. |
| `[../hyperframes-creative/frame-presets/](../hyperframes-creative/frame-presets/)` | Step 2: choose and adopt a frame preset. |
| `[../hyperframes-creative/references/design-spec.md](../hyperframes-creative/references/design-spec.md)` | Step 2: apply brand tokens correctly. |
| `[references/story-design.md](references/story-design.md)` | Step 3: plan the product-launch story. |
| `[../hyperframes-animation/blueprints-index.md](../hyperframes-animation/blueprints-index.md)` | Step 3: role→blueprint menu. Step 4: pick the shot shape. |
| `[../hyperframes-core/references/storyboard-format.md](../hyperframes-core/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes-core/references/script-format.md](../hyperframes-core/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../hyperframes/references/storyboard-format.md](../hyperframes/references/storyboard-format.md)` | Step 3: write `STORYBOARD.md`. |
| `[../hyperframes/references/script-format.md](../hyperframes/references/script-format.md)` | Step 3: write `SCRIPT.md`. |
| `[../media-use/audio/references/tts.md](../media-use/audio/references/tts.md)` | Step 3.1: choose or understand TTS providers and voices. |
| `[references/visual-design.md](references/visual-design.md)` | Step 4: write the frame's shot sequence (+ Layout vocabulary). |
| `[references/motion-language.md](references/motion-language.md)` | Step 4: the motion vocabulary + the motion doctrine. |
| `[references/cut-catalog.md](references/cut-catalog.md)` | Step 4-5: the cut catalog (worker builds within-frame seams). |
| `[../hyperframes-animation/rules-index.md](../hyperframes-animation/rules-index.md)` + `[../hyperframes-animation/rules/](../hyperframes-animation/rules/)` | Step 5: local rule recipe bodies for the cited motions. |
| `[../hyperframes-core/references/frame-worker-core.md](../hyperframes-core/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[../hyperframes/references/frame-worker-core.md](../hyperframes/references/frame-worker-core.md)` | Step 5: the shared worker contract (packet builder prepends it to the delta). |
| `[sub-agents/frame-worker.md](sub-agents/frame-worker.md)` | Step 5: the workflow's frame-worker delta. |
| `[../hyperframes-core/references/subagent-dispatch.md](../hyperframes-core/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
| `[../hyperframes/references/subagent-dispatch.md](../hyperframes/references/subagent-dispatch.md)` | Step 5: dispatch sub-agents safely. |
@@ -2,7 +2,7 @@
Step 3 of the product-launch flow. Output: `STORYBOARD.md` (the narrative plan, one frame per beat) and `SCRIPT.md` (the locked spoken narration).
This step decides **what the video says, in what order, and how each beat is said** — and it says each beat in the SHAPE of a proven script. It does not design layout, composition, or motion (that is Step 4). For exact file syntax follow `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/script-format.md`.
This step decides **what the video says, in what order, and how each beat is said** — and it says each beat in the SHAPE of a proven script. It does not design layout, composition, or motion (that is Step 4). For exact file syntax follow `../hyperframes/references/storyboard-format.md` and `../hyperframes/references/script-format.md`.
## What story design produces
@@ -1,18 +1,18 @@
#!/usr/bin/env node
// Thin wrapper over the shared packet builder in hyperframes-core — this file only
// Thin wrapper over the shared packet builder in hyperframes — this file only
// pins the paths that are specific to this workflow skill. The logic (frame
// splitting, rule citation, packet bounds, `_role.md` assembly) has one owner:
// ../../hyperframes-core/scripts/lib/frame-packets-core.mjs
// ../../hyperframes/scripts/lib/frame-packets-core.mjs
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as core from "../../hyperframes-core/scripts/lib/frame-packets-core.mjs";
import * as core from "../../hyperframes/scripts/lib/frame-packets-core.mjs";
const SKILL_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const CONFIG = {
animationDir: resolve(SKILL_DIR, "../hyperframes-animation"),
corePath: resolve(SKILL_DIR, "../hyperframes-core/references/frame-worker-core.md"),
corePath: resolve(SKILL_DIR, "../hyperframes/references/frame-worker-core.md"),
deltaPath: resolve(SKILL_DIR, "sub-agents/frame-worker.md"),
};
@@ -5,7 +5,7 @@
// ship standalone: installed via `npx skills add`, a skill's scripts can't reach
// the monorepo's core package, and the core export points at .ts source that
// `node` (which runs these scripts) can't load. CANONICAL contract = the core
// parser + skills/hyperframes-core/references/storyboard-format.md; keep this in
// parser + skills/hyperframes/references/storyboard-format.md; keep this in
// lockstep. Behavior: never throws, accepts freeform narrative, recognizes
// Frame/Beat/Scene headings at H2/H3, preserves unknown keys verbatim under
// `extra` (keys lowercased). Pure node — no deps.
@@ -1,6 +1,6 @@
# Frame worker — product-launch delta
> The shared law is the core contract above (the packet builder prepends `../hyperframes-core/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a product-launch frame; you run N-up, **one frame each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
> The shared law is the core contract above (the packet builder prepends `../hyperframes/references/frame-worker-core.md` to this file as `_role.md`) — read the two as one role. This file carries only what's specific to a product-launch frame; you run N-up, **one frame each** — your dispatch carries exactly one packet. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in the core contract or `hyperframes-core`.
## Your `focal:` / `roles:` — real captured media
+1 -1
View File
@@ -37,7 +37,7 @@ Then ask a short confirmation question:
Use a yes/no choice UI when the environment provides one; otherwise ask the question in plain text.
Do not implement the slideshow until the user says yes. If they say no, stop using this skill — read `/hyperframes` and let the intent layer re-route. This confirmation is a **routing decision**, not a preference gate — per `../hyperframes-core/references/brief-contract.md` § 1 it survives autonomous mode ("surprise me" does not skip it): building the wrong deliverable type is a quality failure, not a creative call.
Do not implement the slideshow until the user says yes. If they say no, stop using this skill — read `/hyperframes` and let the intent layer re-route. This confirmation is a **routing decision**, not a preference gate — per `../hyperframes/references/brief-contract.md` § 1 it survives autonomous mode ("surprise me" does not skip it): building the wrong deliverable type is a quality failure, not a creative call.
---
+1 -1
View File
@@ -298,7 +298,7 @@ Rules that apply to every channel:
- If the user has already pre-approved defaults ("just use defaults",
"no need to ask", "auto-pick everything"), asked you not to ask, or the
run carries an ongoing autonomous signal ("surprise me" / "decide for me" —
`../hyperframes-core/references/brief-contract.md` § 1) — **skip
`../hyperframes/references/brief-contract.md` § 1) — **skip
the question entirely** and use: `recommendedRatio`, `layout="stack"`
(safest cross-ratio default), `style` chosen from transcript tone in
the most neutral group (editorial/data), `autoCount`. Tell the user