← Style engine · Research report · Engine architecture, routing & 10/10 QC

Engine decision after this research (2026-09-28): Gemini Omni is the only video model. Where this report routes shots to Veo 3.1, the engine uses Omni with the rules on the main page.

05 · Style engine: architecture, model routing and automatic 10/10 QC

Research for Lead Up's multi-style ad engine (vertical 9:16, 30-60 s, Tunisian Derja). Date: 2026-09-27. Scope: engineering of a "style engine" (any look, same script), model routing per shot, automatic quality control, creative testing and orchestration. Research only: no media generated, nothing spent.

Evidence tags: [official] Google/Meta docs or blogs · [paper] peer-reviewed or arXiv · [practitioner] blogs, tool vendors, agencies · [ours] measured in this repo (ai-docs/03-lessons-learned.md) · [unverified] claim seen once, or numbers with no traceable source · [proposal] my design recommendation.

Related earlier research (not repeated here): research/01-omni-prompting.md, 05-omni-pro-workflows.md, 06-nano-banana-2.md, 07-anti-ai-look.md, 08-hybrid-pipeline.md.


0. TL;DR

  1. Split the ad into three layers that never mix: a style-agnostic layer (script, VO, word timings, beat sheet, "shot intents"), a style layer (a versioned style bible = data + reference images + per-model prompt fragments + Remotion theme tokens + music/SFX recipe + QC thresholds), and a render layer (router → generators → judges → Remotion). The same approved script then renders in any style by swapping one style@version pin. [proposal]
  2. Shot intents, not shot prompts. The script produces semantic shots ("hook-macro: phone flooded with messages", "talking-head: installer answers", "data: 300 DT → 0"). A style adapter decides how each intent is made in each style (AI video, 2.5D still, or pure code). In 2D-flat or collage, most intents become code; in photoreal, most become Omni/Veo. [proposal]
  3. Style-locking is strongest at the still-image stage. Lock the look in one owner-approved style frame per style, then make one first frame per shot from it (image-to-video inherits the look). Nano Banana 2 has no dedicated style-reference slot (objects ≤10, characters ≤4); Nano Banana Pro has up to 3 style references [official]. Use Pro for the style frame and for hard styles; NB2 for volume. Veo 3.1 accepts only asset references (≤3, 8 s clips); style references exist only on veo-2.0-generate-exp [official]. Omni takes style via in the style of <IMAGE_REF_0> and can restyle an existing clip while keeping its motion [official].
  4. Routing rule of thumb: Omni 1.1 Flash (4 s, 720p, $0.10/s) is the default AI-motion engine; Veo 3.1 Fast ($0.10/s at 720p) when you need subject references + native English/French dialogue or 8 s single takes; Veo 3.1 Standard ($0.40/s) only for hero photoreal shots; Veo 3.1 Lite ($0.05/s) for cheap atmospheric B-roll; code (Remotion) for every text, number, UI, map, logo, chart and for whole "graphic" styles. Derja lip-sync is not evaluated by Google on either model ("other languages not evaluated") [official]: use our TTS + a post lip-sync model, or avoid on-camera speech.
  5. "10/10" must be operational: all hard gates pass (deterministic checks: text correct, safe zones, loudness, colour range, no inserted cuts, no text in AI shots, luma, first frame lit) and the ad wins or ties a blind pairwise comparison against the style's "golden" ad, and a binary checklist judged by a VLM has no "no". VLMs rank far better than they score: pairwise judging reached 0.86 Spearman with human leaderboards vs 0.36 for pointwise scores (GenArena) [paper]; "VLM judges can rank but cannot score" [paper]. So: checklists for gates, pairwise for selection, never 1-10 scales for decisions.
  6. Best-of-N is cheap insurance, judging is cheaper still. One 4 s Omni take costs $0.40; judging it with Gemini 3.1 Pro at 8 fps/high resolution is about $0.02. With a 60 % pass rate, 3 takes give a 94 % chance of at least one pass. Gains from more samples converge (Video-T1) [paper]; spend the extra N on the hook (5-10 takes) not on filler.
  7. Styles are the cheapest real diversity for Meta. Meta's official guidance says diversify visuals/formats, messaging, storytelling and personas; Andromeda improved ads quality by 8 % and needs "more volume and diversity of creative" [official]. Ads that look alike are (per practitioners) grouped and compete as one; a style change is a genuinely different visual. Build concept = angle × style, variants = hooks/CTAs. [official + practitioner]
  8. Owner time ≈ 15 min per ad is realistic with 4 gates: script OK, style pick (from style cards, amortized), picks strip (optional), final phone watch. Everything else is automatic, logged, cached and cost-capped. [proposal]

1. Style system design

1.1 What the three layers contain [proposal]

LAYER A  (style-agnostic, approved once per ad)
  brief.md → script.md (Derja, linted, owner OK) → VO takes → align.json (word times)
  → beats.json (beat sheet: 8-10 beats, each with message + on-screen facts + CTA)
  → intents.json (25-35 shot intents: semantic type, subject, action, facts shown, duration window, importance)

