internal · sop

WV Video Generation — End-to-End SOP

One entry point, deliberate forks at the rendering stage, shared assembly/QA/persistence. As of 2026-05-15.

The flow

entry

Universal entry point

MCP · portal · CLI — same surface for every video type

  • wv_generate(brand, type, config)
  • Inputs: brand_slug · target_duration · platform · (avatar) · (sku)
stage 1

Context load

Shared across every video type

  • Brand brainbrands.brand_config (voice, tagline, URL, SKUs, typography, caption_style, flavor_palette, caption_aliases)
  • Avatar profilebrands.avatars[avatar_id] (core_problems, messaging_hooks, best_skus)
  • Asset DNA libraryassets table (product_shots + product_renders + lifestyle + video_clips w/ moment-level DNA: structural beats, transcript segments, product-visible windows, broll-safe windows, text overlays)
  • Exclude filterclients/<brand>/asset_excludes.json (per-brand reserved-asset list)
stage 2

Angle commit

Two paths, both end with a topic string

  • Path A — user types topic directly
  • Path BOpus 4.7 ideator proposes 4 candidate angles, grounded in brand brain + avatar + asset DNA + creative DNA + RAG production knowledge. User picks one; its angle becomes the topic.
stage 3

Director — one validated LLM call

Claude Sonnet 4.6 — turns the angle into a committed storyboard

  • Outputs per scene: scene_type · target_duration · voiceover_text · text_overlay · method · asset_ref · audio_role
  • Validates before spending a cent: word count ≤ 3× scene_dur · overlay derived from VO · passthrough window ≥ scene_dur · CTA never solid_color
  • If validation fails, loops the director once with feedback
  • method is the fork — passthrough · fal_i2v · fal_t2v · arcads_actor · solid_color
stage 4 · audio-first

TTS per scene → measure → retime (BEFORE the fork)

Why first: scene durations follow audio truth, not estimates. Prevents mid-sentence cuts and black tails.

  • ElevenLabs reads voiceover_text for each scene — one mp3 per scene.
  • _expand_for_tts() runs caption_aliases first so abbreviations speak right ("NIC" → "nicotine", "B12" → "B twelve", "vitabark" → "VitaBar").
  • Measure each mp3 with ffproberetime_blueprint_from_audio() rewrites every scene.start_time / scene.end_time from the measured audio + a 0.3s breath buffer.
  • Now every scene's slot matches its spoken line. Downstream methods generate at real durations.
stage 5 · fork

Method routing — per scene, per method

Now executes at the audio-corrected durations. The director decides; the executor renders. Mix methods in one video.

passthrough_clip

Real existing UGC clip trimmed to a specific timestamp window. Zero AI regeneration; zero hallucination risk.

$0 / clip

fal_i2v · fal_t2v

fal/Kling 2.6 Pro. Animates a real product/lifestyle still (i2v) or generates from text (t2v) when no asset fits.

~$0.35 / clip

arcads_actor

Arcads OmniHuman 1.5 talking-head with b-roll cut-ins on top. Highest-fidelity UGC.

~$0.30 / sec

Deprecated: solid_color — flat brand-color background w/ pill overlay. Used to be a 4th branch but caused "black screen" defects (brand-green reads as black on most screens). Director now auto-upgrades any CTA solid_color request to product_shot. Reserved only for rare text-only hook moments where the line IS the moment.

stage 6 · music

Music (independent track)

Generated at the now-known total duration; can run in parallel with the fork's clip jobs

  • ElevenLabs Music — mood from the director's audio_mood field, length = sum of retimed scenes.
  • Bedded at 12% volume under the VO mix.
  • SFX gated OFF by default (ENABLE_SFX=1 to opt in — Kling sometimes generates SFX that bleeds, kept off).
stage 7 · assemble

Assemble

