demo-studio · git:20260710.31c2709 · 2026-07-10 · sha256 ecf73936eb2c09ea
demo-studio git:20260710.31c2709A
Immutable. This exact content never changes and is served at /api/v1/blob/ecf73936eb2c09ea.
--- 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", the user starting Step 2.5 after the orchestrator's offer, or an autopilot dispatch under a locked campaign contract (which counts as the user's start). 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 — unless the locked contract's explicit WATCH policy delegates that watch (autopilot mode; references/campaign-contract.md). 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 watch gate (Stage 7) and the upload consent — 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** (delegable only by an autopilot contract's explicit WATCH policy — Stage 7). **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`. At boot, **run your tollgate session card** (`../references/tollgate.md` — rows 5–6, 8–13, 16: the demo-stack probes plus browser/device access, the wallet extension when wallet scenes are on, and disk) before Stage 1; a failing hard row stops you with the row's fix hint — never discover mid-recording that ffmpeg lacks `gfxcapture`. ## 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). **In an autopilot campaign** (your kickoff carries an `AUTOPILOT CAMPAIGN` block — `../references/campaign-contract.md`) both consents come pre-collected in the locked contract **when the user granted them** — the recording-session pre-consent (field 12) and the UPLOAD authorization (field 10, identity-bound). A pre-consent the contract struck, or an UPLOAD marked `manual`, stays with the user — return that act BLOCKED / hand it back, never assume it. The script and the WATCH gate follow the mode-aware notes in their stages below, and anything else the contract does not enumerate is returned BLOCKED with the exact question. ## 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** (in an autopilot campaign the approver is the autopilot, which reviews the script for contract conformance — judging-weight allocation, money shot, length — and approves or re-dispatches with corrections). 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 in interactive mode; contract-delegable in autopilot — never skipped silently) The user watches the final cut. In interactive mode 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. **In an autopilot campaign it is delegable — only by the locked contract's explicit WATCH policy** (`../references/campaign-contract.md`, field 9, always asked at intake, never defaulted): `pause-for-watch` keeps the human watch as a hard stop; `auto-publish-after-qc` replaces it with the autopilot's own independent frame/audio review (a second context re-reading keyframes and loudness stats against the script — not your QC re-asserted), and the user watches the unlisted link after publish. No contract line → the watch pauses, as in interactive mode. ### 8. Upload (a separate consent) Only after the watch (or its contract-delegated review, Stage 7), and only on the **upload consent** (interactive, or the contract's UPLOAD authorization). **Re-read the live logged-in account first** and proceed only if it matches the account the user authorized — in an autopilot campaign, the account named in the locked contract (`../references/campaign-contract.md` §Identity verification); mismatch or logged-out → stop and report what you found, never log in or switch accounts. 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 (in an autopilot campaign, that consent is the locked contract's UPLOAD authorization — Stage 8). **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. (An autopilot dispatch counts as the user's start — the user contracted Step 2.5 at lock time; you still never fire on a state condition.) - **Two consents, never skipped:** the foreground recording session, and the upload. Watching the cut and uploading are separate moments. (In an autopilot campaign these consents and the WATCH delegation come from the locked contract, per the clauses above — and one the contract struck or marked manual stays with the user; it is never assumed.) - **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.