LAYER B  (style, reusable across ads; versioned)
  library/styles/<style_id>/
     style.yaml            the style bible (data, section 1.2)
     theme.ts / theme.json Remotion tokens (colours, fonts, springs, transitions, grain, LUT, caption preset)
     frames/style_frame.png, palette_card.png, texture_swatch.png, golden/*.png (owner-approved stills)
     golden_ad.mp4         the best finished ad in this style (pairwise anchor for QC)
     prompts/nb2.j2, omni.j2, veo.j2, lyria.j2, tts_direction.txt
     adapters.yaml         intent type → recipe (engine, template, refs, N takes, trim rules)
     qc.yaml               thresholds (style similarity floor, luma range, cut rate, judge checklist additions)
  library/characters/<char_id>/spec.yaml (style-neutral: age, build, wardrobe, colours, features)
                              /<style_id>/sheet.png (turnaround in that style; generated once, approved once)
  library/worlds/<world_id>/spec.yaml + /<style_id>/plates/*.png (rooftop Tunis, blue door, living room…)

LAYER C  (render, per ad × style)
  router → keyframes (NB2/Pro) → motion (Omni / Veo / 2.5D / code) → judges → picks.json
  → timeline (VO anchors fixed, beat grid of THIS style's music) → Remotion(theme) → master → QC → publish

Why this split matters: - The VO and word timings are shared by every style, so re-styling never re-records the voice. Music changes per style, so the beat grid (and therefore exact cut points) is recomputed per style; this is a cheap local step (pipeline/timeline.py). - Characters and worlds are defined twice: once as neutral text specs (so a clay installer and a photoreal installer are "the same person"), once per style as an approved sheet. Identity references cannot cross styles: a clay sheet used as a reference for a photoreal shot drags the output toward clay. - Intents make routing a table lookup rather than a prompt-writing session each time.

1.2 The style bible as data (schema) [proposal, fields justified by the sources in §1.3-1.5]

id: clay3d            # stable slug; used in ad names and manifests
version: 1.3.0        # semver: major = look change (re-approve golden), minor = prompt tweak, patch = typo
family: 3d            # photoreal | cinematic | ugc | 3d | 2d | anime | collage | kinetic-type
status: approved      # draft | approved | retired
cost_class: ai-heavy  # ai-heavy | mixed | code-only  (drives budget estimates)

look:                 # words for AI prompts: NEVER hex codes in video prompts (Omni prints them) [ours]
  medium: "hand-made plasticine claymation, visible thumbprints, matte surfaces"
  palette_words: "chalk-white (pure white, never green or yellow), terracotta, Tunisian blue, warm sand"
  palette_hex: {bg: "#F4EFE6", accent: "#1E6FD9", warm: "#C8643B"}   # for Remotion + ΔE checks only
  lighting: "one soft key light from 10 o'clock, gentle fill, soft contact shadows, no rim glow"
  lens_camera: "35mm equivalent, eye-level, shallow but not creamy depth of field, locked-off or slow dolly"
  texture: "fine clay grain, slight fingerprints, no plastic sheen"
  line: null          # 2D styles: "uniform 6 px ink outline, flat fills, no gradients"
  composition: "subject off-centre on thirds, 25% negative space top for captions band"
  semantic_negatives: # described positively (Google advice) + our measured failures
    - "surfaces are matte; the air is clear, with no bokeh balls, sparkles, lens flares or glow"
    - "the image contains no letters, numbers, logos or words anywhere"
    - "never copy any word from a reference image"
  dont: ["centred hero with glow", "rainbow gradients", "hyper-detailed skin pores"]

motion:
  ai_motion_words: "stop-motion feel, snappy holds, objects squash on impact, already moving at frame one"
  camera_words: "locked-off, then a short push-in"
  forbidden_motion: "no floaty drift, no slow settle at the end"
  code_feel: snappy   # maps to Remotion spring presets in theme.ts
  target_cut_s: [1.2, 2.0]
  transitions_allowed: [hard-cut, whip, match-cut]
  frame_step: 1       # 2 = animate on twos (stop-motion / collage feel) for code layers

type_and_captions:
  caption_preset: owner-outline   # owner rule: thick black outline captions (global unless the owner changes it)
  display_font_ar: "Cairo ExtraBold"
  latin_font: "Inter Black"
  type_motion: "pop-in 6 frames, overshoot 8%"

audio:
  lyria_prompt: "playful pizzicato and marimba, bouncy, 116-122 BPM, clear downbeats, no vocals"
  sfx_palette: [squish, pop, clay-thud, soft-whoosh]
  vo_direction: "warm, energetic, smiling"     # Gemini TTS style prompt; same words, different delivery
grade: {lut: clay_warm.cube, grain: 0.02, vignette: 0.1}

references:           # every reference has ONE job (practitioner rule; Google role tags)
  - {file: frames/style_frame.png, role: style,  note: "match rendering, palette, light; ignore content"}
  - {file: frames/texture_swatch.png, role: style, note: "surface texture only"}
routing_overrides:    # optional: per intent type
  talking-head: {engine: omni, lipsync: none, framing: "over-shoulder or hands, mouth off-camera"}
qc:
  style_sim_min_z: -1.0     # CSD similarity vs golden set, as z-score of in-style distribution (§3.5)
  luma_yavg: [90, 190]
  judge_extra_checks: ["no surface looks glossy or plastic", "no photographic skin texture"]
golden_ad: golden_ad.mp4
changelog: ["1.3.0 2026-09-27 chalk-white wording, removed 'soft glow'"]

Design notes: - Words for models, hex for code. Omni wrote "#EF4444" on screen when given hex codes [ours]; Remotion and colour checks need exact hex. Keep both in the bible. - Semantic negatives. Google: "describe what you wish to exclude" as a positive scene description, e.g. "a desolate landscape with no buildings or roads" rather than "no man-made structures" [official, Veo guide]; the Gemini image guide calls them "semantic negative prompts" [official]. - Version everything. A style is a product: changing the lighting sentence is a new version, and an ad manifest pins clay3d@1.3.0. That is what makes a winning ad reproducible months later. - cost_class lets the planner mix styles for Meta diversity without blowing the budget (a kinetic-type or 2d style can be ~$0-3 of AI per ad; see §2.6).

1.3 Style-locking in Nano Banana 2 / Pro (keyframes, sheets, plates)

Facts [official]: - NB2 (gemini-3.1-flash-image, GA): up to 14 inputs; up to 10 high-fidelity object images and up to 4 character images; no style-reference category. NB Pro (gemini-3-pro-image): up to 6 objects, 5 characters, 3 style references. NB2 Lite: 1K only. Price per 2K image: NB2 $0.101, Pro $0.134. Thinking level minimal/high. (Tables and URLs in research/06-nano-banana-2.md §1.) - Google's templates: "Describe the scene, don't just list keywords"; style transfer template "Transform the provided photograph of [subject] into the artistic style of [style]"; multi-image template "Take the [element 1] and place it with [element 2]"; inpainting "Change only the [element]… Keep everything else unchanged"; control the camera with photographic language (85 mm, macro, Dutch angle) [official].

Practitioner consensus: start with 2-4 references and give each a role; "the reference image carries identity while the prompt carries everything else"; do not assume the model matches the reference's style by default, restate it [practitioner]. Measured by us: 3-4 parallel calls with 4 refs is the safe rate; always set an HTTP timeout; it copies words from references unless told not to; "one 2K first frame per shot" beat contact-sheet panels (16/16 usable) [ours].

Recommended lock recipe per style [proposal]: 1. Style frame (once per style, owner-approved): NB Pro at 2K, with 1-3 style refs (mood images the owner supplied or earlier golden frames) + the style bible's look paragraph. Output: one hero still that defines medium, palette, light, texture. Also a palette card and a texture swatch cut from it. 2. Character sheets per style: NB2 (or Pro) with the style frame as a reference ("match the rendering of image 1 exactly; image 1's content is irrelevant") + character spec text → turnaround (front, 3/4, profile, hands). 3. Per-shot first frames: NB2 2K, references = [style frame, character sheet(s), plate or prop], prompt = verbatim style block + shot intent rendered as a scene description + semantic negatives. Same template for every shot of every ad in that style. 4. Style QC before any video money: CSD style similarity vs the golden set (§3.5) + a VLM checklist (no text, right palette, right light) + owner sees one contact sheet. Re-generate outliers only. 5. Seeds: the Gemini config accepts a seed, but image/video generation is not guaranteed deterministic (Veo docs say seed "slightly improves" determinism) [official]. Reproducibility = keep the outputs (content-addressed cache), not re-running a seed.

1.4 Style-locking in Omni and Veo (motion)

Omni 1.1 Flash [official, details in research 01/05]: 3-10 s, 24 fps, 9:16, 720p default (1080p/4K are upscaled), reference tags <FIRST_FRAME>, <LAST_FRAME>, <IMAGE_REF_n>, <VIDEO_REF_n> (≤3 video refs ≤3 s, audio ignored), style through the prompt ("in the style of a woman is walking"), restyle of an existing clip (≤10 s input) while keeping motion ("crayon → graphite → glass 3D → risograph" example), extension to 40 s. "English fully supported; other languages not evaluated"; no system instructions, temperature or stop sequences. Omni production id on Vertex for us: gemini-omni-1.1-flash-preview on global only [ours]; the Gemini-API id gemini-omni-1.1-flash is GA since 2026-08-27 [practitioner report of the release].

Veo 3.1 [official]: 4/6/8 s; 720p default, 1080p, 4K; 8 s mandatory for 1080p, 4K, reference images and first/last frame; up to 3 asset reference images (person, character or product); style reference only on veo-2.0-generate-exp; extend +7 s up to 20 times (148 s total, 720p only; not on Lite); native audio with dialogue in quotes, SFX: and Ambient noise: lines; timestamp prompting [00:00-00:02] … for multi-shot; seed only "slightly improves" determinism; 16:9 and 9:16; videos kept 2 days on the server; SynthID. Lite has no reference images and no extension [practitioner, consistent with docs].

Lock recipe [proposal]: - Image-to-video is the style lock. A first frame made in step 1.3.3 carries the style; the video prompt carries motion only ("motion-only prompt", our rule 4) plus a fixed 1-sentence style phrase repeated verbatim in every shot of the style (practitioner rule: "one fixed style phrase for every scene"). - Add the style frame as <IMAGE_REF_0> with the role spelled out ("use only for rendering style, palette and light; do not copy its content"), and the character sheet as <IMAGE_REF_1>. Use exclusion language for every reference (practitioner). - Camera language from a fixed vocabulary in the bible (Google lists: dolly, tracking, crane, aerial, slow pan, POV; wide, close-up, extreme close-up, low angle, two-shot; shallow DoF, wide-angle, macro; Omni adds push in, punch in, dolly zoom, locked off, "oner", smartphone zoom, webcam style) [official]. Styles restrict it: UGC = handheld smartphone, slight shake; cinematic = dolly/crane, anamorphic feel; collage = locked-off. - Restyle for multi-style hero shots: make the choreography once (in the cheapest style or a grey-box Remotion animatic), then Omni-restyle into each style. Same motion and timing across styles, so the same cut list works. Costs one extra generation per style; quality of restyle on our content is untested [proposal, to test]. - Colour lock in post: one grade (LUT) and grain per style in Remotion/ffmpeg over all AI shots; this hides small per-clip drift (research 07).

1.5 Style-locking in Remotion (code layers)

Remotion props flow: defaultProps → inputProps override at render → calculateMetadata() can compute duration and other metadata asynchronously (e.g. from timeline.json); props can be validated by a schema (zod) and edited in Studio [official]. So [proposal]: - theme.json per style validated by a zod schema: colour tokens (bg, fg, accent, caption fill/outline), fonts per script (Arabic display/body, Latin), type scale, stroke widths, corner radii, shadow model (none / hard offset / soft), spring presets (snappy, bouncy, heavy: damping/stiffness/mass), easing curves, allowed transitions, frameStep (animate on twos for stop-motion/collage), grain and LUT, caption preset. - Scenes read only tokens, never literal colours or fonts. Rendering the same ad in a new style is npx remotion render … --props=ads/<slug>/render_<style>.json. - Code-only styles (kinetic type, 2D flat, infographic, collage from NB2 cut-outs) then cost almost nothing to add. - Owner's caption rule (thick black outline) stays global; the theme may only change the font within the rule.

1.6 Starter style catalogue (what each needs) [proposal]

Style Main engine AI $/45 s ad (est.) Key risks Notes
3D clay (current) NB2 frames → Omni i2v ~$15-18 glossy plastic look, glow creep Already proven [ours]
Photoreal cinematic NB Pro style frame, NB2 frames → Omni; Veo Std for 1-2 heroes ~$18-30 hands, faces, uncanny motion, Tunisian locations wrong Needs strongest artifact QC
AI UGC (phone selfie) NB2 "phone photo" frames → Omni/Veo; lip-sync post step ~$15-25 + lip-sync Derja lip-sync, "plastic" skin, over-clean light Honest framing only: no fake testimonials (§4.5)
2D flat vector NB2 flat assets (alpha via diff-matte) + Remotion rigs ~$2-5 AI frames drift to 3D shading Mostly code; very cheap diversity
Anime / cel NB Pro style frame, NB2 frames → Omni ~$15 drift to 3D, face inconsistency Anime forgives motion errors
Paper collage / cut-out NB2 cut-out elements + Remotion on twos, paper texture ~$2-6 AI video can't do stepped timing well Code is better than AI here
Kinetic typography Remotion only (+ Lyria) ~$0.1 readability of Arabic Zero AI video; strongest control
Cinematic 2.5D NB2 plates + Depth Anything parallax ~$2-4 stretched edges Research 08 §3

2. Model routing per shot type

2.1 Capability and price sheet (2026-09)

Omni 1.1 Flash Veo 3.1 Standard Veo 3.1 Fast Veo 3.1 Lite Code (Remotion)
Clip length 3-10 s; extend to 40 s 4/6/8 s; extend to 148 s (720p) same as Std 4/6/8 s, no extend any
Resolution 360p, 720p native; 1080p/4K upscaled 720p, 1080p, 4K 720p-4K 720p (1080p priced) 1080×1920 exact
Price /s 360p ~$0.034 · 720p $0.10 · 1080p ~$0.152 · 4K ~$0.304 $0.40 (720p/1080p) · $0.60 (4K) $0.10 (720p) … $0.30 (4K) $0.05 (720p) · $0.08 (1080p) $0 (CPU)
Image refs many (docs inconsistent: 5-10); style via prompt ≤3 asset refs, 8 s only ≤3 asset none n/a
Video refs ≤3 × ≤3 s (motion/likeness) no no no n/a
First/last frame yes (we use first only) yes (8 s) yes yes n/a
Restyle/edit clip yes (≤10 s input) edit exists on Vertex n/a
Native audio yes, always (music creeps in) yes, dialogue + SFX + ambience yes yes our SFX kit
Dialogue/lip-sync English only evaluated best of the family; non-English "not documented" n/a
Arabic text in frame no (invents garbled Arabic) [ours] poor (subtitle problem) exact
Batch discount none
Sources: Gemini API pricing page (Veo tiers, Omni $17.50/M video tokens = 5,792 tok/s at 720p ≈ $0.10/s) [official];
Omni per-resolution figures from an analyst breakdown [practitioner]; Omni specs from the API doc [official]; Veo specs
from the Veo doc [official]; reference types from the Vertex references doc (search excerpt) [official].

Not available: no "Veo 4" exists as of September 2026; Google shipped Omni instead [practitioner, several trackers].

2.2 Routing table by shot intent [proposal]

Shot intent Default route Fallback / upgrade Takes N Why
Hook (0-2 s) visual Omni i2v 4 s from NB2 2K frame, trim to 0.8-1.5 s Veo 3.1 Fast 4 s i2v 5-8 Highest leverage; selection matters most
Product/object hero, physics Omni i2v 4 s Veo Std 8 s 1080p for the 1 hero 3 Omni good at physical motion; Veo Std for flagship
Character action, no speech Omni i2v + <IMAGE_REF> sheet Veo Fast 8 s with ≤3 asset refs 3 Reference-driven identity
Talking head in Derja Our TTS VO + Omni/Veo performance with mouth hidden or off-camera (hands, over-shoulder, phone POV) Omni/Veo take "speaking silently" → post lip-sync to our VO (lipsync-2-pro or LatentSync) 3 + lip-sync Neither model is evaluated for Arabic; our VO is the approved voice
Talking head FR/EN line Veo 3.1 Std/Fast with dialogue in quotes 3 Best native lip-sync (~80 % single speaker, practitioner)
Continuous move > 10 s Omni extend (≤40 s) Veo extend (720p) 2 per link Extension keeps momentum; avoid F/L chaining (research 05)
Same shot in several styles Omni restyle of one master take per-style i2v 1-2/style Keeps timing identical across styles
Atmosphere B-roll (sky, street, texture) Veo 3.1 Lite 720p 4 s ($0.20) Omni 720p 2 Cheapest acceptable motion
Camera move over a still 2.5D parallax (Depth Anything V2 + Remotion) Omni 0 Crisp, free, no wobble (research 08)
Text, numbers, UI, chat bubbles, counters, maps, charts, logo, end card, captions Remotion never AI 0 Exact; AI prints hex codes and fake Arabic [ours]
Graphic styles (2D, collage, kinetic) Remotion + NB2 assets Omni for 1-2 organic moments 0-2 Stepped timing and exact shapes are code strengths

Router logic (pseudo) [proposal]:

engine = style.adapters[intent.type].engine            # style decides first
if intent.has_text_or_numbers:           engine = "remotion"   # hard rule
if intent.speech_lang == "ar-TN":        engine += "+lipsync" or reframe to hide mouth
if intent.needs_subject_refs and engine == "veo": duration = 8     # Veo refs force 8 s
if intent.importance == "hook":          N = style.qc.hook_takes (5-8) else N = 3
if budget.remaining < estimate(engine,N): downgrade (Veo Std→Fast→Omni→2.5D) and log it

2.3 Upscaling and frame rate

2.4 Budget per style variant (45 s ad, ~28 shots, ~12 AI) [proposal, prices §2.1]

Item Calc $
NB Pro style frame (once per style, amortized) 3 × $0.134 0.40
NB2 keyframes 14 × $0.101 (+30 % re-gens) 1.8
Omni takes 11 shots × 3 × 4 s × $0.10 + hook 1 × 8 × 4 s × $0.10 16.4
Judges (Gemini 3.1 Pro/3.x Flash) ~45 clips + ~20 stills + final ~1.5
Lyria bed, TTS < 0.2
Total AI-heavy style ≈ $20
Code-heavy style (2D/collage/kinetic) NB2 assets + 0-2 Omni shots ≈ $1-5
Extra hook variant (same body) 1 shot × 6 takes × 4 s × $0.10 + Remotion ≈ $2.5

Our real solar v2 cost ≈ $15 [ours], consistent with this.


3. Quality at 10/10, automatically

3.1 What the research says about automatic judges

3.2 Gemini as judge: frame sampling and cost [official]

3.3 The rubric: hard gates (deterministic) + checklist (VLM) + pairwise (VLM) [proposal]

"10/10" = every hard gate passes, every checklist item is "yes", and the ad wins or ties the style's golden ad in a blind pairwise judgment (both orders). Owner's phone watch is the final gate.

A. Hard gates (code, no model opinion)

# Dimension Check Tool Threshold
A1 Hook frame 0 lit and readable; first visual change ≤ 0.5 s; VO or hook text starts ≤ 0.3 s signalstats YAVG on frame 0; scene/motion diff; align.json YAVG(frame0) ≥ 90; change ≤ 15 frames
A2 Text correctness every on-screen string equals the approved string, char by char; RTL order by pixel position Remotion props diff + OCR on stills (Gemini high-res OCR) 0 differences [ours]
A3 Readability text height and dwell Remotion layout metrics Arabic cap ≥ 56 px at 1080 w; dwell ≥ 0.4 s + 0.25 s/word; contrast ≥ 4.5:1
A4 Safe zones nothing essential in Reels UI overlays bounding boxes from Remotion top 14 % and bottom 35 % clear (our rule: top 270 / bottom 670 px) [practitioner]
A5 No text in AI shots zero glyphs in any AI clip OCR (Gemini or PaddleOCR, Arabic+Latin) on 2 fps frames 0 detections [ours]
A6 No inserted cuts in the used segment shot boundary PySceneDetect / pipeline/alarm/cuts.py 0 in kept range [ours]
A7 No frozen/black frames, no flicker ffmpeg freezedetect, blackdetect, luma delta per frame none;
A8 Edit rhythm cuts on beats/words; mean shot length timeline.json 100 % cuts within ±1 frame of beat or word; mean 1.2-2.0 s
A9 Seam energy no clip ends on a "settling" still optical-flow magnitude in the last 6 frames before each AI cut ≥ 40 % of the clip's median flow [proposal]
A10 Audio loudness ffmpeg ebur128 −14 ±1 LUFS integrated, ≤ −1.5 dBTP [ours]
A11 VO intelligibility transcript vs script Gemini transcript (WER) + keywords WER ≤ 5 %, all keywords present [ours]
A12 Mix VO over music loudness per stem music −12 to −18 LU under VO while speaking
A13 Technical container/colour ffprobe 1080×1920, 30 (or 24) fps, H.264 High, yuv420p, color_range=tv, BT.709, AAC 48 kHz [ours]
A14 Brightness whole film signalstats mean YAVG 90-190 [ours]
A15 Brand + CTA logo on end card ≥ 1.5 s; CTA said and shown in the last 5 s; brand named by 3 s (if the concept allows) timeline + props present
A16 Style drift (stills) CSD similarity to golden set CSD embedding, z-score vs in-style distribution z ≥ qc.style_sim_min_z (calibrate)
A17 Compliance claims match brief facts; no fake testimonial/review; AI-disclosure need recorded brief facts list, human pass

B. VLM checklist (binary items; Gemini 3.1 Pro; each "no" needs a timestamp) Per AI shot: identity matches sheet · hands (5 fingers, no fusion) · faces stable (no melting, eyes aligned) · objects keep shape (no morphing) · physics plausible · no glyph-like marks · on-style (bible's look + judge_extra_checks) · intent fulfilled (the action in the intent happens in the kept segment) · no "AI tells" from research 07 (glow, bokeh, sparkles, waxy skin). Per ad: hook creates a question or surprise in 2 s · message understood with sound off · every beat's fact visible · product/offer clear · CTA explicit · pacing never drags (flag any 0.7 s+ of dead air) · music mood fits the style · SFX on visible impacts · nothing looks unintentionally AI.

C. Pairwise (selection and the final bar) - Take selection: round-robin among N takes of a shot, both orders; winner = most wins; ties → deterministic tiebreaker (higher motion energy at the trim point, then fewer checklist warnings). - Ad-level bar: new ad vs golden_ad.mp4 of the same style, both orders, rubric in the prompt. "Win or tie in both orders" = pass. A new winner can become the new golden ad after owner OK (a ratchet).

3.4 Making the judge reliable [proposal, grounded in §3.1]

  1. Deterministic checks first, VLM second: cheaper and never hallucinate. Reject before paying the judge.
  2. Binary questions with evidence, structured JSON output ({item, pass, timestamp, evidence}); reject any "fail" without a timestamp and any "pass" on an item the judge could not see (e.g. hands not in frame = n/a).
  3. Pairwise, both orders, temperature 0-low; disagreement between orders = tie → escalate (third call at higher fps or to the owner's picks strip).
  4. Few-shot from our own defect library: 1-2 labeled frames per failure (inserted cut, hex code text, garbled Arabic, green "lime-white", glossy clay…). ArtifactLens shows a few hundred labels per category are enough for a strong VLM detector; we can start with 10-20 per category.
  5. Sampling that fits the failure: 8-10 fps and high media resolution for 4 s takes (morphs and hands happen between 1 fps samples); 1-2 fps low for whole-ad pacing reviews.
  6. Calibrate against the owner. Label ~150 past takes (keep/reject + reason; we already have v1/v2/alarm takes and picks.json). Automate a gate only if judge-owner agreement is high (Cohen's kappa ≥ 0.7); otherwise keep it advisory. Re-check each quarter and whenever a model id changes.
  7. Self-preference: Gemini judging Gemini output. Pairwise among same-model takes cancels most of it; for the final ad, add a cross-vendor second opinion on stills (e.g. Claude) as advisory.
  8. Track judge vs market: correlate judge scores with hook rate/CPL (§4.4). If the judge prefers ads the market rejects, change the rubric, not the market.

3.5 Style-consistency metrics [proposal]

3.6 Best-of-N strategy and costs [proposal]


4. Creative testing and learning loops

4.1 What Meta says (official)

4.2 What practitioners add (treat numbers with care)

4.3 The variant matrix for Lead Up [proposal]

4.4 Naming, tracking and the learning loop [proposal]

Ad name (parsable, like Motion's DATE_FORMAT_HOOK_ANGLE parsing):

LU_{client}_{offer}_{concept}_{angle}_{style}@{ver}_{hook}_{cta}_{len}s_{yyyymmdd}
LU_SOLARTN_PAYONSALE_C03_riskrev_clay3d@1.3_H2-priceflood_WA-SOLAR_45s_20260927

The same fields live in ads/<slug>/manifest.json (plus model ids, prompt template versions, costs, QC results), so ad name ↔ manifest is a join key. Motion-style AI tagging (visual format, messaging angle, hook, talent, product, …) can be replicated by running a VLM tagger over every exported ad and storing tags next to the manifest [practitioner: Motion tags 8 dimensions].

Loop: 1. Pull per-ad daily metrics (Marketing API insights): impressions, 3 s views (hook rate = 3 s views / impressions), ThruPlays (hold rate), CTR, cost per lead / per WhatsApp conversation, and the qualified-lead outcome from Lead Up's CRM (the real target). 2. Join on ad name → manifest features: style, angle, hook type, first-frame brightness, face in first second, cut rate, text density, music BPM, QC scores. 3. Model small data honestly: hierarchical/Bayesian shrinkage (Beta-Binomial on hook rate, Gamma on CPL) per feature; report "probability style X beats Y on hook rate" instead of p-values. 4. Write back to the engine: style win rates per vertical in library/styles/*/stats.json; winning frames join the golden set; winning hooks become templates; losing patterns join the defect/"don't" lists; planner allocates next batch ~50 % new concepts / 30 % variants of winners / 20 % proven (practitioner split). 5. Check the judge (§3.4.8) against market results each cycle.

