Purpose
Every video project's storyboard.html is the single review surface — the operator opens it once and sees the latest state of every gate (idea brief → lyric/script → keyframes → animations → master). Per STORYBOARD.md at the repo root and the music-video-ad molecule's doctrine, all assets/handoffs/review notes land in this one file.
Hand-authoring the HTML across many states drifts. This atom replaces that with a fragment + render contract: each state writes a small JSON fragment under <video_folder>/_state/NN-<slug>.json, and scripts/render_storyboard.py collates them into the final HTML.
When to use
- Initializing a new ad — write a State 0 fragment and render the shell so the operator has a URL to bookmark from day one.
- After every state advance — append the new state's fragment, re-render. The pipeline already runs
update-storyboardas a post-step inmusic-video-adandcreate-cartoon-music-video; this atom is what those post-steps call. - Reviewing —
open <video_folder>/storyboard.html.
Use the legacy hand-authored route only if the project is one-off or the fragment kinds don't cover the format. For music-video, cartoon-music-video, comic-strip, and most narrator-driven ads the fragment kinds below are sufficient.
Fragment schema
<video_folder>/_state/<order-prefix>-<slug>.json. The order prefix decides where the section lands in the rendered HTML — use the orchestrator's state numbering (1-, 2-, 2.5-, 2.6-, 3-, 3.5-, 5-, 5.5-, 7-, 7.4-, 7.5-, 8-, 9-, 10-).
{
"state": "5.5-animate", // section id (used in #anchor)
"label": "State 5.5 · Animate", // human header
"summary": "17 clips, Seedance 2.0 with Veo fallback on 4.",
"kind": "scenes", // one of: html | lyrics | scenes | metrics | video | audio | image
"scenes": [
{
"id": "scene-01", "bar": 1, "timecode": "00:00–00:03.3",
"lyric": "what's the move",
"keyframe": "generated/keyframes/scene-01.png",
"animated": "generated/animated/scene-01.mp4",
"prompt": "the felt amigurumi woman LIFTS her phone, slow push-in",
"notes": ""
}
]
}
Other kinds:
| kind | required fields | renders as |
|---|---|---|
html |
html (trusted raw body) |
drop-in HTML block |
lyrics |
lyrics: ["line", …] (+ optional accent_words) |
serif lyric block, accent words italic-bold in the accent color |
scenes |
scenes: [{id, lyric, keyframe, animated?, prompt?, notes?, timecode}] |
responsive grid; auto-swaps animated→keyframe if mp4 missing |
metrics |
metrics: [{label, value}] |
small two-column key/value table |
video |
video: <relpath> (+ optional video_caption) |
inline 9:16 controlled <video> |
audio |
audio: <relpath> (+ optional audio_caption) |
inline <audio controls> |
image |
image: <relpath> (+ optional image_caption) |
inline <img> |
Missing assets render as a labeled orange placeholder so the gap is visible. Asset paths are relative to <video_folder>.
Workflow
- State N produces its artifacts (lyrics file, keyframe PNGs, master mp4, etc.).
- State N writes (or rewrites)
<video_folder>/_state/<order>-<slug>.jsonreflecting its current artifacts. - State N calls:
python3 skills/atoms/planning/create-storyboard-html/scripts/render_storyboard.py \ --video-folder <video_folder> \ [--title "Brand · Format"] \ [--accent-color "#0052FF"] - Storyboard is now current. State N + 1 repeats the cycle.
Output
<video_folder>/storyboard.html- A TOC of state sections at the top.
- One
<section>per fragment, ordered by the fragment filename prefix.
Quality Checks
- Every fragment that references an asset path resolves to a real file under
<video_folder>(otherwise the page renders an explicit "missing" placeholder, which is the loud signal). - The fragment filename's order prefix matches an orchestrator state number — drift means the section sorts out of order.
- The page opens in a browser without a server (relative paths, no external CDN).
_state/*.jsonfiles are valid JSON (the renderer skips malformed ones with a warning, but they should never ship malformed).
Failure Modes
- A state appends a fragment but forgets to re-run the renderer — the storyboard is stale. The orchestrator's
update-storyboardpost-step exists to prevent this. - Two fragments share the same order prefix — they sort lexicographically within the prefix; pick distinct slugs.
- Asset paths leak
..outside the project — paths are not sandboxed; trust the pipeline that writes the fragments. - Operator hand-edits
storyboard.htmland gets stomped on the next render — never hand-edit; only fragments are durable. If you need a one-off HTML insert, write akind:"html"fragment.
Composition
This atom is wrapped by:
molecules/music-video-ad— runsrender_storyboard.pyafter every state.molecules/create-cartoon-music-video— same.video-orchestrator/create-storyboard— the standalone storyboard-only entrypoint for non-molecule runs.
The legacy free-form storyboard.html hand-writing pattern is deprecated in favor of fragments. For per-scene keyframe-only review pages (not the full single-review-surface), use atoms/planning/render-keyframe-storyboard — that atom has a different schema scoped to one phase.