Shared captions + render

  • Brand-aware captionscommon/captions.py: pick_caption_colors() (flavor → emphasis → default), apply_caption_aliases(), load_overlay_font() with typography.overlay_weight.
  • Renderer — Creatomate (video_v2 path) or ffmpeg local (arcads / narrated / supercut). All video elements muted (volume:"0%"). Audio = video duration exact. 1080×1920 @ 30fps.
stage 8 · gate

QA — Mandatory editorial review

No video reaches the user without passing

  • Gemini 2.5 Pro editorial review (tools/qa/gemini_review.py)
  • Captures: overall_rating · issues · strongest_moments · audio_notes · caption_notes
  • Block "show" if score < 7. If failed: identify worst issue → decide per-scene regen vs full re-render.
stage 9 · persist

Persist + deliver

  • Supabase Storage — public mp4 URL
  • Creative row — w/ creative_dna tags + cost metadata
  • Cost ledger entry
  • Brand handoff — copy to ~/Desktop/NEW VB Vids/ + open (VitaBar workflow rule)
  • Notify caller — MCP / portal / CLI receives generation_id complete

The five video types and which fork they take

Type (wv_generate)Director?Method per sceneBody shapeStatus
video_remix ✓ Sonnet passthrough + fal_i2v + fal_t2v mixed TTS-driven scenes Wired
arcads_video_ads — Internal LLM (gpt-4o JSON) arcads_actor + passthrough b-roll Actor speaks · b-roll cut-ins ~60% Wired (just shipped)
video_ads (narrated b-roll) — Standalone passthrough + fal_i2v TTS VO over b-roll Independent path
ai_video_ads — Direct Kie.ai/Veo3 fal_t2v only Single AI-generated clip Independent path
video_supercut (script only, not yet a wv_generate type) — Transcript-driven passthrough only UGC montage from existing clips Not in MCP

What's shared today vs. what isn't

Shared moduleUsed byNot yet used by
common/brand_db.fetch_brand() arcads · narrated · supercut · video_v2 ai_video_ads
common/captions.py arcads · narrated · supercut · video_v2 (just wired) ai_video_ads
Sonnet director (video_engine/director.py) video_v2 only arcads · narrated · supercut · ai_video_ads
Asset library moment-level summary video_v2 director Could feed all the others
Opus ideator (video_engine/ideator.py) CLI only None of the wv_generate types surface it
Creatomate assembly video_v2 only Others use ffmpeg locally
Gemini review gate None — manual today Should be ALL paths

The three gaps to close

Gap 1 · Make the director path-agnostic

Today the director outputs a video_v2-shaped storyboard. It should output scenes whose method is one of a fixed enum (passthrough_clip | fal_i2v | fal_t2v | arcads_actor | solid_color) — and the EXECUTOR picks up its scenes. Today Arcads/narrated/supercut bypass the director entirely.

Gap 2 · Move Opus ideator in front of every type

The ideator only fronts generate_video_v2 today. wv_generate with any type should accept an --ideate flag that runs Opus first across brand+avatar context, then feeds the chosen angle into whichever executor.

Gap 3 · Make Gemini review a gating step, not a manual call

tools/qa/gemini_review.py is a CLI run by hand today. To meet the SOP, every assembly path should END with the Gemini review baked in — configurable score threshold (default 7/10) — and the platform should refuse to mark a Creative as "ready" until it passes.

The minimal next step

Make the director the truth source for every video type. After that, every video — regardless of executor — comes from the same flow: brand brain → angle → director → method-routed → shared assembly → Gemini-gated → persisted.

Recommended order

  1. Add method: "arcads_actor" to the director's enum (~30 min)
  2. Wire _run_arcads_video_ads to consume a director storyboard instead of running its own gpt-4o script-writer (~1 hr)
  3. Same for narrated b-roll + supercut (~1 hr each)
  4. Gate Gemini review into every path — review score lands on the Creative row (~30 min)
  5. Surface --ideate on wv_generate as an optional pre-pass (~20 min)

~4-5 hours total to land the unified SOP.