Turn a GitHub release note into a short animated video (30–90 s), optionally narrated and scored, and email the owner a link when it's ready.
/dashboard/releases/[id]/video-settings):
release.published webhook (optionally pre-releases too).@remotion/player, using the same composition the renderer uses.RELEASE_VIDEO_MUSIC_MODELS) and optional style prompt./dashboard/releases/[id]/videos/[videoId] shows live progress steps, then the player, a download link, the storyboard, Regenerate and Delete.lib/workflow/release-video-handler.ts (Vercel Workflow), calling lib/services/release-video-pipeline.service.ts one durable step at a time:
| Step | What happens |
|---|---|
begin | Consumes one release_videos unit (release-video:<id>, idempotent) and builds the agent's context. |
designTurn ×N | One model call of the storyboard agent (lib/release-video/agent.ts) plus its tools. The conversation is carried between steps, so no step runs long and a crash resumes from the last turn. |
saveStoryboard | Saves the accepted storyboard or fails the video. |
narrate ×scenes | Speech per scene through the AI Gateway speech model, saved as MP3. |
music | Lyria clip over the Gemini API when enabled. Requests allow up to 5 minutes per attempt within a 12-minute total budget; transient failures and empty audio responses are retried. Requested music must be generated and saved before rendering. Exhausted failures fail the video and refund its allowance instead of silently delivering a video without music. |
launch | Starts the renderer container in Vercel Sandbox (FatalError on failure; a billable sandbox is never re-launched automatically). |
Deleting a video removes its row and its storage folder; a run still in progress stops at its next step.
Tools:
generate_image({ name, prompt, transparent }) — up to 6 images per video. transparent: true asks the image model for a green-screen drawing and keys it out (lib/release-video/cutout.ts, ported from summary-chip) into a PNG with alpha; false returns a full-bleed 16:9 illustration. The theme adds its own illustration style to the prompt.submit_storyboard({ title, accent, scenes }) — validated against the schema in lib/release-video/storyboard.ts (title scene first, outro last, images must exist, narration on every scene when voice-over is on). Problems are returned so the agent can fix them.Limits: 3–8 scenes, 14 turns, 3 failed model calls in a row. RELEASE_VIDEO_MODEL may be a comma-separated fallback list; after a failed turn the next turn uses the next model.
Themes live in remotion/release-video/ (relative imports only, no @/). The app imports them for previews. Set RELEASE_VIDEO_RENDER_SNAPSHOT_ID to reuse the prepared podcast runtime from summary-chip-ios (Remotion, Chromium and FFmpeg in /vercel/sandbox). Each render uploads this repository's trusted theme sources and runner, then bundles them inside the snapshot without installing dependencies. Agent-written code is never executed. The snapshot takes precedence over RELEASE_VIDEO_RENDER_IMAGE.
The dedicated renderer image remains supported and bundles the themes at image build time. Both paths restrict sandbox networking to signed storage asset hosts and re-time narrated scenes with ffprobe before rendering 1080p H.264. Uploads use presigned S3 PUT URLs.
Vercel snapshots are project-scoped: Chippy's snapshot cannot be used with Flowcy's OIDC credentials. Run bun scripts/prepare-release-video-renderer.ts to reuse Chippy's preparation recipe in this project (no Docker required). It installs the pinned Remotion runtime and browser, verifies Chromium and FFmpeg, and creates a clean snapshot containing no user data. Copy the resulting RELEASE_VIDEO_RENDER_SNAPSHOT_ID from .env.renderer.local into .env.local and deployment configuration.
Alternatively, publish a dedicated renderer image (Docker Desktop running, Vercel CLI logged in and linked):
# Build and smoke-render the sample locally only:
bun run video-renderer:publish 1.0.0 --skip-push
# Build, smoke-render, push to VCR, verify in a real Sandbox and set RELEASE_VIDEO_RENDER_IMAGE:
bun run video-renderer:publish 1.0.0 --configure production,previewWhen using the dedicated image, re-publish after changing remotion/release-video/. The snapshot path receives current themes from each deployment and only needs rebuilding when runtime dependencies change.
| Variable | Default | Purpose |
|---|---|---|
RELEASE_VIDEO_MODEL | anthropic/claude-sonnet-5.5 | Storyboard agent model(s), comma-separated fallback list |
RELEASE_VIDEO_IMAGE_MODEL | google/gemini-3.1-flash-lite-image | generate_image model; off disables images |
RELEASE_VIDEO_SPEECH_MODEL | elevenlabs/eleven-v4-turbo | Narration and voice samples |
GEMINI_API_KEY | — | Lyria music; without it music is unavailable |
RELEASE_VIDEO_MUSIC_MODEL | lyria-3-clip-preview | Default music model (~50 s per 30 s clip; lyria-3.5 took over 4 min in testing) |
The Sandbox SDK uses Vercel OIDC in deployments. Locally, set VERCEL_TOKEN, VERCEL_TEAM_ID and VERCEL_PROJECT_ID.
Tables: releaseVideoSettings (one per release repository) and releaseVideo (migration 0005_release_videos.sql).
Video setup saves a default duration and speed per repository. The generation dialog can override them for a single video, including resetting a saved duration to Automatic. Available presets are 30 seconds, 1 minute, 1 minute 30 seconds and 3 minutes. Custom accepts whole seconds from 10 to 600; Automatic follows the release content.
Slow, Normal and Speedy affect script density, narration playback and animation timing across all themes. Speedy asks for compact headlines, fewer bullets and brief narration; its reveals and narration run at 1.6× normal speed (Slow: 0.8×). Theme previews reflect the selected speed. Fixed durations remain fixed at any speed.
The renderer probes narration before fitting the timeline, reserves lead/tail time and distributes frames to match the chosen duration exactly. If narration cannot fit even at 2×, the job reports an error requesting a shorter script or longer duration instead of truncating audio. Automatic timing keeps the full narration.
Apply migration 0006_moaning_menace.sql before using these settings. Snapshot renders upload the current composition and runner for each job. Deployments using a baked container image must rebuild/publish the renderer image to pick up the timing changes.
Video setup and the generation dialog offer a video language picker. The default matches the release notes; an explicit language translates both scene text and narration during storyboard generation. Saved settings also apply to automatic release videos.
Captions are enabled by default. The dedicated Caption settings dialog selects up to five languages and the default subtitle language. With no selection, captions match the video's language; otherwise translation happens automatically. Narrated videos use the narration script; silent videos use the scene text. Caption text omits punctuation, including commas, periods and their multilingual equivalents. Captions follow the final fitted scene/audio timing, with long lines split into shorter cues (scene-aligned, not word-level transcription).
The default MP4 includes every selected language as a selectable mov_text subtitle track, with the first language enabled by default. Video and audio are copied without re-encoding. Captions can be disabled or switched in compatible players. The browser player uses same-origin, authorized WebVTT tracks. The Download dialog also offers the clean MP4 without subtitle tracks and one UTF-8 VTT per selected language. All artifacts upload directly from the sandbox using presigned S3 PUT URLs, and authorized downloads use signed S3 URLs. Deleting the video deletes these artifacts too. Existing videos retain their original downloads and SRT labels; regenerate older burned-in videos to get soft captions and VTT files.
Apply migration 0007_icy_marauders.sql before deploying caption settings. The snapshot path receives the updated runner and caption helper automatically. Deployments using the dedicated renderer image must rebuild/publish the image to include captions.mjs and the updated runner. Its FFmpeg must support WebVTT input and the mov_text subtitle encoder. No libass subtitle filter is required.
Use Additional video languages in Video setup or the generation dialog to select up to four languages in addition to the primary video language (five complete videos total). Saved choices apply to automatic releases; the generation dialog can override or clear them for a single run.
Each language starts an independent workflow. The agent writes all scene content and the narration script in that language. Enabled voice-over uses the multilingual narration voice. Enabled captions include that video's own language as the default soft subtitle track, with all selected languages also available as subtitle tracks and VTT downloads. The original caption-only translation settings remain independent of complete video languages.
Each version has its own weekly video generation charge, status, errors, notification, files and downloads. The Videos list labels each version by language; multi-version generation navigates there. Download filenames and ready emails also identify the language. One workflow launch failure does not prevent the other versions from starting; partial failures are shown in the status overlay. Each workflow enforces the weekly video allowance before any model work.
Apply migration 0008_modern_vindicator.sql before deploying this feature. No renderer runtime change is required for additional complete videos.
RxSubscription owns the release_videos meter. Configure a weekly reset, block overage, a default/Free limit of 1, and a Plus plan limit of 7. Each generated language version consumes one unit using release-video:<id> as its idempotency key before model work begins; failures refund that unit on a best-effort basis. Videos do not consume ai_sessions when RxSubscription is enabled. The legacy billing provider retains its existing behavior.
The live GitHub Autopilot catalog was configured and verified on 2026-11-08: release_videos resets every calendar week, blocks overage, defaults to 1, and grants Plus subscribers 7 (including trials). Flowcy's billing API returned a limit and remaining allowance of 7 for an existing Plus subscriber, and the generation allowance check passed. No usage was consumed by this verification. An absent meter still blocks generation; keep the same configuration in any separate application catalog.
poll ×N | Every 10 s, reads the container's status.json. |
finish | The container uploads render.mp4 with a single presigned PUT; the app verifies its size. |
fail / notify | Failures refund the video generation unit. The owner is emailed (ready or failed) through sendNotification, which applies the daily quota. |
RELEASE_VIDEO_MUSIC_MODELS | default + lyria-3-pro-preview, lyria-3.5 | Models offered in the setup |
RELEASE_VIDEO_RENDER_IMAGE | — | Alternative digest-pinned renderer image; either image or snapshot is required |
RELEASE_VIDEO_RENDER_SNAPSHOT_ID | — | Shared prepared podcast runtime snapshot; preferred over the dedicated image |
RELEASE_FILES_S3_* | — | Storage for images, audio and videos (release-videos/<userId>/<videoId>/) |