4.5 Guardrails for AI UGC and claims

Meta and most markets treat fabricated testimonials as deceptive; the brief's facts list is the only source of claims. AI UGC should be framed as a presenter or a dramatization, not a fake customer review. Record the AI-disclosure decision per ad. SynthID is embedded in Google outputs [official].


5. Workflow orchestration

5.1 Gates (owner time budget ≈ 15 min per ad) [proposal, matches repo rules 1-2]

Gate Who What the owner sees Spend unlocked
G0 Script owner script + lint report VO (cents)
G1 Styles owner 1-card-per-style preview (style frame, palette, 3 s motion sample, cost estimate) per-style keyframes
G1b New style (rare) owner style frame + 6 golden stills + 10 s test adds a style to the library (amortized)
G2 Picks (optional) auto, owner may glance contact strip of chosen takes with judge notes none (already spent)
G3 Final owner phone watch, sound on/off, QC report publish

Everything between gates is automatic. Each gate shows cost so far and projected cost (rule "each paid gate needs a go").

5.2 Pipeline as idempotent steps [proposal]

ads/<slug>/manifest.json   pins: script hash, style@ver, prompt templates@ver, model ids, budget cap
steps (each: inputs → outputs, skip if output hash matches):
  vo → align → beats → intents → [per style] music → timeline → keyframes → keyframe_qc
     → takes → take_qc (gates + checklist + pairwise) → picks → remotion_render → master → final_qc → publish

