Turn a weekly changelog .md into a finished branded changelog video (square 1080, ~45-60s, Annie VO, animated brand background, mock-UI visualizations, lowkey captions). Use when the user provides a changelog/digest markdown and wants the weekly video, or says "changelog video". Self-contained — fonts, background, lexicon, and scripts ship in this skill.
70
86%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Input: a changelog .md (themes + items, like the weekly HyperFrames digest).
Output: a lint-clean, seam-gate-green HyperFrames project in
projects/active/weekly-changelog-<range>/. Render only when asked.
Load first, non-negotiable: motion-doctrine (+ cut-the-curve,
oversized-cursor if a cursor appears, seam-craft) and captions-overlay.
This skill supplies the changelog-specific pipeline; the doctrine supplies the
motion law.
Every theme is illustrated by an animated mock of the actual UI or a
faithful analog acting out the change in experience — never text bullets.
Route every theme/item through references/visualization-registry.md BEFORE
writing the script; the registry decides ui-recreate / ui-analog / terminal /
checklist. Text checklist is the LAST resort, reserved for genuinely
non-visual items (reliability fix lists).
Do this before writing any composition HTML. Skipping it always produces a video that looks like a similar project you built before, NOT this skill's brand — that's the single most common way this skill goes off-brand. The skill's assets, fonts, and scaffold are the skill; the SKILL.md prompt is a router.
mkdir -p project/assets/fonts
cp <SKILL_DIR>/assets/fonts/*.woff2 project/assets/fonts/
cp <SKILL_DIR>/assets/bgm.mp3 project/bgm.mp3
ffmpeg -y -stream_loop 15 -i <SKILL_DIR>/assets/bg-pattern.mp4 -t <TOTAL> \
-vf "scale=1080:1080,fps=30,eq=saturation=0.72,drawbox=c=black@0.5:t=fill" \
-an -c:v libx264 -crf 20 -pix_fmt yuv420p project/assets/bg-pattern-<TOTAL>s.mp4
cp <SKILL_DIR>/examples/master-skeleton.html project/index.htmlThen read references/build-spec.md end-to-end (not skimmed) — it defines the brand tokens (TT Norms Pro + ABC Solar Display + TT Norms Mono, cream #f5f6f4, rationed green #5ef17c, glass cards with green-tinted borders, kicker/sec-chip pill shape, 32px caption rail at top: 990) that every scene inherits from the scaffold.
Only THEN begin steps 1-6 below. Steps 1-4 (parse, route, script, VO) plan what goes into the scaffold; step 5 fills placeholders (<RANGE>, <TOTAL>, <CUT_N>, <DUR_N>, scene bodies) inside the already-copied project/index.html — you do NOT rewrite the scaffold's chrome, fonts, palette, or layout shell.
If you catch yourself reaching for cp on a prior video's index.html, or writing your own @font-face declarations, or designing a WebGL shader background instead of using the encoded bg-pattern MP4 above: STOP. Delete the current index.html and restart at the cp of the master-skeleton scaffold. Rebuilding scene content on the right scaffold is cheaper than retrofitting brand into the wrong scaffold.
For each theme, pick the surface from references/visualization-registry.md
and write one line: theme → surface → the 2-4 sequenced actions the mock performs, each tied to a script phrase. If no registry surface fits and no
faithful analog exists, it's a checklist scene — don't invent fake UI for
something we can't represent honestly.
Write the script as token lines per references/script-voice.md:
conversational register, every technical term carrying a spoken phonetic
form from references/lexicon.json while display keeps standard spelling.
Captions show display; the VO reads spoken. Any term not in the lexicon:
STOP and ask the user how it's pronounced, then add it to the lexicon.
Save as script-tokens.json in the project.
# spoken-layer text only; words JSON = ground-truth timestamps of the SPOKEN text
# Repo-native path: the changelog-video skill runs from the hyperframes repo root,
# so it uses the tracked hyperframes-media TTS helper directly (no `npx hyperframes
# skills` install step). If you've copied the skill into another repo, swap in
# your own path to the media-use / hyperframes-media heygen-tts.mjs.
node skills/hyperframes-media/scripts/heygen-tts.mjs ./vo-spoken.txt \
-o voiceover.mp3 --words vo-words.json \
--voice 330290724a1b470fb63153f34d4c0183 # Annie — lifelike (do not substitute)Requires heygen CLI ≥0.3.0 authenticated (heygen auth login --oauth).
Then align spoken timestamps back to display tokens:
node <SKILL_DIR>/scripts/align-captions.mjs \
--tokens script-tokens.json --words vo-words.json --out captions.jsoncaptions.json is the caption-rail input (display spelling, spoken timing).
The aligner prints MISMATCH warnings — resolve every one before building
(usually a lexicon spelling the TTS renders as multiple words). The audio
is the clock: all beat times come from vo-words.json; a VO regen re-opens
every seam.
Word-timings are a hard gate. Before moving on to step 5, verify
vo-words.json is non-empty and has a words: [...] array with start/end
per word. If it's empty (0 bytes) or missing the array — a known failure mode
when the TTS provider returns audio but no timestamp payload — DO NOT proceed
without them. Fallback: forced-align the produced audio against the display
script using local whisper:
uvx --from openai-whisper whisper voiceover.mp3 \
--model base.en --language en --word_timestamps True \
--output_format json --output_dir .
# then run align-captions.mjs with --words voiceover.json (same shape)Whisper mishears TTS renderings ("gee-sap" → "gsap", "heyjen" → "hey Jen",
etc.) — captions still use the DISPLAY spelling from script-tokens.json;
whisper only supplies the timestamps. align-captions.mjs handles the join.
This fallback is the difference between a captioned build and a silently
uncaptioned one.
Follow references/build-spec.md exactly: brand tokens + fonts (bundled in
<SKILL_DIR>/assets/), the animated background encode, scene scaffold,
chrome, caption rail, one rationed green moment per scene. Then the doctrine
order: ledger.json (all ordinary seams cut-the-curve LEFT) → seam-stamp →
internal beats on VO words → seam-gate verify.
Captions are non-optional. The master-skeleton ships a caption-rail IIFE
that reads a LINES array — leaving that array empty is a shipped bug, not a
style choice. Populate it from captions.json before proceeding to step 6:
// paste in place of "const LINES = /* … */ []" in the caption-rail IIFE:
const LINES = /* contents of captions.json */ [
{ id: 0, end: 2.74, w: [["This", 0.0], ["week,", 0.30], …] },
…
];If align-captions.mjs was skipped or LINES is [], the frame check in
step 6 will fail — do not paper over it by removing #cap-line from the
scaffold.
bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98" (or the installed
hyperframes CLI from the repo-local skills/hyperframes-cli/ skill) —
0 errors (contrast: dim text ≥ .66 alpha; scene content stays above the
caption rail). Do NOT reach for
npx hyperframes@latest; the tracked repo-local CLI is the source of
truth for the composition contract this skill produces against.seam-gate.mjs verify — 0 fail.__player.seek on the raw comp page.ffmpeg -ss <t> … -frames:v 1): captions present,
background video not black, no tiny/frozen frames.t=3, t=15, t=30, t=42 for a 48s VO)
and confirm the caption rail at top: 990 renders visible text on each.
If any frame in a spoken interval is missing captions, the build ships
uncaptioned — treat it as a red gate and re-check step 5's LINES
population. This is exactly what went wrong on the Jul 13-20 v4 build.projects/active/weekly-changelog-<range>/
├── index.html # single-doc master (scenes as slides, stamped seams)
├── ledger.json # vector ledger (seam-stamp input)
├── script-tokens.json # two-layer script (source of truth for VO + captions)
├── vo-spoken.txt # generated: spoken layer, one line
├── voiceover.mp3 + vo-words.json + captions.json
├── bgm.mp3 # copy from <SKILL_DIR>/assets/bgm.mp3 (the house track) unless the user supplies one
└── assets/fonts/ + assets/bg-pattern-<dur>s.mp4| Don't | Instead |
|---|---|
| Bullet-point slides for UI changes | Mock the surface acting out the change |
| Fake UI for un-representable items | Honest checklist scene |
| Plain "JSON"/"CLI" in the TTS text | Lexicon spoken forms; display stays standard |
| Phonetic spellings in captions | Captions always render the display layer |
| Guessing an unknown term's pronunciation | Ask, then grow the lexicon |
| Speaking every changelog item | ≤3 per theme; the digest link carries the rest |
| Green accents everywhere | One green moment per scene (#5ef17c) |
| Starting from a prior video's index.html | Step 0 — copy examples/master-skeleton.html from this skill into project/index.html, always |
Hand-crafted @font-face / WebGL shader / custom BGM | Step 0 — copy this skill's assets/ verbatim; the skill's assets ARE the brand |
| Delivered without CloudFront invalidation | Run aws cloudfront create-invalidation on distribution E2BSLVSZ7FG3U0 for the exact path after any S3 replace — CDN caches the old file otherwise |
Shipping with the LINES array empty in the scaffold | Step 4 must produce a populated captions.json; step 5 must paste it into the IIFE; step 6 gate 5 must confirm captions on rendered frames. An empty LINES = uncaptioned ship = re-do the run |
No vo-words.json → skip captions and ship anyway | Fall back to whisper forced alignment on the produced audio; captions are non-optional |
a562946
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.