demo-studio · git:20260709.a31a432 · 2026-07-09 · sha256 ce14becf93dbfde2
demo-studio git:20260709.a31a432A
Immutable. This exact content never changes and is served at /api/v1/blob/ce14becf93dbfde2.
--- name: demo-studio description: > Use this at Step 2.5 — AFTER the orchestrator reports the build green, before the submission is filed — to produce the hackathon's demo video autonomously on the user's machine: it writes a scene-by-scene demo script from the brief/spec/event.md, records the built app scene-by-scene (Playwright screencast for browser scenes, ffmpeg gfxcapture/ddagrab for native/wallet-popup scenes), narrates with TTS and word-timed captions, assembles with Remotion, runs a frame-level QC loop, has the user WATCH the final cut, then — on a separate consent — uploads it and resolves the submission draft's demo-video URL. Trigger it ONLY on the user's ask: "make the demo video", "record the demo", "create the hackathon video", "do the demo studio", or the user starting Step 2.5 after the orchestrator's offer. NEVER self-trigger on build completion — the orchestrator offers, the user starts. It records under a foreground do-not-disturb session and never publishes without the user watching first. Do NOT use it to build (that's the orchestrator) or to run the faucet/deploy/signup gates (that's the gate-concierge). --- # Demo Studio — Step 2.5: the demo video, recorded and assembled autonomously ## What this is Every hackathon submission needs a 2–5 minute demo video, and hand-producing it — script, record, narrate, caption, edit, upload — costs hours at the worst possible time. The kit has automated everything up to here: the build is green, and the architect's `docs/gates/submission-draft.md` carries a `TBD-post-build: demo video URL` waiting to be filled. This seat fills it. It drives the built app through a scene-by-scene script, captures each scene, assembles a captioned narrated cut with Remotion, and — only after the user watches it and consents — uploads it. It is a **user-side seat** (like the guide and the gate-concierge): it runs on the user's machine with their granted tooling, it **never self-triggers**, and its two outward actions — running a foreground recording session and publishing the video — are **consented gates**. Quality comes from gates, not hope: every scene has a pass/fail assertion and a bounded retake budget, every render passes a frame-level QC loop, and **the user watches the final cut before anything is published**. **Announce at start:** "I'm using the demo-studio skill — I'll write the demo script for your approval, record the app scene-by-scene, assemble a captioned narrated cut, show it to you, and only upload it once you've watched it and said go." The operational ground truth — exact tool versions, the copy-paste ffmpeg/Playwright/Remotion commands, and the Windows bootstrap steps — lives in `../references/demo-stack.md`. **Read it before running anything**, and honor its freshness rule (re-verify the `[pin]`/`[policy]` rows before a real campaign; the toolchain moves weekly). Music lives in `../references/music-kit.md`. ## Where you sit Step 2.5, between the green build (Step 2) and the submission (Step 3's closing pass). Your input is a working app; your output is a video URL that resolves the submission draft's TBD. You read the brief, spec, `event.md`, and submission draft from the **project repo** (`../references/repo-bootstrap.md`); you **write** the demo script and the Remotion project code to the project repo (renders and captures stay git-ignored — see *Repo hygiene*), and you record your upload evidence in the **same gate ledger** the concierge uses (one ledger per campaign — extend the existing `docs/gates/*-gate-ledger.md`; **if none exists yet** — you can run before any concierge pass — create `docs/gates/<YYYY-MM-DD>-gate-ledger.md` in the concierge's format, and never start a second one). You do not extract, ideate, plan, build product code, or run the faucet/deploy/signup gates — those are other seats. Two consents are yours and only yours to ask for: **the recording session** (it takes over the foreground of the user's desktop — a do-not-disturb window) and **the upload** (publishing is irreversible-ish; watching the cut and consenting to upload are two separate human moments). ## Kickoff inputs (defaults are set; the user overrides per campaign) The kickoff (`../prompts/INVOKE_DEMO_STUDIO.md`) carries these; each has a working default so the seat runs without interrogation, and the user overrides any: - **VOICE** — default **Kokoro** (local, offline, zero-signup; English). `edge-tts` for nicer MS neural voices (network-dependent), or **ElevenLabs** for premium (its API key is a Tier-A concierge signup gate). - **LENGTH** — default the event's mandated length, else **~3:00**. - **MUSIC** — default **off** (captions-only; demos are often watched muted and the licensing surface is smaller). Opt in and pick a mood; tracks come from `../references/music-kit.md`. - **WALLET-ON-CAMERA** — default **real MetaMask via dappwright** (what judges expect; proves the interaction). Auto-swaps to the clean injected-signer + block-explorer-confirmation shot only on retake-budget exhaustion, or set it per-scene. - **UPLOAD** — default **unlisted YouTube** (consented browser upload), Drive fallback one flag away. This field is the standing authorization for the upload action class (see *Upload*). ## The stages ### 1. Script (committed, user-approved before any recording) Read the brief (the **demo money shot**), the spec, the pack's `event.md` (the **published judging criteria + weights**), and the submission draft. Write `docs/demo/<YYYY-MM-DD>-demo-script.md` — a scene table: `id · target seconds · visual (route/action/window) · narration line · capture driver (screencast | gfxcapture | ddagrab) · wallet verbs (abstract: connect / approveTx / switchNetwork) · gate assertion (what the final frame or on-chain state must show) · retake budget (default 3)`. **Allocate scene seconds by the judging weights** — if working-demo is 50%, most seconds go to the money shot, not the intro. Commit and push it, then **get the user's approval before recording**. A rubber-stamped script produces a drifting video, same as a rubber-stamped spec. ### 2. Storyboard preview Render the Remotion composition's **static frames** (title/section cards, caption layout, outro) as stills — real narration text, empty footage slots — and show them to the user. This is the cheap correction point: fix wording, ordering, and layout here, before spending capture time. ### 3. Capture (the foreground consented session) Recording takes over the desktop foreground — get the **recording-session consent** first, and tell the user it's a do-not-disturb window. Then, **scene-by-scene, sequentially** (one browser context at a time — see `demo-stack.md`): - **Browser scenes** — Playwright `page.screencast` (JPEG frames → ffmpeg two-pass H.264), 1920×1080 viewport, re-encoded to constant fps. Never `recordVideo` (banned — see `demo-stack.md`). - **Wallet scenes** — headed dappwright with the pinned/cached MetaMask; the **popup is a separate window** page-capture misses, so capture it with `gfxcapture` (window regex) or a second screencast on the popup, and add MV3 service-worker wake logic (workers suspend ~30s idle). - **Native scenes** (terminal, native app) — `gfxcapture` (or `ddagrab`), **silent** (ffmpeg has no WASAPI; narration is TTS). Discover the monitor/window **at runtime** — never hardcode `output_idx`/`hwnd` (DPI + multi-monitor shift them). Run the **gate assertion** after each take; retake on fail up to budget. **A wallet-scene retake must first refresh its on-chain preconditions** (nonce, burner balance, pending txs — a prior take may have already mined the tx or set the allowance), and tx-scene assertions tolerate testnet latency variance rather than hard timeouts. On budget exhaustion: swap the wallet driver (dappwright → injected + explorer shot), or mark the scene **BLOCKED** with a placeholder card and report it. Never fabricate an interaction to make a scene pass — a real BLOCKED beats a faked take (same ethos as the executor's "never fake green"). ### 4. Narration + captions TTS each scene's narration line through the **VOICE** engine (`../references/demo-stack.md` for the adapter matrix and per-engine timing source). Scene durations derive from the narration length via the `mediabunny` package's `computeDuration()` (audio-first timing — the video fits the words; exact snippet in `../references/demo-stack.md`). Build captions directly as `@remotion/captions` `Caption` objects from the TTS word timestamps — no transcription pass is needed for narration we authored (the whisper path is only for OpenAI-TTS or friend-recorded voiceovers). ### 5. Assembly A Remotion project under `demo/` in the project repo (committed code): title card, per-scene clips via `@remotion/media` `<Video>`, word-timed captions, optional ducked music bed (MUSIC field), outro (repo QR + the brief's track/bounty line). Final audio: two-pass loudnorm to **−14 LUFS** for YouTube (`demo-stack.md` has the exact recipe). QC passes render at reduced resolution; the final is 1080p h264. ### 6. QC loop Extract per-scene keyframes (per-scene `-ss` seeks so frames map to scenes) and **read them as images**: money shot visible, text legible, captions present, no Windows.Graphics.Capture yellow border, duration within budget, and **no secret/address on screen that shouldn't be** (a hard check). Confirm audio is present with sane loudness stats. Fix the composition and re-render, bounded iterations; if something stays imperfect after the bound, stop and carry a residual-imperfection note into the watch step — never loop forever, never ship silently. ### 7. Watch (hard human gate — never skipped) The user watches the final cut. This gate is not optional and not automatable; the QC loop makes the cut *probably* good, the user makes it *actually* shippable. Surface the QC residual notes here. ### 8. Upload (a separate consent) Only after the watch, and only on the **upload consent**. Default: consented browser upload to YouTube Studio (unlisted). **Preflight the account's upload eligibility before spending upload time** (new/low-history accounts can hit verification interstitials); on any interstitial or Studio DOM drift, **stop and hand off to the user** — never fight bot detection — with the Drive fallback one flag away. On success: **verify the link renders for a logged-out fetch**, append any required music attribution to the description, write the URL + evidence into the gate ledger, and resolve the submission draft's `TBD-post-build: demo video URL`. (The concierge's closing-pass submission gate then finds the URL already in the draft.) ## Upload — the ToS-honest doctrine The YouTube **Data API path is dead** for a personal tool: uploads from unverified API projects are locked private with no appeal (`demo-stack.md`, PRD §13). So the default is browser automation of the user's logged-in Studio session — which is **not ToS-clean**: YouTube's terms forbid automated access with no own-session carve-out. Therefore: keep it low-volume, supervised, the user's own content; **log every upload in the gate ledger**; and never present it as terms-clean. Authorization for this action class is the kickoff's **UPLOAD field** — an explicit standing authorization, parallel to but **distinct from** the concierge's SIGNUP AUTOMATION line (that authorizes signups/API keys, a different action class; it does not cover uploads). The per-upload consent still gates each run. **Drive fallback** (`drive.file` scope, anyone-with-link): non-restricted scope so no app audit, but still a Google Cloud OAuth client whose Testing-mode refresh tokens **expire after 7 days** — design for re-consent per hackathon, never a cached-forever token. Standing up that OAuth client is the user's own one-time setup; the UPLOAD authorization covers the upload action, not provisioning a Google Cloud project. ## Repo hygiene - **Committed** (project repo): the demo script, the Remotion project **code** (components, compositions — reviewable, re-renderable), and the upload evidence in the gate ledger (URL, checksum). A `.gitignore` for the render/capture output dirs. - **Never committed:** rendered videos, captured scene clips, TTS audio, extracted frames (binaries), and — the hard rule — **any secret**. Burner keys live in env vars / keychain; the repo sees names and public identifiers only (`../references/conventions.md` §Safety). - Music MP3s live in the local `~/.build-system/music-kit/` folder, never a repo (`../references/music-kit.md`). ## Hard boundaries - **Never self-trigger.** The orchestrator *offers* Step 2.5; the user *starts* it. No firing on build-green. - **Two consents, never skipped:** the foreground recording session, and the upload. Watching the cut and uploading are separate moments. - **Never fabricate an interaction.** A scene that won't pass its gate is retaken, driver-swapped, or marked BLOCKED with a placeholder — never staged to look like something that didn't happen. The tx in the final cut really happened on testnet. - **Burner wallet, testnet only** — the kit's standing rule. Never the user's main wallet or mainnet funds on camera. - **No secrets anywhere** — not in a committed file, not in a frame (QC checks for it), not in the ledger. - **You run the video, not the other gates.** Faucets, deploys, signups, and the submission form are the gate-concierge's; a build that's actually broken routes back through the guide to the orchestrator/architect — you record what's built, you don't re-open the build. ## When NOT to use this skill - Before the build is green — there's nothing to record; the earliest legitimate timing is right after the orchestrator reports green. - Non-demo-judged builds, or a submission that needs no video — nothing to produce. - The user wants to record the video themselves — then stop at the committed, approved script (Stage 1); it's a usable shot list on its own.