5.3 How the market tools map to this


  1. Refactor v2 into layers (1-2 days): intents.json from the current shot list; library/styles/clay3d/ from the existing kit; theme.json read by the Remotion scenes; manifest + run ledger + cache in pipeline/.
  2. Deterministic QC (1 day): gates A1-A14 as one script producing qc_report.json + a contact sheet.
  3. Judge v1 (1-2 days): take-level pairwise + checklist with Gemini 3.1 Pro; label 150 past takes; measure agreement; keep advisory until kappa is good.
  4. Second and third styles chosen for cost and diversity: one code-heavy (2D flat or kinetic type), one AI-heavy (photoreal cinematic or anime). Each: style frame (NB Pro) → golden stills → 10 s test → owner G1b.
  5. Re-render the approved solar script in 3 styles — the proof that the engine works — then the first real Meta test pack (6-10 ads) with the naming scheme and daily metrics pull.
  6. Derja talking-head test (small, paid gate): Omni/Veo silent performance + lip-sync to our VO; decide whether AI UGC is viable in Derja.

7. Open questions and unknowns


Sources

Google (official) - Gemini API pricing (Veo 3.1 tiers, Omni token rate, NB2, Lyria, Gemini 3.x): https://ai.google.dev/gemini-api/docs/pricing - Veo 3.1 in the Gemini API (durations, 8 s rule, refs, extend 148 s, seed, latency, retention): https://ai.google.dev/gemini-api/docs/veo - Ultimate prompting guide for Veo 3.1 (formula, camera vocab, audio syntax, negatives, ingredients, timestamps): https://cloud.google.com/blog/products/ai-machine-learning/ultimate-prompting-guide-for-veo-3-1 - Introducing Veo 3.1 (developers blog): https://developers.googleblog.com/introducing-veo-3-1-and-new-creative-capabilities-in-the-gemini-api/ - Vertex: generate videos from references (asset vs style refs): https://docs.cloud.google.com/vertex-ai/generative-ai/docs/video/use-reference-images-to-guide-video-generation - Vertex: Veo 3.1 model page: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/veo/3-1-generate - Vertex: extend Veo videos: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/video/extend-a-veo-video - Gemini Omni API doc (specs, languages, refs, restyle, extension): https://ai.google.dev/gemini-api/docs/omni - Gemini Omni prompt guide (DeepMind): https://deepmind.google/models/gemini-omni/prompt-guide/ - Gemini Omni 1.1 Flash on Agent Platform: https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/gemini/omni-1-1-flash - Gemini Omni Flash model card: https://deepmind.google/models/model-cards/gemini-omni-flash/ - Nano Banana image generation doc (reference limits, thinking, grounding): https://ai.google.dev/gemini-api/docs/image-generation - Nano Banana 2 model page: https://deepmind.google/models/gemini-image/flash/ - How to prompt Gemini 2.5 Flash Image (templates, semantic negatives): https://developers.googleblog.com/en/how-to-prompt-gemini-2-5-flash-image-generation-for-the-best-results/ - Gemini video understanding (1 fps default, custom fps, clipping, 10 videos/request): https://ai.google.dev/gemini-api/docs/video-understanding - Gemini media resolution (tokens per image/frame): https://ai.google.dev/gemini-api/docs/media-resolution

Meta (official) - Meta Andromeda (engineering blog): https://engineering.fb.com/2024/12/02/production-engineering/meta-andromeda-advantage-automation-next-gen-personalized-ads-retrieval-engine/ - The Creative Advantage: diversification with Andromeda: https://www.facebook.com/business/news/the-creative-advantage-unlocking-the-power-of-diversification-with-meta-andromeda - Demystifying creative diversification: https://www.facebook.com/business/news/demystifying-creative-diversification - Advantage+ creative: https://www.facebook.com/business/ads/meta-advantage-plus/creative

Papers and model repos - GenArena (pairwise vs pointwise VLM judging): https://arxiv.org/abs/2602.06013 - VLM Judges Can Rank but Cannot Score: https://arxiv.org/html/2604.25235v1 - RAVEN-Eval (rubric-guided pairwise video evaluation): https://arxiv.org/abs/2608.09111 - Judging LLM-as-a-judge (MT-Bench; biases and mitigations): https://arxiv.org/abs/2306.05685 - VideoScore2: https://huggingface.co/papers/2509.22799 - Video-T1 test-time scaling: https://arxiv.org/abs/2503.18942 and https://arxiv.org/html/2503.18942v1 - Diagnosis-guided candidate recycling (related TTS work): https://arxiv.org/pdf/2608.29322 - ArtifactLens (few-label artifact detection): https://arxiv.org/abs/2602.09475 - CSD, Measuring Style Similarity in Diffusion Models: https://arxiv.org/abs/2404.01292 - When style similarity scores fail (CSD calibration): https://arxiv.org/pdf/2605.09030 - HPSv3: https://github.com/MizzenAI/HPSv3 - Aesthetic Predictor v2.5 (SigLIP): https://github.com/discus0434/aesthetic-predictor-v2-5 - Q-Align: https://proceedings.mlr.press/v235/wu24ah.html - SeedVR2 (video restoration/upscaling): https://github.com/ByteDance-Seed/SeedVR

Practitioner, vendor and press - Omni pricing breakdown per resolution: https://www.eesel.ai/blog/gemini-omni-1-1-flash-pricing - Omni reference-driven video (tags, limits): https://runware.ai/docs/models/google-gemini-omni-flash/guides/reference-driven-video - Omni review (duration, audio sync): https://www.buildfastwithai.com/blogs/gemini-omni-flash-review-google-ai-video-model-2026 - Veo 4 status (not released): https://wavespeed.ai/blog/posts/what-is-veo-4/ - Veo 3.1 Lite vs Fast vs Standard: https://www.buildfastwithai.com/blogs/google-veo-3-1-ai-video-generator - Veo dialogue/lip-sync in other languages: https://prompt-architects.com/blog/101-veo-dialogue-prompts and https://github.com/ulmeanuadrian/kie-mcp/issues/1 - Lip-sync models: https://sync.so/lipsync-2-pro , https://lipsync.com/blog/open-source-lip-sync - NB2 practical guide (roles for references): https://masonry.so/blog/nano-banana-2-guide - NB2 consistency (no style slot in NB2, Pro has 3): https://www.glbgpt.com/hub/nano-banana-2-subject-consistency/ - Andromeda playbook (concepts, cadence, similarity claims): https://segwise.ai/blog/meta-andromeda-update-creative-strategy-2026 - Entity ID explainer: https://www.artifexdigital.co/blog/understanding-metas-entity-id-why-your-28-ads-might-actually-be-3 - Creative testing frameworks: https://paidmediaworld.com/meta-creative-testing-framework/ , https://adlibrary.com/posts/creative-angle - Reels safe zones and hook guidance: https://adadvisor.ai/blog/facebook-reels-ads , https://withblip.com/blog/how-to-use-meta-ad-safe-zones-without-cutting-off-your-creative/ - Motion AI tagging: https://www.foxwelldigital.com/blog/ai-tagging-the-end-of-naming-convention-chaos-and-the-start-of-smarter-creative-analysis , https://www.motionapp.com/releases/introducing-ai-tagging - Foreplay: https://www.foreplay.co - Pencil platform: https://trypencil.com/the-platform - Icon: https://icon.com - AI UGC tools 2026: https://morphic.com/resources/tools/best-ai-ugc-ad-tools , https://novoads.ai/en/blog/arcads-vs-creatify - Promise studio (Muse records prompts, settings, approvals): https://www.forbes.com/sites/maureenkerr/2026/07/24/dave-clarks-ai-studio-promise-aims-for-films-hollywood-can-release/ - Asteria Continuum Suite: https://deadline.com/2026/03/asteria-launches-continuum-suite-ai-operating-system-film-tv-production-1236749265/ - Remotion parameterized rendering: https://www.remotion.dev/docs/parameterized-rendering - Langfuse prompt management: https://langfuse.com/docs/prompts/get-started

Internal - ai-docs/01-architecture.md, ai-docs/03-lessons-learned.md, ai-docs/04-qc-checklists.md, research/01, 05-09.