Master Agent Packages

Source: Hybrid templates for media-assets management.md · Verbatim Directives & Code

GLOBAL RULES

Universal Agent Protocol & Global Invariants Package

9 Executable Code Blocks

Full self-contained global rules (Cognitive Pipeline, Tiered QA, Anti-Hallucination, Image Rules, OS Adaptation) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 9 Code Blocks
Paste-ready package · 300 lines from original file
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |
TEMPLATE U1

Template U1 — Screen → 9:16 Short-Form Master Package

19 Executable Code Blocks

Verbatim executive directives and code for Template U1 (9:16 Short-Form video from screen recording) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 19 Code Blocks
Paste-ready package · 301 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
# ===== CODE FOR UNIFIED TEMPLATE U1 =====

### U1 — Screen → 9:16 Short-Form

**Agent directive: You are converting a screen recording or screen capture into a vertical 9:16 short-form video optimized for TikTok, Instagram Reels, and YouTube Shorts. Follow every step in order. Do not skip probing. Do not skip QA.**

1. **Always probe before grading.** If source is already 1080×1920, skip reframe.
2. **Hook must work on mute.** Test by playing first 3s with volume at zero — if message is unclear, your captions/visuals failed.
3. **Count-up numbers > static pop.** For Dose Calculator results, animate 0→final in 0.5–0.8s with a soft pop SFX. Never just flash the number.
4. **Two variants minimum.** Always cut an ultra-tight (12–22s) AND a standard (25–40s). Different platforms reward different lengths.
5. **Zoom target = the number, not the full screen.** Ken Burns should drift toward the result/dose/value on screen.

### 1. Probe any file first (always start here)

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

Pro-tip: Pipe to `jq` for quick checks:

```bash
# Duration only
ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4

# Resolution
ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 input.mp4

# FPS
ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 input.mp4
```


### 2. Archive raw (always before any edit)

```bash
mkdir -p "$OUT_DIR/raw_archive"
cp "$RAW_PATH" "$OUT_DIR/raw_archive/"
```


### 3. HDR → SDR (run if ffprobe shows bt2020/hlg/pq)

```bash
ffmpeg -i "$RAW_PATH" \
  -vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709:t=bt709:m=bt709,tonemap=hable:desat=0,zscale=t=bt709,format=yuv420p" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  "$OUT_DIR/sdr.mp4"
```


### 4. Crop browser chrome / dock

```bash
# Pro-tip: measure chrome height with ffprobe frame grab first
# macOS: Top ~80px chrome + bottom ~80px dock = crop 160px total
# Linux: chrome height varies by DE — probe a frame first, measure manually
# ponytail: on Retina macOS, actual pixel count is 2x logical — crop AFTER downscale
ffmpeg -i raw.mp4 \
  -vf "crop=in_w:in_h-160:0:80" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  cropped.mp4
```


### 5. Brand grade (Clean High-Key — Dose/Dentist default)

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.05:saturation=0.92,curves=m='0/0.03 0.5/0.52 1/0.96'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded.mp4
```

Pro-tip: For **Female ProMedic** (warm rose), shift saturation up and add warmth:

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.04:saturation=0.97,colorbalance=rs=0.04:gs=-0.01:bs=-0.03,curves=m='0/0.04 0.5/0.53 1/0.97'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_female.mp4
```

Pro-tip: For **Coach ProMedic** (neutral-warm, slightly saturated):

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.04:contrast=1.06:saturation=1.03,curves=m='0/0.02 0.5/0.53 1/0.98'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_coach.mp4
```


### 6. Ken Burns from still image → clip

```bash
# ponytail: ALWAYS -framerate 30 on image inputs (VFR trap kills sync)
ffmpeg -framerate 30 -loop 1 -t 4 -i still.png \
  -vf "scale=3840:2160,zoompan=z='min(zoom+0.001,1.15)':d=120:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920" \
  -c:v libx264 -t 4 -pix_fmt yuv420p \
  ken_burns.mp4
```

Pro-tip: Change `1.15` to your app's `{ZOOM_MAX}`. For Coach use `1.22`, for Dose/Dentist use `1.15`.


### 7. Image folder → slideshow video

```bash
# ponytail: -framerate 30 is mandatory, not optional
# Linux note: if glob fails, verify images are actually PNG/JPG — some Linux tools save without extension
ffmpeg -framerate 30 -pattern_type glob -i 'images/*.png' \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,zoompan=z='min(zoom+0.0008,1.12)':d=90:s=1080x1920" \
  -c:v libx264 -pix_fmt yuv420p -r 30 \
  slideshow.mp4
```


### 9. Mux voiceover — no A/V drift

```bash
# ponytail: apad + -shortest is the A/V drift killer. Never skip both.
ffmpeg -i video.mp4 -i voiceover.wav \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -af "apad" -shortest \
  muxed.mp4
```


### 10. Loudnorm (EBU R128 — all platforms)

```bash
# Standard: -14 LUFS (YouTube, IG, FB, LinkedIn)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized.mp4

# TikTok loud-feed variant: -11 to -12 LUFS
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized_loud.mp4
```


### 17. Whisper transcription (for captions — U3 step 0)

```bash
# ponytail: word-level timestamps are required for kinetic captions
# Whisper is 100% free, local, no API key — runs on CPU or GPU
# Linux GPU tip: install torch with CUDA first for 5-10x speed boost
whisper input.mp4 --model base --language en --output_format json --word_timestamps True --output_dir "$OUT_DIR"
```

Pro-tip: For better accuracy on medical/technical terms, use `--model small` or `--model medium` (still free, just slower).


### 18. Caption burn-in from SRT (free, local)

```bash
# Burn .srt subtitles directly into the video
# Linux: verify font exists first — fc-list | grep -i arial
# If Arial missing on Linux, use: FontName=DejaVu Sans
ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontName=Arial,FontSize=22,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=2'" \
  -c:v libx264 -crf 18 -c:a copy \
  captioned.mp4
```

Pro-tip: For kinetic-style word-highlight, use `pysubs2` to split SRT into 2–4 word groups:

```bash
python3 -c "
import pysubs2
subs = pysubs2.load('captions.srt')
# pysubs2 is free: pip install pysubs2
for line in subs:
    words = line.text.split()
    # Split into 3-word chunks with even timing
    chunk_size = 3
    duration = line.end - line.start
    chunks = [words[i:i+chunk_size] for i in range(0, len(words), chunk_size)]
    for i, chunk in enumerate(chunks):
        t0 = line.start + (duration * i // len(chunks))
        t1 = line.start + (duration * (i+1) // len(chunks))
        print(f'{pysubs2.time.ms_to_str(t0)} --> {pysubs2.time.ms_to_str(t1)}')  
        print(' '.join(chunk))
"
```


### 14. Final export — short-form (U1/U5 output)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 15M -maxrate 20M -bufsize 30M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1920:flags=lanczos" \
  "$OUT_DIR/short_916.mp4"
```


### 13. Extract frames for QA check

```bash
# Grab frames at 25%, 50%, 75% of duration for visual QA
DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
for pct in 25 50 75; do
  T=$(echo "$DURATION * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i input.mp4 -frames:v 1 "qa_frame_${pct}pct.png" -y
done
```
===========================================

16:9
----

### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### D) Platform Adaptation — Linux vs macOS

**Agent: detect OS with `uname -s` before running any capture or render command. Adapt tool paths, fonts, and capture methods accordingly.**

**Linux 16:9 screen recording — sharp text protocol:**
```bash
if [ "$(uname -s)" = "Linux" ]; then
  # Install rendering fonts (MANDATORY before any text/subtitle work)
  apt install -y fonts-liberation fonts-dejavu-core fontconfig xvfb 2>/dev/null
  fc-cache -fv

  # Virtual display at 2x target (3840x2160 for 1920x1080 output)
  Xvfb :99 -screen 0 3840x2160x24 -ac &
  export DISPLAY=:99

  # Capture with lanczos downscale — this is what prevents blurry text
  ffmpeg -video_size 3840x2160 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1920:1080:flags=lanczos" \
    -tune stillimage \
    raw_capture_169.mp4
fi
```

**macOS 16:9 screen recording:**
```bash
if [ "$(uname -s)" = "Darwin" ]; then
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1920:1080:flags=lanczos" \
    raw_capture_169.mp4
fi
```

**Why Linux screen recordings produce blurry text — root causes and fixes:**
1. **No Xvfb or low resolution Xvfb** → text rendered at low res then upscaled = blur. Fix: Xvfb at 2x target.
2. **Missing system fonts** → fallback bitmap fonts are ugly. Fix: install fonts-liberation + fonts-dejavu-core.
3. **Default bilinear scaling** → smears text edges. Fix: always use `flags=lanczos` in scale filter.
4. **High CRF** → not enough bits for sharp edges. Fix: CRF 14 for screen content, not 18+.
5. **Wrong pixel format** → chroma subsampling on text. Fix: capture in yuv444p if possible, output in yuv420p.
6. **No -tune stillimage** → encoder wastes bits on motion that doesn't exist in mostly-static screens.
TEMPLATE U2

Template U2 — Screen → 16:9 Long + Feed Master Package

10 Executable Code Blocks

Verbatim executive directives and code for Template U2 (16:9 Long-Form & Derivative 4:5/1:1 feed clips) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 10 Code Blocks
Paste-ready package · 170 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
# ===== CODE FOR UNIFIED TEMPLATE U2 =====

### U2 — Screen → 16:9 Long + Feed

**Agent directive: You are converting a screen recording into a horizontal 16:9 long-form video for YouTube, plus derivative feed crops (4:5 and 1:1) for Instagram/Facebook feed. The short 9:16 version (U1) must be built first as the grade reference — never start with 16:9 directly.**

1. **Build 9:16 first, then expand.** Never start with long-form — you'll drift the grade. Short is the locked reference.
2. **Kinetic captions only in the hook.** After the first 5–12s, switch to steady full-clause subtitles. Constant kinetic text fatigues viewers on 3+ minute videos.
3. **Chapters are mandatory for YouTube.** Mark: Problem / Solution / Proof / How it works / CTA.
4. **Grid crop test for 4:5.** The key content must sit inside the central 1080×1080 band (y:135–1215) because Instagram crops to square on the profile grid.
5. **End-screen zone.** Keep the last 5–20s clear in the central 90% for YouTube end-screen cards.

### 1. Probe any file first (always start here)

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

Pro-tip: Pipe to `jq` for quick checks:

```bash
# Duration only
ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4

# Resolution
ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 input.mp4

# FPS
ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 input.mp4
```


### 2. Archive raw (always before any edit)

```bash
mkdir -p "$OUT_DIR/raw_archive"
cp "$RAW_PATH" "$OUT_DIR/raw_archive/"
```


### 5. Brand grade (Clean High-Key — Dose/Dentist default)

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.05:saturation=0.92,curves=m='0/0.03 0.5/0.52 1/0.96'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded.mp4
```

Pro-tip: For **Female ProMedic** (warm rose), shift saturation up and add warmth:

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.04:saturation=0.97,colorbalance=rs=0.04:gs=-0.01:bs=-0.03,curves=m='0/0.04 0.5/0.53 1/0.97'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_female.mp4
```

Pro-tip: For **Coach ProMedic** (neutral-warm, slightly saturated):

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.04:contrast=1.06:saturation=1.03,curves=m='0/0.02 0.5/0.53 1/0.98'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_coach.mp4
```


### 15. Final export — long-form (U2 output)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 20M -maxrate 25M -bufsize 40M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1920:1080:flags=lanczos" \
  "$OUT_DIR/long_169.mp4"
```


### 16. Re-crop for feed formats (U4 output)

```bash
# 4:5 feed (1080x1350) — re-center, don't blind-crop
ffmpeg -i master.mp4 \
  -vf "scale=1080:1350:force_original_aspect_ratio=decrease,pad=1080:1350:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_45.mp4"

# 1:1 feed (1080x1080)
ffmpeg -i master.mp4 \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_11.mp4"
```


### 10. Loudnorm (EBU R128 — all platforms)

```bash
# Standard: -14 LUFS (YouTube, IG, FB, LinkedIn)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized.mp4

# TikTok loud-feed variant: -11 to -12 LUFS
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized_loud.mp4
```


### 13. Extract frames for QA check

```bash
# Grab frames at 25%, 50%, 75% of duration for visual QA
DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
for pct in 25 50 75; do
  T=$(echo "$DURATION * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i input.mp4 -frames:v 1 "qa_frame_${pct}pct.png" -y
done
```
=========================================
general enhancements
--------------------

### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you
TEMPLATE U3

Template U3 — General Enhance Master Package

19 Executable Code Blocks

Verbatim executive directives and code for Template U3 (General Video Enhancement) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 19 Code Blocks
Paste-ready package · 527 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
# ===== CODE FOR UNIFIED TEMPLATE U3 =====

### U3 — General Enhance

**Agent directive: You are enhancing an existing video — improving its pacing, captions, audio mix, and visual quality for maximum viewer retention. This template works on any aspect ratio. Probe the input first to determine what you're working with, then apply enhancements in order. Do not re-grade if the video already has a brand grade applied.**

1. **Name the tone in one word.** If you need three effects to describe your look, simplify. "Clinical." "Warm." "Gritty." One word.
2. **Script density = ~1 idea per 15s.** Don't cram multiple claims into one breath.
3. **Visual change every 7–10s minimum** for feed content (not just when the script changes — independently).
4. **Effects on app UI are poison.** Never stack grain + light-leak + blur + chromatic aberration on clinical numeric screens. Use `app-tech` kit = clean high-key, zero grain on numbers.
5. **Speed ramp recipe:** 1.5–2× on setup/context, normal or slight slow on payoff/result reveal
6. **Promedic pack is conditional.** Detect Dose / Female / Coach / Dentist ProMedic first. If not Promedic → skip brand table, per-app zoom tiers, app CTAs, and platform picks; use general KIT+PACE only. If Promedic → force `app-tech`, inject brand table, enforce per-app zoom ceilings (hook included), format safe zones, short-form-first multi-export, save-reason CTAs, and app platform emphasis. Report `promedic_pack=applied|skipped`.
7. **When Promedic: identity = pacing, not only color.** Coach must feel faster (interrupts ~1.8–2.8s, wider zoom). Dose/Dentist stay precise. Female warmer/honest — not clinical-cold. Color alone does not brand the family.

### 1. Probe any file first (always start here)

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

Pro-tip: Pipe to `jq` for quick checks:

```bash
# Duration only
ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4

# Resolution
ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 input.mp4

# FPS
ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 input.mp4
```


### 12. Silence detection (for dead-air cuts)

```bash
# Find silences longer than 0.4s at -30dB threshold (hyper pace)
ffmpeg -i input.mp4 -af "silencedetect=noise=-30dB:d=0.4" -f null - 2>&1 | grep "silence_"

# For balanced pace, use d=0.55
# For story pace, use d=0.75
```


### 19. Scene/shot detection (free, for interrupt placement)

```bash
# Detect scene changes — useful for finding where to place pattern interrupts
ffmpeg -i input.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep showinfo
# Adjust 0.3 threshold: lower = more sensitive, higher = fewer cuts
```


### 17. Whisper transcription (for captions — U3 step 0)

```bash
# ponytail: word-level timestamps are required for kinetic captions
# Whisper is 100% free, local, no API key — runs on CPU or GPU
whisper input.mp4 --model base --language en --output_format json --word_timestamps True --output_dir "$OUT_DIR"
```

Pro-tip: For better accuracy on medical/technical terms, use `--model small` or `--model medium` (still free, just slower).


### 18. Caption burn-in from SRT (free, local)

```bash
# Burn .srt subtitles directly into the video
# Linux: verify font exists first — fc-list | grep -i arial
# If Arial missing on Linux, use: FontName=DejaVu Sans
ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontName=Arial,FontSize=22,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=2'" \
  -c:v libx264 -crf 18 -c:a copy \
  captioned.mp4
```

Pro-tip: For kinetic-style word-highlight, use `pysubs2` to split SRT into 2–4 word groups:

```bash
python3 -c "
import pysubs2
subs = pysubs2.load('captions.srt')
# pysubs2 is free: pip install pysubs2
for line in subs:
    words = line.text.split()
    # Split into 3-word chunks with even timing
    chunk_size = 3
    duration = line.end - line.start
    chunks = [words[i:i+chunk_size] for i in range(0, len(words), chunk_size)]
    for i, chunk in enumerate(chunks):
        t0 = line.start + (duration * i // len(chunks))
        t1 = line.start + (duration * (i+1) // len(chunks))
        print(f'{pysubs2.time.ms_to_str(t0)} --> {pysubs2.time.ms_to_str(t1)}')  
        print(' '.join(chunk))
"
```


### 11. Duck music under voiceover

```bash
# ponytail: volume=0.3 ducks music to ~10dB below VO; amix merges them
ffmpeg -i voiceover.wav -i music.mp3 \
  -filter_complex "[1:a]volume=0.3[music];[0:a][music]amix=inputs=2:duration=first:dropout_transition=2[out]" \
  -map "[out]" -c:a aac -b:a 192k -ar 48000 \
  mixed_audio.aac
```

Pro-tip: For dynamic ducking (music dips only when VO is speaking), use FFmpeg's `sidechaincompress`:

```bash
ffmpeg -i voiceover.wav -i music.mp3 \
  -filter_complex "[0:a]asplit=2[vo][sc];[1:a][sc]sidechaincompress=threshold=0.02:ratio=6:attack=200:release=1000[ducked];[vo][ducked]amix=inputs=2:duration=first[out]" \
  -map "[out]" -c:a aac -b:a 192k -ar 48000 \
  mixed_dynamic.aac
```


### 20. Speed ramp (setup fast, payoff slow)

```bash
# 1.5× speed on setup (0-10s), normal speed on payoff (10s-end)
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]trim=0:10,setpts=PTS/1.5[fast];[0:v]trim=10,setpts=PTS-STARTPTS[slow];[fast][slow]concat=n=2:v=1:a=0[v];[0:a]atrim=0:10,atempo=1.5[afast];[0:a]atrim=10,asetpts=PTS-STARTPTS[aslow];[afast][aslow]concat=n=2:v=0:a=1[a]" \
  -map "[v]" -map "[a]" -c:v libx264 -crf 18 -c:a aac -ar 48000 \
  speed_ramped.mp4
```

---

## Pro-Tips by Template

### U1 — Screen → 9:16 Short-Form

1. **Always probe before grading.** If source is already 1080×1920, skip reframe.
2. **Hook must work on mute.** Test by playing first 3s with volume at zero — if message is unclear, your captions/visuals failed.
3. **Count-up numbers > static pop.** For Dose Calculator results, animate 0→final in 0.5–0.8s with a soft pop SFX. Never just flash the number.
4. **Two variants minimum.** Always cut an ultra-tight (12–22s) AND a standard (25–40s). Different platforms reward different lengths.
5. **Zoom target = the number, not the full screen.** Ken Burns should drift toward the result/dose/value on screen.

### U2 — Screen → 16:9 Long + Feed

1. **Build 9:16 first, then expand.** Never start with long-form — you'll drift the grade. Short is the locked reference.
2. **Kinetic captions only in the hook.** After the first 5–12s, switch to steady full-clause subtitles. Constant kinetic text fatigues viewers on 3+ minute videos.
3. **Chapters are mandatory for YouTube.** Mark: Problem / Solution / Proof / How it works / CTA.
4. **Grid crop test for 4:5.** The key content must sit inside the central 1080×1080 band (y:135–1215) because Instagram crops to square on the profile grid.
5. **End-screen zone.** Keep the last 5–20s clear in the central 90% for YouTube end-screen cards.

### U3 — General Enhance

1. **Name the tone in one word.** If you need three effects to describe your look, simplify. "Clinical." "Warm." "Gritty." One word.
2. **Script density = ~1 idea per 15s.** Don't cram multiple claims into one breath.
3. **Visual change every 7–10s minimum** for feed content (not just when the script changes — independently).
4. **Effects on app UI are poison.** Never stack grain + light-leak + blur + chromatic aberration on clinical numeric screens. Use `app-tech` kit = clean high-key, zero grain on numbers.
5. **Speed ramp recipe:** 1.5–2× on setup/context, normal or slight slow on payoff/result reveal
6. **Promedic pack is conditional.** Detect Dose / Female / Coach / Dentist ProMedic first. If not Promedic → skip brand table, per-app zoom tiers, app CTAs, and platform picks; use general KIT+PACE only. If Promedic → force `app-tech`, inject brand table, enforce per-app zoom ceilings (hook included), format safe zones, short-form-first multi-export, save-reason CTAs, and app platform emphasis. Report `promedic_pack=applied|skipped`.
7. **When Promedic: identity = pacing, not only color.** Coach must feel faster (interrupts ~1.8–2.8s, wider zoom). Dose/Dentist stay precise. Female warmer/honest — not clinical-cold. Color alone does not brand the family.

### U4 — Platform-Specific

1. **Same grade, different pacing.** Never re-grade for a platform. Only change: crop, caption density, interrupts cadence, loudness, CTA language.
2. **LinkedIn is a different animal.** Skip zoom-punches for Dose/Dentist. Professional CTA. Trust > virality tricks. 16:9 or 1:1 preferred.
3. **TikTok vs Reels:** Almost identical pipeline, but TikTok rewards slightly louder (-11 LUFS vs -14) and faster hook pressure (1.0–1.3s intent vs ~2s for Reels).
4. **If master can't crop cleanly for a format, say so.** Mark INCONCLUSIVE and request a re-record. Don't force a broken crop.

### U5 — Viral Pipeline + Final Gate

1. **Step 1 (Hook) is highest ROI.** Spend 80% of your optimization time on the first 3 seconds.
2. **Loop engineering:** Match the last spoken word/visual fragment to flow back into the opening. Users rewatching = algorithm signal.
3. **QA is three-state.** PASS / FAIL / INCONCLUSIVE. "I think it's fine" = INCONCLUSIVE = not done.
4. **Max 3 re-render attempts.** If you can't pass QA in 3 tries, the input needs to change, not the render settings.

### U6 — Master Orchestrator

1. **Use U6 when you have raw material and want everything.** It chains A→J (capture → short → enhance → viral → long → feed → platform → SEO → app guardrails → final gate).
2. **Stage checkpoints.** After each stage (B, C, D...), verify the intermediate file before proceeding. Don't discover a grade problem at stage J.
3. **App guardrails are STOP conditions.** If any common mistake for the app appears in your plan or render → FAIL immediately. Don't finish and then check.

---

## Common Mistakes (from editor.addict.best §8)

### All Apps
- ❌ Starting with logo / greeting / slow fade → always cold-open
- ❌ Dead air longer than interrupt budget
- ❌ Captions outside safe zone
- ❌ Wrong safe-zone numbers for wrong format (e.g., using 9:16 zones on 16:9)
- ❌ SFX louder than voiceover
- ❌ Missing `-framerate 30` on image inputs
- ❌ Missing `apad` + `-shortest` when muxing audio
- ❌ Missing `-ar 48000` on final audio
- ❌ Claiming "done" without QA PASS
- ❌ Using default bilinear scaling on screen recordings (always use `flags=lanczos`)
- ❌ Screen-capturing on Linux without installing fonts first
- ❌ Capturing at target resolution instead of 2x then downscaling

### Per-App Traps
| App | Never do this |
|:---|:---|
| Dose Calculator | Invent/hallucinate medical numbers. Use energetic meme grade. Exceed 1.15× zoom. |
| Female ProMedic | Use childish pink (it's rose-gold/blush). Mix Coach energy/pacing. |
| Coach ProMedic | Use clinical restraint — Coach is the highest-energy app. Forget readability on form cues. |
| Dentist Pro | Use teal identical to Dose blue (Dentist = teal/cyan, Dose = clinical blue). Over-punch on short-form. |

---

## Quick Shell One-Liners

```bash
# Check if video is HDR
ffprobe -v quiet -select_streams v:0 -show_entries stream=color_transfer -of csv=p=0 input.mp4
# If output is "smpte2084" or "arib-std-b67" → needs HDR→SDR

# Verify A/V sync drift
ffprobe -v quiet -show_entries stream=start_time -of csv=p=0 input.mp4
# Both streams should start at ~0.000000

# Check final loudness (post-loudnorm verification)
ffmpeg -i final.mp4 -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | tail -20

# Batch probe all outputs
for f in "$OUT_DIR"/*.mp4; do
  echo "=== $f ==="
  ffprobe -v quiet -show_entries stream=width,height,r_frame_rate,codec_name -show_entries format=duration,size -of flat "$f"
done

# Verify no watermark (check for alpha/overlay in bottom-right)
ffmpeg -ss 1 -i input.mp4 -frames:v 1 -vf "crop=200:50:iw-200:ih-50" check_watermark.png

# Quick safe-zone overlay for 9:16 (visual check)
ffmpeg -i short.mp4 -vf "drawbox=x=60:y=250:w=870:h=1200:color=red@0.3:t=2" -t 5 safezone_check.mp4

# Detect OS for platform-specific commands
OS_TYPE="$(uname -s)"
echo "Running on: $OS_TYPE"  # Darwin=macOS, Linux=Linux

# Linux: verify fonts are installed (run before any subtitle work)
if [ "$(uname -s)" = "Linux" ]; then
  fc-list | head -5 || echo "FAIL: no fonts installed — run: apt install fonts-liberation fonts-dejavu-core"
fi
```

---

## Combining Templates — Real Examples

### Example 1: Dose Calculator → TikTok + Reels

```
1. Fill variables:  APP_NAME="Dose Calculator"  ZOOM_MAX="1.15"  WB="5900" ...
2. Run U1 (Screen → 9:16):
   - Archive raw → HDR check → crop chrome → grade (clean high-key) →
     Ken Burns on dose result → safe zone 1080×1920 →
     kinetic captions → count-up numbers → audio normalize →
     export short (12-22s) + standard (25-40s)
3. Run U3 (Enhance):
   - KIT=app-tech  PACE=balanced
   - Hook check → dead air cut → caption coverage check → loop attempt
4. Run U5 gate section:
   - QA all outputs → verify resolution, faceless, safe zone, zoom ≤ 1.15×
5. Run U4 SEO section:
   - Title: "Most clinicians miss this interaction — live dose fix"
   - Hashtags: #dosecalculator #clinicaltools #medicalapp
   - Save CTA: "Save this clinical reference"
```

### Example 2: Coach ProMedic → YouTube Long + IG Feed

```
1. Fill variables:  APP_NAME="Coach ProMedic"  ZOOM_MAX="1.22"  WB="5700" ...
2. Run U1 first (short master as grade reference)
3. Run U2 (Screen → 16:9 Long + Feed):
   - Expand from short master → 1920×1080 → chapters →
     kinetic hook only, then steady subtitles →
     interrupts every 6-10s → export long
   - Feed: re-center UI → 1080×1350 (4:5) + 1080×1080 (1:1)
4. Run U3 (Enhance) on long master
5. Run U5 (Final Gate) on all outputs
6. Run U4 SEO for YouTube:
   - Chapters: 0:00 Problem | 0:45 Solution | 1:30 Proof | 2:15 How | 3:00 CTA
   - Description first 150 chars = search intent
```

### Example 3: Full Production — U6 One-Shot

```
1. Fill variables + set JOBS="short_916,long_169,feed_45,feed_11,platform_pack"
2. U6 runs stages A→J automatically:
   A. Archive + HDR→SDR + chrome crop
   B. Short 9:16 master
   C. Enhance pass (retention ROI)
   D. Viral pipeline (hook→loop→variants)
   E. Long 16:9 from short
   F. Feed 4:5 + 1:1
   G. Platform pack (tiktok, reels, yt_shorts, yt_long, ig_feed_45, linkedin)
   H. SEO publish package per variant
   I. App guardrails check
   J. Final gate — all_pass required
3. Output: qa_report.md in {OUT_DIR}/reports/
```

---

## Six Deadly Sins (memorize these)

1. **VFR trap** — always `-framerate 30` on image inputs
2. **A/V drift** — always `apad` + `-shortest` when muxing, always `-ar 48000`
3. **Context decay** — save state checkpoints after every stage
4. **False positive "done"** — only QA PASS counts (INCONCLUSIVE = not done)
5. **Blind acceptance** — visually verify frames at 25/50/75% duration
6. **Face leak** — faceless absolute on all brand content, always check generated assets

---

## Safe Zone Quick Reference

| Format | Canvas | Safe area for text/captions | Grid-crop safe |
|:---|:---|:---|:---|
| 9:16 short | 1080×1920 | x: 60–930, y: 250–1450 | N/A |
| 16:9 long | 1920×1080 | x: 120–1800, y: 70–1010 | N/A |
| 4:5 feed | 1080×1350 | Central 1080×1080 (y: 135–1215) | Square center |
| 1:1 feed | 1080×1080 | Central ~90% | Full frame |
| Stories | 1080×1920 | Same as 9:16 but stricter top/bottom | N/A |

---

## Pacing Cheat Sheet

| Preset | Silence cutoff | Interrupt cadence | Best for |
|:---|:---|:---|:---|
| `hyper` | 0.40 s | 2.0–2.6 s | TikTok, Reels, Coach short-form |
| `balanced` | 0.55 s | 2.8–3.8 s | General short-form, Dose/Dentist |
| `story` | 0.75 s | 4.0–5.5 s | Long-form, tutorials, Female storytime |

---

## Free Tools Stack (zero paid dependencies)

Every command in this file uses free, open-source, locally-run tools. No paid API keys needed (your AI agent API is the only external dependency).

| Tool | What it does | Install (macOS) | Install (Linux) | Cost |
|:---|:---|:---|:---|:---|
| **FFmpeg** | All video/audio processing, export, grade, crop, mux | `brew install ffmpeg` | `apt install ffmpeg` | Free |
| **ffprobe** | Media analysis, duration, resolution, codec detection | Included with FFmpeg | Included with FFmpeg | Free |
| **Whisper** | Speech → text with word timestamps (captions) | `pip install openai-whisper` | `pip install openai-whisper` | Free |
| **pysubs2** | SRT/ASS caption manipulation, chunking, timing | `pip install pysubs2` | `pip install pysubs2` | Free |
| **ImageMagick** | Image resize, overlay, thumbnail generation | `brew install imagemagick` | `apt install imagemagick` | Free |
| **bc** | Math in shell (duration calculations) | Pre-installed | Pre-installed (or `apt install bc`) | Free |
| **jq** | JSON parsing (ffprobe output, Whisper JSON) | `brew install jq` | `apt install jq` | Free |
| **Python 3** | Scripting for batch ops, caption splitting | Pre-installed | Pre-installed | Free |
| **Xvfb** | Virtual framebuffer for headless screen capture | N/A (not needed) | `apt install xvfb` | Free |
| **fonts-liberation** | System fonts for text rendering | Built-in | `apt install fonts-liberation` | Free |

**Not used, not needed:** No Adobe, no CapCut API, no RunwayML, no Eleven Labs, no paid cloud vision. The AI agent (your existing API) handles creative decisions; these tools handle execution.

---

## How to Merge This .md with editor.addict.best HTML

The goal: the HTML app has the 6 unified templates (the **what**). This .md has the commands and pro-tips (the **how**). Together they form one unified system. Here's how to combine them correctly.

### Method 1: AI Agent reads both files (simplest — recommended)

Give your AI agent both references in the system prompt or context:

```
You have two reference documents:
1. https://editor.addict.best — the master HTML app with 6 unified templates,
   conflict resolution laws, brand table, and full template text.
   Sections: #s1–#s9, #six-templates (u1–u6), #brand, #master
2. editor-pro-tips-and-commands.md — FFmpeg commands, pro-tips per template,
   combination recipes, free tool stack, QA one-liners.

Workflow:
- Pick template from the HTML (e.g. copy U1 from #u1-body)
- Fill {VARIABLES} using the brand table from HTML #brand
- Execute using FFmpeg commands from the .md
- QA using the shell one-liners from the .md
- SEO package using the .md combination examples
```

Pro-tip: The HTML has a search bar (press `/`) — tell the agent to use section IDs (`#s1`, `#u3`, `#brand`) to locate content fast.

### Method 2: Embed .md as a linked companion in the HTML

Add a single link in the HTML's nav or site-inventory section pointing to this file:

```html
<!-- Add to editor.addict.best nav section -->
<a href="/editor-pro-tips-and-commands.md">📋 Pro-Tips & Commands</a>
```

The .md is already served from the same web root at:
`https://editor.addict.best/editor-pro-tips-and-commands.md`

So the agent can fetch either file from the same domain. No merge needed — the HTML is the template source, the .md is the execution guide.

### Method 3: Section-to-section cross-reference

When chaining templates, map HTML sections to .md sections:

| Step | Read from HTML | Execute with .md |
|:---|:---|:---|  
| Pick template | `#u1-body` through `#u6-body` | — |
| Fill brand vars | `#brand` table | "Template Variables" block |
| Grade | Template's PHASE 2 step 1 | Recipe #5 (brand grade commands) |
| Motion / Ken Burns | Template's PHASE 2 step 2 | Recipe #6 (Ken Burns) or #7 (slideshow) |
| Captions | Template's PHASE 2 step 4 | Recipe #17 (Whisper) + #18 (burn-in) |
| Audio mux | Template's PHASE 2 step 6 | Recipe #9 (mux) + #10 (loudnorm) + #11 (duck) |
| Export | Template's PHASE 2 step 8 | Recipe #14 (short) or #15 (long) or #16 (feed) |
| QA | Template's PHASE 3 checklist | "Shell One-Liners" + Recipe #13 (frame extract) |
| SEO | Template's SEO section | "Combining Templates" examples |

### Method 4: Agent self-check loop

After any render, the agent should run this verification sequence (all free, all local):

```bash
# 1. File exists and has video stream?
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUTPUT" | grep -q video && echo "PASS: has video" || echo "FAIL: no video stream"

# 2. Resolution correct?
RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "Resolution: $RES"  # Compare to expected canvas

# 3. Duration > 0?
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$OUTPUT")
echo "Duration: ${DUR}s"  # Must be > 0

# 4. FPS = 30?
FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "FPS: $FPS"  # Should be 30/1

# 5. Audio sample rate = 48000?
SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$OUTPUT")
echo "Sample rate: $SR"  # Must be 48000

# 6. Visual QA frames
for pct in 25 50 75; do
  T=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i "$OUTPUT" -frames:v 1 "qa_${pct}.png" -y 2>/dev/null
done
echo "QA frames saved — visually inspect for black/corrupt/face-leak"

# 7. Loudness check
ffmpeg -i "$OUTPUT" -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | grep input_i
```

All 7 checks use free tools. The AI agent reads the results and decides PASS / FAIL / INCONCLUSIVE per the HTML template's QA checklist.

---

*This .md supports editor.addict.best. The HTML app owns the templates — this file owns the commands, pro-tips, and merge logic. Both are served from the same domain. Together they are the complete system.*


### 10. Loudnorm (EBU R128 — all platforms)

```bash
# Standard: -14 LUFS (YouTube, IG, FB, LinkedIn)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized.mp4

# TikTok loud-feed variant: -11 to -12 LUFS
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized_loud.mp4
```


### 13. Extract frames for QA check

```bash
# Grab frames at 25%, 50%, 75% of duration for visual QA
DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
for pct in 25 50 75; do
  T=$(echo "$DURATION * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i input.mp4 -frames:v 1 "qa_frame_${pct}pct.png" -y
done
```
////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Template-4: social media package
--------------------------------

### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you
TEMPLATE U4

Template U4 — Social Media Package Master Package

6 Executable Code Blocks

Verbatim executive directives and code for Template U4 (Multi-platform social package: 9:16, 16:9, 4:5, 1:1) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 6 Code Blocks
Paste-ready package · 148 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
# ===== CODE FOR UNIFIED TEMPLATE U4 =====

### U4 — Platform-Specific

**Agent directive: You are adapting a finished master video into platform-specific variants. You do NOT re-edit or re-grade — you only change crop, pacing, loudness, caption density, and CTA language per platform. The master must already be graded and QA-passed before you start. If it is not, run U1/U2/U3 first.**

1. **Same grade, different pacing.** Never re-grade for a platform. Only change: crop, caption density, interrupts cadence, loudness, CTA language.
2. **LinkedIn is a different animal.** Skip zoom-punches for Dose/Dentist. Professional CTA. Trust > virality tricks. 16:9 or 1:1 preferred.
3. **TikTok vs Reels:** Almost identical pipeline, but TikTok rewards slightly louder (-11 LUFS vs -14) and faster hook pressure (1.0–1.3s intent vs ~2s for Reels).
4. **If master can't crop cleanly for a format, say so.** Mark INCONCLUSIVE and request a re-record. Don't force a broken crop.

### 1. Probe any file first (always start here)

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

Pro-tip: Pipe to `jq` for quick checks:

```bash
# Duration only
ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4

# Resolution
ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 input.mp4

# FPS
ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 input.mp4
```


### 16. Re-crop for feed formats (U4 output)

```bash
# 4:5 feed (1080x1350) — re-center, don't blind-crop
ffmpeg -i master.mp4 \
  -vf "scale=1080:1350:force_original_aspect_ratio=decrease,pad=1080:1350:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_45.mp4"

# 1:1 feed (1080x1080)
ffmpeg -i master.mp4 \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_11.mp4"
```


### 14. Final export — short-form (U1/U5 output)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 15M -maxrate 20M -bufsize 30M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1920:flags=lanczos" \
  "$OUT_DIR/short_916.mp4"
```


### 15. Final export — long-form (U2 output)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 20M -maxrate 25M -bufsize 40M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1920:1080:flags=lanczos" \
  "$OUT_DIR/long_169.mp4"
```


## Quick Shell One-Liners

```bash
# Check if video is HDR
ffprobe -v quiet -select_streams v:0 -show_entries stream=color_transfer -of csv=p=0 input.mp4
# If output is "smpte2084" or "arib-std-b67" → needs HDR→SDR

# Verify A/V sync drift
ffprobe -v quiet -show_entries stream=start_time -of csv=p=0 input.mp4
# Both streams should start at ~0.000000

# Check final loudness (post-loudnorm verification)
ffmpeg -i final.mp4 -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | tail -20

# Batch probe all outputs
for f in "$OUT_DIR"/*.mp4; do
  echo "=== $f ==="
  ffprobe -v quiet -show_entries stream=width,height,r_frame_rate,codec_name -show_entries format=duration,size -of flat "$f"
done

# Verify no watermark (check for alpha/overlay in bottom-right)
ffmpeg -ss 1 -i input.mp4 -frames:v 1 -vf "crop=200:50:iw-200:ih-50" check_watermark.png

# Quick safe-zone overlay for 9:16 (visual check)
ffmpeg -i short.mp4 -vf "drawbox=x=60:y=250:w=870:h=1200:color=red@0.3:t=2" -t 5 safezone_check.mp4
```
---
////////////\\\\\\\\\\\\\\\\\\\\\\\\////////////////////////\\\\\\\\\\\\//

Template-5: pass gate
---------------------

### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you
TEMPLATE U5

Template U5 — Pass Gate / Retention Loop Master Package

6 Executable Code Blocks

Verbatim executive directives and code for Template U5 (Viral retention loops & hook optimization) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 6 Code Blocks
Paste-ready package · 136 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
# ===== CODE FOR UNIFIED TEMPLATE U5 =====

### U5 — Viral Pipeline + Final Gate

**Agent directive: You are optimizing a video for viral performance on short-form platforms and running the final quality gate. Your primary focus is the hook (first 3 seconds), loop engineering, and passing ALL QA checks with three-state results. No video ships without every check returning PASS.**

1. **Step 1 (Hook) is highest ROI.** Spend 80% of your optimization time on the first 3 seconds.
2. **Loop engineering:** Match the last spoken word/visual fragment to flow back into the opening. Users rewatching = algorithm signal.
3. **QA is three-state.** PASS / FAIL / INCONCLUSIVE. "I think it's fine" = INCONCLUSIVE = not done.
4. **Max 3 re-render attempts.** If you can't pass QA in 3 tries, the input needs to change, not the render settings.

### 12. Silence detection (for dead-air cuts)

```bash
# Find silences longer than 0.4s at -30dB threshold (hyper pace)
ffmpeg -i input.mp4 -af "silencedetect=noise=-30dB:d=0.4" -f null - 2>&1 | grep "silence_"

# For balanced pace, use d=0.55
# For story pace, use d=0.75
```


### 14. Final export — short-form (U1/U5 output)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 15M -maxrate 20M -bufsize 30M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1920:flags=lanczos" \
  "$OUT_DIR/short_916.mp4"
```


### 13. Extract frames for QA check

```bash
# Grab frames at 25%, 50%, 75% of duration for visual QA
DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
for pct in 25 50 75; do
  T=$(echo "$DURATION * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i input.mp4 -frames:v 1 "qa_frame_${pct}pct.png" -y
done
```


### 10. Loudnorm (EBU R128 — all platforms)

```bash
# Standard: -14 LUFS (YouTube, IG, FB, LinkedIn)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized.mp4

# TikTok loud-feed variant: -11 to -12 LUFS
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized_loud.mp4
```


## Quick Shell One-Liners

```bash
# Check if video is HDR
ffprobe -v quiet -select_streams v:0 -show_entries stream=color_transfer -of csv=p=0 input.mp4
# If output is "smpte2084" or "arib-std-b67" → needs HDR→SDR

# Verify A/V sync drift
ffprobe -v quiet -show_entries stream=start_time -of csv=p=0 input.mp4
# Both streams should start at ~0.000000

# Check final loudness (post-loudnorm verification)
ffmpeg -i final.mp4 -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | tail -20

# Batch probe all outputs
for f in "$OUT_DIR"/*.mp4; do
  echo "=== $f ==="
  ffprobe -v quiet -show_entries stream=width,height,r_frame_rate,codec_name -show_entries format=duration,size -of flat "$f"
done

# Verify no watermark (check for alpha/overlay in bottom-right)
ffmpeg -ss 1 -i input.mp4 -frames:v 1 -vf "crop=200:50:iw-200:ih-50" check_watermark.png

# Quick safe-zone overlay for 9:16 (visual check)
ffmpeg -i short.mp4 -vf "drawbox=x=60:y=250:w=870:h=1200:color=red@0.3:t=2" -t 5 safezone_check.mp4
```

---

### Method 4: Agent self-check loop

After any render, the agent should run this verification sequence (all free, all local):

```bash
# 1. File exists and has video stream?
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUTPUT" | grep -q video && echo "PASS: has video" || echo "FAIL: no video stream"

# 2. Resolution correct?
RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "Resolution: $RES"  # Compare to expected canvas

# 3. Duration > 0?
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$OUTPUT")
echo "Duration: ${DUR}s"  # Must be > 0

# 4. FPS = 30?
FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "FPS: $FPS"  # Should be 30/1

# 5. Audio sample rate = 48000?
SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$OUTPUT")
echo "Sample rate: $SR"  # Must be 48000

# 6. Visual QA frames
for pct in 25 50 75; do
  T=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i "$OUTPUT" -frames:v 1 "qa_${pct}.png" -y 2>/dev/null
done
echo "QA frames saved — visually inspect for black/corrupt/face-leak"

# 7. Loudness check
ffmpeg -i "$OUTPUT" -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | grep input_i
```

All 7 checks use free tools. The AI agent reads the results and decides PASS / FAIL / INCONCLUSIVE per the HTML template's QA checklist.





////////////\\\\\\\\\\\\\\\\\\\\\\\\////////////////////////\\\\\\\\\\\\///
TEMPLATE U6

Template U6 — Aggregated Master Prompt (All Templates Combined)

32 Executable Code Blocks

Verbatim executive directives and code for Template U6 (Aggregated Master Prompt U1→U3→U5→U2→U4) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 32 Code Blocks
Paste-ready package · 652 lines from original file
Template-6: Aggregated Master Prompt — All Templates Combined
-------------------------------------------------------------

**Agent directive: This is the MASTER template. When the user gives you this template, you must read their request carefully, determine WHICH template functions (U1–U5) apply, and execute them in the correct order. This template contains ALL the tools, tips, rules, and codes from templates 1–5. You do NOT need any other template file — everything is here.**

---

## HOW TO THINK — Agent Decision Map

**Step 1: Read the user's request. Classify it into one or more jobs:**

| User wants... | Use template | Format | Key output |
|:---|:---|:---|:---|
| Screen recording → short vertical video | U1 | 9:16 (1080×1920) | `short_916.mp4` |
| Screen recording → long horizontal video + feed crops | U2 | 16:9 (1920×1080) + 4:5 + 1:1 | `long_169.mp4` + `feed_45.mp4` + `feed_11.mp4` |
| Improve/enhance an existing video | U3 | Same as input | Enhanced version |
| Adapt a finished video for specific platforms | U4 | Multiple crops + loudness variants | Platform pack |
| Optimize for viral + run QA gate | U5 | Same as input | Viral-optimized + QA report |
| **Group of images → produced video** | **U7** | **All formats (9:16, 16:9, 4:5, 1:1, 2:3)** | **Multi-platform video pack** |
| **Voice-over + SEO captions for a video** | **U8** | **2 audio + 2 transcript + USAGE_TIPS** | **5-file VO package** |
| **AI vision analysis, content understanding, scene split** | **U9** | **Transcript + scene map + categories + prompts** | **Structured content intelligence** |
| Full production from raw to everything | U6 = U1→U3→U5→U2→U4 | All formats | Complete package |

**Step 2: Determine the execution order.**
- If user says "make a TikTok/Reel from this screen recording" → U1 then U5
- If user says "make a YouTube video" → U1 (grade ref) then U2 then U5
- If user says "enhance this video" → U3 then U5
- If user says "post this everywhere" → U4 (assumes master exists)
- If user says "convert these images to video" or "make a video from these photos" → **U7 only (self-contained)**
- If user says "add voice-over" or "generate captions" or "add narration" → **U8 only (self-contained)**
- If user says "understand this video" or "analyze video content" or "split into scenes" or "what's in this video" → **U9 only (self-contained)**
- If user says "analyze then add voice-over" → **U9 then U8** (U9 feeds transcript/scene map into U8)
- If user says "images to video with voice-over" → **U7 then U8**
- If user says "do everything" or gives raw material → U6 chain: U1 → U3 → U5 → U2 → U4
- If unclear → ask the user. Do NOT guess.

**Step 3: Detect the operating system BEFORE running any command.**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
echo "Detected OS: $OS_TYPE"
```
- macOS: use `avfoundation` for capture, fonts are built-in, Retina = 2x resolution
- Linux: use `x11grab` + Xvfb for capture, MUST install fonts first, capture at 2x then downscale with lanczos

**Step 4: Follow the rules below — no exceptions.**

---

## UNIVERSAL RULES (apply to EVERY job, EVERY template)

### Mandatory technical invariants — break any of these = FAIL
1. **`-framerate 30`** on ALL image inputs (stills, slideshows, Ken Burns). VFR kills sync.
2. **`apad` + `-shortest`** when muxing audio to video. Prevents A/V drift.
3. **`-ar 48000`** on ALL final audio output. No exceptions.
4. **`flags=lanczos`** in ALL scale filters. Default bilinear = blurry text on screen recordings.
5. **HDR → SDR** conversion BEFORE any color grading. Check with ffprobe for bt2020/hlg/pq.
6. **Archive raw** before any edit: `cp "$RAW_PATH" "$OUT_DIR/raw_archive/"`
7. **Probe first**: `ffprobe -v quiet -print_format json -show_format -show_streams input.mp4` — never assume resolution, duration, or codec.

### QA — Three-state law (non-negotiable)
- **PASS** = measured and met the requirement
- **FAIL** = measured and did NOT meet → fix root cause → re-render → re-check
- **INCONCLUSIVE** = could not measure → this is NOT PASS → blocks "done"
- Never promote INCONCLUSIVE to PASS. Never skip QA. Never hardcode PASS.
- Maximum 3 fix-and-recheck loops. If 3 fails → escalate (input quality problem, not render settings).

### Cognitive pipeline (run mentally on every job)
1. **PERCEIVE** — probe media, cache facts (duration, res, fps, HDR, audio streams)
2. **INTERPRET** — match assets to intent beats (hook / problem / proof / how / CTA)
3. **COMPOSE** — choose motion/transition per beat (every effect needs a WHY)
4. **REALIZE** — render with the invariants above
5. **CRITIQUE** — extract QA frames at 25/50/75%, mute-test hook, verify all checks PASS

### Linux-specific mandatory steps
- Install fonts BEFORE any text/subtitle work: `apt install -y fonts-liberation fonts-dejavu-core fontconfig && fc-cache -fv`
- Screen capture: use Xvfb at 2x target resolution, then downscale with lanczos
- Use CRF 14 (not 18) for screen content — text needs more bits
- Use `-tune stillimage` for mostly-static screen content
- Verify font availability before subtitle burn-in: `fc-list | grep -i arial`
- If Arial not found, substitute with DejaVu Sans in force_style

### macOS-specific mandatory steps
- Retina displays capture at 2x logical resolution — always downscale with lanczos
- Crop browser chrome AFTER downscale (pixel heights differ at Retina vs logical)
- Use `avfoundation` for screen capture: `ffmpeg -f avfoundation -framerate 30 -i "1:none" ...`

### Light Stack Optimization Rules — Same Quality, Less Compute

**Agent: before choosing the heavy path, check if the light path covers the job. Heavy is only justified when the light path has a proven gap for THIS specific input.**

#### 1. Captions / STT — avoid Whisper when you already have the script
- **If you generated the VO** (edge-tts): skip Whisper entirely. Convert your script text + edge-tts VTT timestamps directly to SRT. This is exact, free, and lighter.
- **If you must transcribe existing speech**: use `faster-whisper` with `base` model (CTranslate2 backend — 3-4× faster than vanilla Whisper, 60% less RAM). Install: `pip install faster-whisper`.
- **If VPS is weak and API key exists**: offload to OpenAI Whisper API (`/v1/audio/transcriptions`) instead of running local medium/large.
- **Avoid local Whisper medium/large** unless: (a) the speech is messy (accents, background noise, overlapping speakers), AND (b) no API key is available.

```bash
# Light: script → SRT (no Whisper, no model download, instant)
# Use this when YOU wrote the VO script and generated audio with edge-tts
python3 -c "
script_lines = [
    (0.0, 2.5, 'This app helps you calculate the right dosage.'),
    (2.5, 5.5, 'Enter the patient details.'),
]
with open('captions.srt', 'w') as f:
    for i, (s, e, txt) in enumerate(script_lines, 1):
        sh, sm, ss = int(s//3600), int(s%3600//60), s%60
        eh, em, es = int(e//3600), int(e%3600//60), e%60
        f.write(f'{i}\
{sh:02d}:{sm:02d}:{ss:06.3f} --> {eh:02d}:{em:02d}:{es:06.3f}\
{txt}\
\
')
print('SRT from script — zero ML, exact timestamps')
"

# Medium: faster-whisper base (when you MUST transcribe existing speech)
pip install faster-whisper 2>/dev/null
python3 -c "
from faster_whisper import WhisperModel
model = WhisperModel('base', compute_type='int8')  # int8 = fast, low RAM
segs, info = model.transcribe('input.mp4', word_timestamps=True)
for seg in segs:
    print(f'[{seg.start:.2f} → {seg.end:.2f}] {seg.text}')
"
```

#### 2. TTS — edge-tts is the default, always
- **Default**: `edge-tts` (free, neural, AR+EN, no GPU). This is already Template 8's recommendation.
- **Single Arabic voice for mixed AR+EN scripts**: the Arabic neural voices handle short English terms naturally. Don't switch voices mid-sentence — it creates jarring seams.
- **OpenAI TTS**: only when English must be premium marketing quality AND the key is available. Never the default.
- **Skip Coqui/XTTS**: heavier, rarely better than edge-tts for app demo narration.

#### 3. Video understanding — smart frame density (not always 1fps)
- **Default**: percent ladder (every 10% of duration) + scene-change frames = 12-20 frames for a typical short video. Agent reads them with native vision.
- **Upgrade to 1fps**: when the video has rapid micro-interactions (fast form fills, multi-step wizards, animation-heavy UI) or when you've never analyzed this type of content before.
- **Contact sheet first**: generate a single contact sheet image, triage at a glance, then extract dense frames only for complex sections.
- **QA**: always 3 frames at 25%/50%/75% — never re-watch the full video.
- **Skip OpenCV** for descriptions — agent native vision IS the tool. Keep OpenCV only for numeric metrics (sharpness score, face bounding boxes) when explicitly needed.

```bash
# Light: percent ladder (10 frames for a 30s video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VIDEO")
mkdir -p keyframes
for pct in 0 10 20 30 40 50 60 70 80 90 100; do
  ts=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$ts" -i "$VIDEO" -frames:v 1 -y "keyframes/pct_${pct}.png" 2>/dev/null
done
echo "11 frames extracted (percent ladder)"

# Light: contact sheet (single image for quick triage)
ffmpeg -i "$VIDEO" \
  -vf "select='isnan(prev_selected_t)+gte(t-prev_selected_t\,$(echo "$DUR/12" | bc -l))',scale=320:-1,tile=4x3" \
  -frames:v 1 -y keyframes/contact_sheet.jpg 2>/dev/null
echo "Contact sheet: keyframes/contact_sheet.jpg"
```

#### 4. Screen capture — sharp once, not "fix soft later"
- **VPS**: capture at target resolution (1920×1080 or 1080×1920), Chrome `--app` mode, CRF 12-14, preset `veryfast` to `medium`. CRF dominates text sharpness more than preset.
- **Mac**: native Retina capture + lanczos downscale only when Retina forces 2× logical pixels.
- **2× capture + downscale**: only when text is still soft after native-res capture. It's a fix for bad DPI, not a default step.
- **One master capture → ffmpeg trim/crop** for multiple shorts. Don't re-record for every short clip.

#### 5. Encode / multi-platform — master + copy mux + few crops
- **Mux VO**: `ffmpeg -i video.mp4 -i vo.m4a -c:v copy -c:a aac -ar 48000 -shortest -y output.mp4` — video stream is NOT re-encoded (seconds, not minutes).
- **Loudnorm once**: apply loudnorm on the final audio, not on every intermediate file.
- **Grade once**: color correct the master, then crop from the graded master. Don't re-grade each export.
- **Full U6 multi-platform pack**: only when publishing to ALL channels that same day. Otherwise: 1 master + 1-2 crops.

#### 6. Music / mix — prefer silence for clinical content
- **App demos and medical UI**: VO-only, no music. Numbers and doses need clarity, not vibes.
- **Viral shorts / lifestyle**: simple low-volume music (0.15-0.25 amplitude), no multi-band ducking.
- **Never**: complex duck filter chains for a 15-second app demo.

#### 7. Scene detection — agent beat table beats automated detection for UI demos
- **App UI demos**: agent watches 10-12 frames, writes a beat table manually. UI screen changes aren't classic "scenes" — automated detectors over-split on scrolling and subtle transitions.
- **General/unknown video**: PySceneDetect (Template 9 Phase 4) is still the right choice — the agent hasn't seen this content before.
- **Split intentionally**: 3-5 clips from the beat table, not 15-20 auto-detected clips that need manual review.

**Light stack summary (same jobs, less compute):**

| Job | Heavy path (avoid unless needed) | Light path (default) |
|:---|:---|:---|
| Captions for TTS VO | Whisper medium local | Script → SRT directly |
| Captions for human speech | Whisper large local | `faster-whisper` base or Whisper API |
| Type-1/2 VO | Coqui / always OpenAI TTS | `edge-tts` neural |
| Understand UI video | 1fps + OpenCV + cloud cascade | 10-12 frames + agent native vision |
| Sharp screen capture | 2× always + slow preset | Target res + CRF 12-14 + veryfast |
| Multi-format export | Full U6 every time | 1 master + `-c:v copy` mux + 1-2 crops |
| QA verification | Full re-render + re-watch | 3 frames at 25/50/75% |
| Scene detection (UI demos) | Auto-detect everything | Agent beat table from frames |

---

## SOCIAL MEDIA SUCCESS FACTORS

### Format selection — when to use 9:16 vs 16:9
| Platform | Primary format | Secondary | Loudness target |
|:---|:---|:---|:---|
| TikTok | 9:16 | — | -11 LUFS |
| Instagram Reels | 9:16 | — | -14 LUFS |
| Instagram Feed | 4:5 or 1:1 | 9:16 | -14 LUFS |
| Instagram Stories | 9:16 | — | -14 LUFS |
| YouTube Shorts | 9:16 | — | -14 LUFS |
| YouTube Long | 16:9 | — | -14 LUFS |
| Facebook Feed | 4:5 or 1:1 | 16:9 | -14 LUFS |
| LinkedIn | 16:9 or 1:1 | — | -14 LUFS |
| Twitter/X | 16:9 or 1:1 | 9:16 | -14 LUFS |

### Proven performance rules (documented results)
1. **Hook within 1.0–1.3s** for TikTok, **1.5–2.0s** for Reels/YouTube Shorts — cold open, no logo, no greeting
2. **Mute-test the hook** — if first 3s don't work without sound, the video fails on auto-scroll feeds
3. **Loop engineering** — last frame/word connects back to opening → rewatches → algorithm boost
4. **Two length variants** — ultra-tight (12–22s) + standard (25–40s) for short-form
5. **Caption coverage** — 95%+ of spoken content must have visible captions (accessibility + sound-off viewers)
6. **Save-worthy CTA** — "Save this for later" outperforms "Follow me" on Instagram
7. **Pattern interrupts** — visual change every 2–4s (short-form) or 7–10s (long-form) to hold attention
8. **Chapters for YouTube** — mandatory, improves SEO and watch time

### Quality benchmarks that prevent algorithm penalties
- Resolution: exact canvas match (1080×1920, 1920×1080, etc.) — no weird sizes
- FPS: 30 (or 60 if intentional). Never variable frame rate.
- No visible watermarks from other platforms
- No black bars from wrong aspect ratio
- Audio normalized to platform-specific LUFS target
- No dead air longer than pacing budget (0.4s hyper / 0.55s balanced / 0.75s story)

### Viral Video — Scroll-Stop Engineering (proven tactics)
1. **Movement in frame 1.** The first frame must have motion, contrast change, or text appearing. Static opening = scroll-past.
2. **Curiosity gap in text overlay.** "Wait for the result…" or "Watch what happens when…" — incomplete information forces view-through.
3. **Face or hands in hook** (when applicable). Human elements trigger pattern recognition → pause scrolling. For faceless brands, use close-up of the app result screen instead.
4. **Color contrast spike in first frame.** Use a brighter/more saturated first 0.5s compared to the rest of the video. This creates visual "pop" in the feed thumbnail.
5. **Audio hook within 0.3s.** Start with a sharp sound (click, pop, whoosh) — not silence, not music fade-in. Sound triggers on auto-play devices with volume on.
6. **Text appears within 0.5s.** On muted auto-play feeds, text is your ONLY hook. It must appear immediately, not after a title card.
7. **Retention valleys = algorithm death.** If viewer attention drops at any point, the algorithm deprioritizes. Prevent valleys with: pattern interrupts (zoom, cut, text change) every 2–4s on short-form.
8. **End with incomplete thought.** The last 0.5s should feel like there's more — this triggers loop replays, which is the single strongest algorithm signal.
9. **First-comment strategy.** Pin a comment with a question or hot take within 60s of posting. Early comments = engagement signal.
10. **Post at platform peak hours.** TikTok: 7–9am, 12–2pm, 7–11pm local. Instagram: 11am–1pm, 7–9pm. YouTube: 2–4pm. LinkedIn: 7–8am, 5–6pm (weekdays).

### Viral Images — Maximum Impact Rules
1. **High contrast + saturated colors** outperform muted palettes on mobile feeds. Increase saturation 5–10% for social exports.
2. **Text-to-image ratio < 20%** for Instagram/Facebook (avoid "too much text" penalty). Pinterest allows more.
3. **Faces looking at camera** get 38% more engagement than profiles or away-facing. For faceless brands, use close-up product shots.
4. **Odd numbers in headlines** ("7 tips", "3 mistakes") outperform even numbers in click-through rate.
5. **Before/after split images** have highest save rate on Instagram. Use ffmpeg hstack:
```bash
# Side-by-side before/after (1:1 for feed)
ffmpeg -i before.png -i after.png \
  -filter_complex "[0:v]scale=540:1080:flags=lanczos[l];[1:v]scale=540:1080:flags=lanczos[r];[l][r]hstack=inputs=2,unsharp=5:5:0.8:3:3:0.4" \
  before_after.jpg -y
```
6. **Carousel first slide** must be the strongest — it's your thumbnail on the grid. Save the CTA for the last slide.
7. **Sharpen EVERY image** before social upload — platforms re-compress aggressively:
```bash
# Social-ready sharpen + quality for any image
ffmpeg -i input.png \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 output_sharp.jpg -y
```

---

## ALL FFMPEG COMMANDS (from templates 1–5)

### 1. Probe any file first (always start here)

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

Quick checks:
```bash
ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4
ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 input.mp4
ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 input.mp4
```

### 2. Archive raw (always before any edit)

```bash
mkdir -p "$OUT_DIR/raw_archive"
cp "$RAW_PATH" "$OUT_DIR/raw_archive/"
```

### 3. HDR → SDR (run if ffprobe shows bt2020/hlg/pq)

```bash
ffmpeg -i "$RAW_PATH" \
  -vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709:t=bt709:m=bt709,tonemap=hable:desat=0,zscale=t=bt709,format=yuv420p" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  "$OUT_DIR/sdr.mp4"
```

### 4. Crop browser chrome / dock

```bash
# macOS: Top ~80px chrome + bottom ~80px dock = crop 160px total
# Linux: chrome height varies by DE/WM — probe a frame first, measure manually
# Retina macOS: crop AFTER downscale to logical resolution
ffmpeg -i raw.mp4 \
  -vf "crop=in_w:in_h-160:0:80" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  cropped.mp4
```

### 5. Brand grade (Clean High-Key — Dose/Dentist default)

```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.05:saturation=0.92,curves=m='0/0.03 0.5/0.52 1/0.96'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded.mp4
```

Female ProMedic (warm rose):
```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.03:contrast=1.04:saturation=0.97,colorbalance=rs=0.04:gs=-0.01:bs=-0.03,curves=m='0/0.04 0.5/0.53 1/0.97'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_female.mp4
```

Coach ProMedic (neutral-warm):
```bash
ffmpeg -i sdr.mp4 \
  -vf "eq=brightness=0.04:contrast=1.06:saturation=1.03,curves=m='0/0.02 0.5/0.53 1/0.98'" \
  -c:v libx264 -preset slow -crf 18 -c:a copy \
  graded_coach.mp4
```

### 6. Ken Burns from still image → clip

```bash
# ponytail: ALWAYS -framerate 30 on image inputs (VFR trap kills sync)
ffmpeg -framerate 30 -loop 1 -t 4 -i still.png \
  -vf "scale=3840:2160,zoompan=z='min(zoom+0.001,1.15)':d=120:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920" \
  -c:v libx264 -t 4 -pix_fmt yuv420p \
  ken_burns.mp4
```

### 7. Image folder → slideshow video

```bash
ffmpeg -framerate 30 -pattern_type glob -i 'images/*.png' \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,zoompan=z='min(zoom+0.0008,1.12)':d=90:s=1080x1920" \
  -c:v libx264 -pix_fmt yuv420p -r 30 \
  slideshow.mp4
```

### 8. 9:16 blurred-fill from landscape source

```bash
ffmpeg -i landscape.mp4 \
  -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20:5[bg];[0:v]scale=1080:-2:force_original_aspect_ratio=decrease[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2" \
  -c:v libx264 -preset slow -crf 18 \
  vertical_blurred.mp4
```

### 9. Mux voiceover — no A/V drift

```bash
# ponytail: apad + -shortest is the A/V drift killer. Never skip both.
ffmpeg -i video.mp4 -i voiceover.wav \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -af "apad" -shortest \
  muxed.mp4
```

### 10. Loudnorm (EBU R128 — all platforms)

```bash
# Standard: -14 LUFS (YouTube, IG, FB, LinkedIn)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized.mp4

# TikTok loud-feed variant: -11 to -12 LUFS
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  normalized_loud.mp4
```

### 11. Duck music under voiceover

```bash
ffmpeg -i voiceover.wav -i music.mp3 \
  -filter_complex "[1:a]volume=0.3[music];[0:a][music]amix=inputs=2:duration=first:dropout_transition=2[out]" \
  -map "[out]" -c:a aac -b:a 192k -ar 48000 \
  mixed_audio.aac
```

Dynamic ducking:
```bash
ffmpeg -i voiceover.wav -i music.mp3 \
  -filter_complex "[0:a]asplit=2[vo][sc];[1:a][sc]sidechaincompress=threshold=0.02:ratio=6:attack=200:release=1000[ducked];[vo][ducked]amix=inputs=2:duration=first[out]" \
  -map "[out]" -c:a aac -b:a 192k -ar 48000 \
  mixed_dynamic.aac
```

### 12. Silence detection (for dead-air cuts)

```bash
ffmpeg -i input.mp4 -af "silencedetect=noise=-30dB:d=0.4" -f null - 2>&1 | grep "silence_"
# For balanced pace, use d=0.55
# For story pace, use d=0.75
```

### 13. Extract frames for QA check

```bash
DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
for pct in 25 50 75; do
  T=$(echo "$DURATION * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i input.mp4 -frames:v 1 "qa_frame_${pct}pct.png" -y
done
```

### 14. Final export — short-form (9:16)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 15M -maxrate 20M -bufsize 30M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1920:flags=lanczos" \
  "$OUT_DIR/short_916.mp4"
```

### 15. Final export — long-form (16:9)

```bash
ffmpeg -i processed.mp4 \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 20M -maxrate 25M -bufsize 40M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1920:1080:flags=lanczos" \
  "$OUT_DIR/long_169.mp4"
```

### 16. Re-crop for feed formats

```bash
# 4:5 feed (1080x1350)
ffmpeg -i master.mp4 \
  -vf "scale=1080:1350:force_original_aspect_ratio=decrease,pad=1080:1350:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_45.mp4"

# 1:1 feed (1080x1080)
ffmpeg -i master.mp4 \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 18 -c:a copy \
  "$OUT_DIR/feed_11.mp4"
```

### 17. Whisper transcription

```bash
# Linux GPU tip: install torch with CUDA first for 5-10x speed boost
whisper input.mp4 --model base --language en --output_format json --word_timestamps True --output_dir "$OUT_DIR"
```

### 18. Caption burn-in from SRT

```bash
# Linux: verify font first — fc-list | grep -i arial — if missing use DejaVu Sans
ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontName=Arial,FontSize=22,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=2'" \
  -c:v libx264 -crf 18 -c:a copy \
  captioned.mp4
```

### 19. Scene/shot detection

```bash
ffmpeg -i input.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep showinfo
```

### 20. Speed ramp

```bash
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]trim=0:10,setpts=PTS/1.5[fast];[0:v]trim=10,setpts=PTS-STARTPTS[slow];[fast][slow]concat=n=2:v=1:a=0[v];[0:a]atrim=0:10,atempo=1.5[afast];[0:a]atrim=10,asetpts=PTS-STARTPTS[aslow];[afast][aslow]concat=n=2:v=0:a=1[a]" \
  -map "[v]" -map "[a]" -c:v libx264 -crf 18 -c:a aac -ar 48000 \
  speed_ramped.mp4
```

### 21. Linux screen capture (headless server)

```bash
if [ "$(uname -s)" = "Linux" ]; then
  apt install -y xvfb fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv
  # For 9:16 output: capture at 2160x3840
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" -tune stillimage \
    raw_capture.mp4
fi
```

### 22. macOS screen capture

```bash
if [ "$(uname -s)" = "Darwin" ]; then
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

---

## AGENT SELF-CHECK (run after EVERY render)

```bash
# 1. File exists and has video stream?
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUTPUT" | grep -q video && echo "PASS: has video" || echo "FAIL: no video stream"

# 2. Resolution correct?
RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "Resolution: $RES"

# 3. Duration > 0?
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$OUTPUT")
echo "Duration: ${DUR}s"

# 4. FPS = 30?
FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$OUTPUT")
echo "FPS: $FPS"

# 5. Audio sample rate = 48000?
SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$OUTPUT")
echo "Sample rate: $SR"

# 6. Visual QA frames
for pct in 25 50 75; do
  T=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i "$OUTPUT" -frames:v 1 "qa_${pct}.png" -y 2>/dev/null
done
echo "QA frames saved — visually inspect for black/corrupt/face-leak"

# 7. Loudness check
ffmpeg -i "$OUTPUT" -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | grep input_i
```

**All 7 checks must return PASS before you declare done. INCONCLUSIVE = not done.**

---

## REFERENCE TABLES

### Safe Zone Quick Reference
| Format | Canvas | Safe area for text/captions | Grid-crop safe |
|:---|:---|:---|:---|
| 9:16 short | 1080×1920 | x: 60–930, y: 250–1450 | N/A |
| 16:9 long | 1920×1080 | x: 120–1800, y: 70–1010 | N/A |
| 4:5 feed | 1080×1350 | Central 1080×1080 (y: 135–1215) | Square center |
| 1:1 feed | 1080×1080 | Central ~90% | Full frame |
| Stories | 1080×1920 | Same as 9:16 but stricter top/bottom | N/A |

### Pacing Cheat Sheet
| Preset | Silence cutoff | Interrupt cadence | Best for |
|:---|:---|:---|:---|
| `hyper` | 0.40 s | 2.0–2.6 s | TikTok, Reels, Coach short-form |
| `balanced` | 0.55 s | 2.8–3.8 s | General short-form, Dose/Dentist |
| `story` | 0.75 s | 4.0–5.5 s | Long-form, tutorials, Female storytime |

### Six Deadly Sins
1. VFR trap — always `-framerate 30` on image inputs
2. A/V drift — always `apad` + `-shortest` when muxing, always `-ar 48000`
3. Context decay — save state checkpoints after every stage
4. False positive "done" — only QA PASS counts (INCONCLUSIVE = not done)
5. Blind acceptance — visually verify frames at 25/50/75% duration
6. Face leak — faceless absolute on all brand content

### Common Mistakes
- ❌ Starting with logo / greeting / slow fade → always cold-open
- ❌ Dead air longer than interrupt budget
- ❌ Captions outside safe zone
- ❌ Wrong safe-zone numbers for wrong format
- ❌ SFX louder than voiceover
- ❌ Missing `-framerate 30` on image inputs
- ❌ Missing `apad` + `-shortest` when muxing audio
- ❌ Missing `-ar 48000` on final audio
- ❌ Claiming "done" without QA PASS
- ❌ Using default bilinear scaling (always `flags=lanczos`)
- ❌ Linux: capturing without installing fonts first
- ❌ Linux: capturing at target resolution instead of 2x then downscaling
- ❌ macOS: cropping at Retina resolution before downscaling

### Per-App Traps
| App | Never do this |
|:---|:---|
| Dose Calculator | Invent/hallucinate medical numbers. Use energetic meme grade. Exceed 1.15× zoom. |
| Female ProMedic | Use childish pink (it's rose-gold/blush). Mix Coach energy/pacing. |
| Coach ProMedic | Use clinical restraint — Coach is the highest-energy app. Forget readability on form cues. |
| Dentist Pro | Use teal identical to Dose blue (Dentist = teal/cyan, Dose = clinical blue). Over-punch on short-form. |

---

## AGENT ERROR PREVENTION — 15 Most Common Mistakes and How to Avoid Them

**Agent: read this list before starting ANY job. These are real errors that agents make repeatedly. Each one wastes time and produces bad output.**

| # | Mistake | What happens | Prevention |
|:---|:---|:---|:---|
| 1 | Running ffmpeg without checking if input file exists | Command fails silently or processes wrong file | Always `[ -f "$INPUT" ]` before any ffmpeg command |
| 2 | Forgetting `-y` flag on output | ffmpeg hangs waiting for overwrite confirmation | Add `-y` to all ffmpeg commands that might overwrite |
| 3 | Using `scale=1080:1920` on a 16:9 source without padding | Output is stretched/distorted | Always use `force_original_aspect_ratio=decrease,pad=...` when changing aspect ratio |
| 4 | Hardcoding crop values without measuring | Crops text, cuts off important UI elements | Extract frame first, measure chrome/dock height, then crop |
| 5 | Running subtitle burn-in with a font that doesn't exist on the system | ffmpeg fails or uses ugly fallback bitmap font | Run `fc-list` and grep for your font name first. On Linux, install fonts-liberation |
| 6 | Applying color grade to HDR content | Washed-out, clipped colors | Always check `color_transfer` with ffprobe. Run HDR→SDR BEFORE grade |
| 7 | Muxing audio without `apad` + `-shortest` | Audio/video length mismatch, drift, playback issues | Always include both flags when combining audio + video |
| 8 | Using variable frame rate input without normalizing | Choppy playback, sync issues, QA failures | Always `-framerate 30` on image inputs; for VFR video, re-encode to CFR first |
| 9 | Generating a 0-byte or tiny output file and not checking | Downstream commands fail on corrupt input | Always verify output size > 1000 bytes after render |
| 10 | Inventing duration values instead of probing | Wrong trim points, broken speed ramps, A/V desync | Every duration value must come from `ffprobe -show_entries format=duration` |
| 11 | Running the same ffmpeg pipeline on Linux and macOS without adaptation | Missing fonts, wrong capture device, broken paths | Always detect OS with `uname -s` and branch accordingly |
| 12 | Skipping QA because "it looked fine in the last render" | Ships broken video — wrong resolution, no audio, face leak | Run the full 7-check self-check after EVERY render, no exceptions |
| 13 | Using `scale=` without `flags=lanczos` | Blurry text in screen recordings, soft edges | Every `scale=` filter MUST include `:flags=lanczos` |
| 14 | Not setting `-ar 48000` on final audio | Platform re-encodes audio, introduces artifacts | Include `-ar 48000` in every final export command |
| 15 | Claiming done after 1 successful render without running variants | Missing platform-specific versions the user needs | Always check if the user wants multiple formats/lengths |

### Pre-flight Checklist (run in your head before executing)

- [ ] Did I probe the input file? Do I know its resolution, FPS, duration, HDR status, audio channels?
- [ ] Did I archive the raw file before editing?
- [ ] Am I on macOS or Linux? Did I adapt my commands?
- [ ] Did I check if needed fonts are installed (Linux)?
- [ ] Is my ffmpeg command using `-y` for non-interactive execution?
- [ ] Does every `scale=` filter include `flags=lanczos`?
- [ ] Does my audio output include `-ar 48000`?
- [ ] If muxing A+V, did I include `apad` + `-shortest`?
- [ ] If using image inputs, did I include `-framerate 30`?
- [ ] After render: did I verify the output file exists, has correct size, resolution, FPS, and audio?
- [ ] Did I extract QA frames at 25/50/75% and visually check for black frames, corruption, face leak?
- [ ] Did I run the loudness check?
- [ ] Are ALL checks PASS (not INCONCLUSIVE)?

**Only after ALL boxes are checked can you report the job as done.**

---

*This aggregated template (U6) contains everything from templates 1–5. The agent reads the user request, picks the right template chain, detects the OS, and executes with full QA. No other file is needed.*
TEMPLATE 7

Template 7 (U7) — Images → Video Production Master Package

24 Executable Code Blocks

Verbatim executive directives and code for Template 7 (converting image folders into produced videos) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 24 Code Blocks
Paste-ready package · 734 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
==================================


////////////\\\\\\\\\\\\\\\\\\\\\\\\////////////////////////\\\\\\\\\\\\////
Template-7: Images → Video Production (Multi-Platform)
------------------------------------------------------

**Agent directive: The user has a group of images and wants you to convert them into a polished, professional video optimized for one or more social platforms. This is NOT a raw slideshow — it is a produced video with transitions, motion, pacing, audio, captions, and platform-specific exports. Follow the method below step-by-step. Do not skip any phase. Every image must be probed before processing. Every output must pass QA.**

---

## THE METHOD — How an Expert AI Agent Converts Images to Video

**This is the core pipeline. Understand it before touching any file.**

```
PHASE 0: INVENTORY    → Collect, probe, sort all images. Know what you have.
PHASE 1: PREPARATION  → Normalize resolution, color space, orientation. Fix problems.
PHASE 2: STORYBOARD   → Assign each image to a beat (hook/problem/proof/how/CTA).
PHASE 3: MOTION       → Apply Ken Burns, zoom, pan per image. Every motion has a WHY.
PHASE 4: TRANSITIONS  → Add crossfade/fade/cut between images. Match pacing to platform.
PHASE 5: AUDIO        → Add music/voiceover, duck, normalize loudness per platform.
PHASE 6: CAPTIONS     → Burn in text overlays or SRT captions. Verify font availability.
PHASE 7: EXPORT       → Render platform-specific variants (9:16, 16:9, 4:5, 1:1).
PHASE 8: QA           → Extract QA frames, verify resolution/FPS/audio, three-state gate.
```

**Why this order matters:**
- You cannot grade images until you know their resolution and color space (Phase 0→1).
- You cannot choose motion until you know which image is the hook vs the CTA (Phase 1→2).
- You cannot set transition timing until you know the pacing preset (Phase 2→3→4).
- You cannot normalize audio until the video is assembled (Phase 4→5).
- You cannot burn captions until the final timing is locked (Phase 5→6).
- You cannot export until everything is assembled and verified (Phase 6→7→8).

**Skipping a phase or reordering them causes cascading errors.** The agent must follow this exact sequence.

---

## PHASE 0: INVENTORY — Know What You Have

**Agent: before doing ANYTHING, probe every image. Do not assume resolution, format, or orientation.**

```bash
# Step 0a: Detect OS
OS_TYPE="$(uname -s)"
echo "OS: $OS_TYPE"

# Step 0b: Create working directory
mkdir -p "$OUT_DIR"/{raw_archive,prepared,frames,exports,reports}

# Step 0c: List and probe all images
echo "=== IMAGE INVENTORY ===" > "$OUT_DIR/reports/inventory.txt"
for img in "$IMAGE_DIR"/*.{png,jpg,jpeg,PNG,JPG,JPEG,webp,WEBP,bmp,tiff} 2>/dev/null; do
  [ -f "$img" ] || continue
  RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$img")
  FMT=$(ffprobe -v quiet -show_entries stream=codec_name -of csv=p=0 -select_streams v:0 "$img")
  SIZE=$(stat -f%z "$img" 2>/dev/null || stat -c%s "$img" 2>/dev/null)
  echo "$img | ${RES} | ${FMT} | ${SIZE} bytes" >> "$OUT_DIR/reports/inventory.txt"
  echo "$img | ${RES} | ${FMT} | ${SIZE} bytes"
done
echo "Total images: $(grep -c '|' "$OUT_DIR/reports/inventory.txt")"
```

**Anti-hallucination checkpoint:** Read the inventory output. The agent now knows exactly how many images exist, their resolutions, and formats. Do NOT proceed if the inventory is empty — report to the user.

### Image sorting rules
- Sort images by filename (they should already be numbered: `01_hook.png`, `02_problem.png`, etc.)
- If not numbered, ask the user for the intended order
- If the user says "use all images", sort alphabetically — but confirm the order is correct before rendering

---

## PHASE 1: PREPARATION — Normalize All Images

**Agent: images from different sources will have different resolutions, color spaces, and orientations. You MUST normalize before assembling.**

### Target canvas by platform

| Platform | Canvas | Aspect ratio |
|:---|:---|:---|
| TikTok / Reels / YT Shorts | 1080×1920 | 9:16 |
| YouTube Long | 1920×1080 | 16:9 |
| Instagram Feed | 1080×1350 | 4:5 |
| Instagram / Facebook Square | 1080×1080 | 1:1 |
| LinkedIn | 1920×1080 or 1080×1080 | 16:9 or 1:1 |
| Pinterest Pin | 1000×1500 | 2:3 |

### Normalize all images to target canvas

```bash
TARGET_W=1080
TARGET_H=1920  # Change per platform: 1920 for 9:16, 1080 for 16:9, etc.

for img in "$IMAGE_DIR"/*.{png,jpg,jpeg,PNG,JPG,JPEG,webp} 2>/dev/null; do
  [ -f "$img" ] || continue
  BASENAME=$(basename "$img")
  
  # Scale to fit inside target canvas, pad with black/white, sharpen
  # ponytail: lanczos + unsharp = sharp text/UI on every platform
  ffmpeg -i "$img" \
    -vf "scale=${TARGET_W}:${TARGET_H}:force_original_aspect_ratio=decrease:flags=lanczos,pad=${TARGET_W}:${TARGET_H}:(ow-iw)/2:(oh-ih)/2:black,unsharp=5:5:0.8:3:3:0.4" \
    -y "$OUT_DIR/prepared/${BASENAME%.*}.png"
  
  # Verify output
  [ -f "$OUT_DIR/prepared/${BASENAME%.*}.png" ] || echo "FAIL: could not prepare $img"
done

echo "Prepared images: $(ls "$OUT_DIR/prepared/"*.png 2>/dev/null | wc -l)"
```

**Linux-specific preparation:**
```bash
if [ "$OS_TYPE" = "Linux" ]; then
  # Ensure fonts are available for any text overlays
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv
  
  # If images contain embedded ICC profiles (from macOS screenshots), strip to sRGB
  # This prevents color shift when rendering on Linux without color management
  for img in "$OUT_DIR/prepared/"*.png; do
    ffmpeg -i "$img" -vf "colorspace=all=bt709:iall=bt709:fast=1" -y "${img%.png}_srgb.png" 2>/dev/null && mv "${img%.png}_srgb.png" "$img"
  done
fi
```

**macOS-specific preparation:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS screenshots are Retina (2x). If images are 2x the target, downscale.
  for img in "$OUT_DIR/prepared/"*.png; do
    W=$(ffprobe -v quiet -show_entries stream=width -of csv=p=0 -select_streams v:0 "$img")
    if [ "$W" -gt $((TARGET_W * 2 - 100)) ] 2>/dev/null; then
      echo "Retina image detected ($W px wide), will be handled by scale filter"
    fi
  done
fi
```

### Color consistency check
```bash
# Ensure all prepared images have the same resolution
echo "=== RESOLUTION CHECK ==="
for img in "$OUT_DIR/prepared/"*.png; do
  RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$img")
  echo "$(basename "$img"): $RES"
done | sort -t: -k2 | uniq -f1 -c | sort -rn
# If more than 1 unique resolution appears, preparation failed — re-run normalization
```

---

## PHASE 2: STORYBOARD — Assign Beats

**Agent: do NOT just dump images in order. Each image must serve a narrative purpose.**

### Beat mapping for short-form (5 beats — ad mode)
| Beat | Purpose | Duration | Image type |
|:---|:---|:---|:---|
| 1. Hook | Stop the scroll. First 1–2s | 1.5–2.5s | Most visually striking image. High contrast. Result/payoff. |
| 2. Problem | Why this matters | 2–3s | Problem state. Before screenshot. Pain point. |
| 3. Proof | Evidence it works | 3–4s | Result screenshot. Numbers. Data. |
| 4. How | Show the process | 3–5s | Step-by-step. App UI. Workflow. |
| 5. CTA | What to do next | 2–3s | Final screen. Logo. Call to action. |

### Beat mapping for long-form (7 beats — narrative mode)
| Beat | Purpose | Duration |
|:---|:---|:---|
| 1. Hook | Grab attention | 2–3s |
| 2. Context | Set the scene | 3–5s |
| 3. Problem | Show the pain | 3–5s |
| 4. Solution | Introduce the answer | 3–5s |
| 5. Proof | Evidence/demo | 5–8s |
| 6. How | Step-by-step | 5–10s |
| 7. CTA | Call to action | 2–4s |

### Duration per image — rules
```bash
# ponytail: duration per image depends on information density
# Rule of thumb:
#   Simple image (logo, gradient, text card) = 1.5–2.5s
#   App screenshot with numbers = 3–4s (viewer needs time to read)
#   Complex diagram/chart = 4–6s
#   Before/after comparison = 3–5s
#
# Total video length targets:
#   TikTok/Reels: 15–30s (5–10 images at ~3s each)
#   YouTube Shorts: 30–55s (10–18 images at ~3s each)
#   YouTube Long: 2–5min (40–100 images or mixed with video)
#   Instagram carousel-as-video: 15–30s
```

---

## PHASE 3: MOTION — Ken Burns, Zoom, Pan

**Agent: static images = viewer scroll-away. Every image MUST have motion. But motion must serve meaning — random zoom is worse than no zoom.**

### Motion presets

| Motion type | When to use | FFmpeg zoompan params |
|:---|:---|:---|
| Slow zoom IN | Reveal detail, draw attention to center | `z='min(zoom+0.001,1.15)'` |
| Slow zoom OUT | Show context, "reveal" moment | `z='if(lte(zoom,1.0),1.15,max(1.001,zoom-0.001))'` |
| Pan LEFT→RIGHT | Show wide UI, scan a list/table | `x='if(lte(on,1),0,x+2)':y='ih/2-(ih/zoom/2)'` |
| Pan RIGHT→LEFT | Return scan, second look | `x='if(lte(on,1),iw/zoom-iw,x-2)':y='ih/2-(ih/zoom/2)'` |
| Pan UP→DOWN | Show full-screen app, scroll effect | `x='iw/2-(iw/zoom/2)':y='if(lte(on,1),0,y+1)'` |
| Hold still + subtle drift | When the image content is text-heavy | `z='min(zoom+0.0003,1.05)'` (barely visible) |

### Generate individual clips with Ken Burns

```bash
# ponytail: ALWAYS -framerate 30 on image inputs. This is the #1 cause of broken output.
# Duration per image: 3 seconds = 90 frames at 30fps

IMG_DURATION=3  # seconds per image — adjust per beat assignment
FRAMES=$((IMG_DURATION * 30))

# Zoom IN (default for most images)
ffmpeg -framerate 30 -loop 1 -t "$IMG_DURATION" -i "$OUT_DIR/prepared/01_hook.png" \
  -vf "zoompan=z='min(zoom+0.001,1.15)':d=${FRAMES}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30" \
  -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/frames/01_hook.mp4"

# Zoom OUT (for reveal/context beats)
ffmpeg -framerate 30 -loop 1 -t "$IMG_DURATION" -i "$OUT_DIR/prepared/02_context.png" \
  -vf "zoompan=z='if(lte(zoom,1.0),1.15,max(1.001,zoom-0.001))':d=${FRAMES}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30" \
  -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/frames/02_context.mp4"

# Pan LEFT→RIGHT (for wide screenshots)
ffmpeg -framerate 30 -loop 1 -t "$IMG_DURATION" -i "$OUT_DIR/prepared/03_wide.png" \
  -vf "zoompan=z='1.15':d=${FRAMES}:x='if(lte(on,1),0,min(x+2,iw/zoom-iw))':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30" \
  -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/frames/03_wide.mp4"

# Minimal drift (for text-heavy images — don't distract from reading)
ffmpeg -framerate 30 -loop 1 -t 4 -i "$OUT_DIR/prepared/04_text.png" \
  -vf "zoompan=z='min(zoom+0.0003,1.05)':d=120:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30" \
  -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/frames/04_text.mp4"
```

### Batch all images with default zoom IN
```bash
# When user gives many images and doesn't specify per-image motion:
i=1
for img in "$OUT_DIR/prepared/"*.png; do
  IDX=$(printf "%02d" $i)
  ffmpeg -framerate 30 -loop 1 -t "$IMG_DURATION" -i "$img" \
    -vf "zoompan=z='min(zoom+0.001,1.15)':d=${FRAMES}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30" \
    -c:v libx264 -pix_fmt yuv420p -r 30 -y \
    "$OUT_DIR/frames/${IDX}_clip.mp4"
  
  # Anti-hallucination: verify each clip immediately
  [ -f "$OUT_DIR/frames/${IDX}_clip.mp4" ] || { echo "FAIL: clip $IDX not created"; continue; }
  DUR_CHECK=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$OUT_DIR/frames/${IDX}_clip.mp4")
  echo "Clip $IDX: ${DUR_CHECK}s"
  
  i=$((i + 1))
done
echo "Total clips: $((i - 1))"
```

---

## PHASE 4: TRANSITIONS — Crossfade, Fade, Hard Cut

**Agent: transitions connect the visual story. The wrong transition breaks pacing. Follow the rules.**

### Transition rules
| Transition | When to use | When NOT to use |
|:---|:---|:---|
| Hard cut (no transition) | Fast pacing, TikTok, pattern interrupt | Story beats, emotional moments |
| Crossfade (0.3–0.5s) | Standard flow, related images in sequence | Between totally unrelated images |
| Fade to black (0.3s) | Chapter break, time skip, topic change | Between consecutive steps of same process |
| Fade from black (0.3s) | Opening, return from chapter break | Mid-sequence (feels like a restart) |
| Wipe | Almost never — looks dated | Everywhere |

### Method A: Concat with crossfade (recommended — cleanest)

```bash
# Step 1: Create concat list
ls "$OUT_DIR/frames/"*.mp4 | sort > "$OUT_DIR/concat_list.txt"
CLIP_COUNT=$(wc -l < "$OUT_DIR/concat_list.txt")

# Step 2: Build crossfade filter chain
# ponytail: xfade filter requires ffmpeg 4.3+. Each xfade takes 2 inputs and produces 1 output.
# For N clips with crossfade duration CF, total duration = sum(durations) - (N-1)*CF

CF=0.5  # crossfade duration in seconds

# For 2 clips:
ffmpeg -i "$OUT_DIR/frames/01_clip.mp4" -i "$OUT_DIR/frames/02_clip.mp4" \
  -filter_complex "[0:v][1:v]xfade=transition=fade:duration=${CF}:offset=$((IMG_DURATION - 1))[v]" \
  -map "[v]" -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/two_clips_faded.mp4"

# For 3+ clips: chain xfade filters
# Example for 4 clips (3s each, 0.5s crossfade):
ffmpeg \
  -i "$OUT_DIR/frames/01_clip.mp4" \
  -i "$OUT_DIR/frames/02_clip.mp4" \
  -i "$OUT_DIR/frames/03_clip.mp4" \
  -i "$OUT_DIR/frames/04_clip.mp4" \
  -filter_complex "\
    [0:v][1:v]xfade=transition=fade:duration=${CF}:offset=2.5[v01]; \
    [v01][2:v]xfade=transition=fade:duration=${CF}:offset=5.0[v012]; \
    [v012][3:v]xfade=transition=fade:duration=${CF}:offset=7.5[vout]" \
  -map "[vout]" -c:v libx264 -pix_fmt yuv420p -r 30 -y \
  "$OUT_DIR/assembled_crossfade.mp4"
```

### Method B: Concat without transitions (hard cuts — fastest, TikTok-style)

```bash
# Create concat file
for clip in "$OUT_DIR/frames/"*.mp4; do
  echo "file '$clip'" 
done | sort > "$OUT_DIR/concat.txt"

# Concat with re-encode (safe — handles different codecs/settings)
ffmpeg -f concat -safe 0 -i "$OUT_DIR/concat.txt" \
  -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p -r 30 \
  -y "$OUT_DIR/assembled_hardcut.mp4"
```

### Method C: Dynamic crossfade generator for any number of images

```bash
# ponytail: this script auto-generates the xfade filter chain for N clips
# Usage: bash generate_xfade.sh "$OUT_DIR/frames" 3 0.5
#   args: clip_dir, seconds_per_clip, crossfade_duration

CLIP_DIR="$OUT_DIR/frames"
CLIP_DUR="$IMG_DURATION"
CF=0.5

CLIPS=($(ls "$CLIP_DIR"/*.mp4 | sort))
N=${#CLIPS[@]}

if [ "$N" -lt 2 ]; then
  echo "Need at least 2 clips"; exit 1
fi

# Build input args
INPUTS=""
for clip in "${CLIPS[@]}"; do
  INPUTS="$INPUTS -i $clip"
done

# Build xfade filter chain
FILTER=""
PREV="0:v"
for ((i=1; i<N; i++)); do
  OFFSET=$(echo "$CLIP_DUR * $i - $CF * $i" | bc -l)
  if [ $i -eq 1 ]; then
    FILTER="[$PREV][$i:v]xfade=transition=fade:duration=${CF}:offset=${OFFSET}[v${i}]"
  else
    FILTER="$FILTER; [v$((i-1))][$i:v]xfade=transition=fade:duration=${CF}:offset=${OFFSET}[v${i}]"
  fi
  PREV="v${i}"
done

echo "ffmpeg $INPUTS -filter_complex \"$FILTER\" -map \"[v$((N-1))]\" -c:v libx264 -pix_fmt yuv420p -r 30 -y \"$OUT_DIR/assembled.mp4\""

# Execute
eval "ffmpeg $INPUTS -filter_complex \"$FILTER\" -map \"[v$((N-1))]\" -c:v libx264 -pix_fmt yuv420p -r 30 -y \"$OUT_DIR/assembled.mp4\""
```

### Available xfade transitions (use sparingly)
```
fade        — standard crossfade (DEFAULT — use this 90% of the time)
fadeblack   — fade through black (chapter breaks)
fadewhite   — fade through white (clinical/medical content)
wipeleft    — horizontal wipe (use rarely)
slideup     — slide from bottom (mobile-native feel)
slidedown   — slide from top
circlecrop  — circle reveal (attention-grabbing but gimmicky)
dissolve    — softer than fade (emotional content)
```
**Rule: use `fade` by default. Use `fadeblack` for topic changes. Use anything else only with a specific WHY.**

---

## PHASE 5: AUDIO — Music, Voiceover, Normalization

```bash
# Option A: Add background music only
ffmpeg -i "$OUT_DIR/assembled.mp4" -i music.mp3 \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -af "afade=t=in:st=0:d=1,afade=t=out:st=$((TOTAL_DURATION - 2)):d=2,loudnorm=I=-14:LRA=11:TP=-1" \
  -shortest -y "$OUT_DIR/with_music.mp4"

# Option B: Add voiceover + ducked music
ffmpeg -i "$OUT_DIR/assembled.mp4" -i voiceover.wav -i music.mp3 \
  -filter_complex "\
    [2:a]volume=0.25,afade=t=in:st=0:d=1,afade=t=out:st=$((TOTAL_DURATION - 2)):d=2[bg]; \
    [1:a][bg]amix=inputs=2:duration=first:dropout_transition=2[mixed]; \
    [mixed]loudnorm=I=-14:LRA=11:TP=-1[aout]" \
  -map 0:v -map "[aout]" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -shortest -y "$OUT_DIR/with_vo_music.mp4"

# Option C: No audio (silent — for muted autoplay feeds)
# Just copy the assembled video. Add captions instead (Phase 6).
cp "$OUT_DIR/assembled.mp4" "$OUT_DIR/silent_version.mp4"
```

### Platform loudness targets
```bash
# Always normalize audio to the platform target
# YouTube / Instagram / Facebook / LinkedIn: -14 LUFS
ffmpeg -i input.mp4 -af "loudnorm=I=-14:LRA=11:TP=-1" -c:v copy -c:a aac -b:a 192k -ar 48000 -y output_14.mp4

# TikTok: -11 LUFS (louder feed)
ffmpeg -i input.mp4 -af "loudnorm=I=-11:LRA=11:TP=-1" -c:v copy -c:a aac -b:a 192k -ar 48000 -y output_11.mp4
```

---

## PHASE 6: CAPTIONS — Text Overlays and Subtitles

```bash
# Method 1: Burn SRT captions (if voiceover exists)
# Linux: verify font first
if [ "$OS_TYPE" = "Linux" ]; then
  FONT=$(fc-list | grep -i "DejaVu Sans" | head -1 | cut -d: -f1)
  [ -n "$FONT" ] || { apt install -y fonts-dejavu-core && fc-cache -fv; FONT="DejaVu Sans"; }
  FONT_NAME="DejaVu Sans"
else
  FONT_NAME="Arial"
fi

ffmpeg -i "$OUT_DIR/with_music.mp4" \
  -vf "subtitles=captions.srt:force_style='FontName=${FONT_NAME},FontSize=24,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=2,MarginV=80'" \
  -c:v libx264 -crf 18 -c:a copy -y \
  "$OUT_DIR/captioned.mp4"

# Method 2: Static text overlay on specific clips (e.g., CTA text)
# Use drawtext for a text card overlay on the last image/clip
ffmpeg -i "$OUT_DIR/frames/05_cta.mp4" \
  -vf "drawtext=text='Save this for later':fontfile='/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf':fontsize=48:fontcolor=white:borderw=3:bordercolor=black:x=(w-text_w)/2:y=(h-text_h)/2" \
  -c:v libx264 -crf 18 -c:a copy -y \
  "$OUT_DIR/frames/05_cta_text.mp4"
```

**Linux drawtext font path:**
```bash
# Find a usable bold font on Linux
FONT_PATH=$(fc-match --format='%{file}\n' "DejaVu Sans:Bold" 2>/dev/null)
echo "Using font: $FONT_PATH"
# Typical: /usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf
```

**macOS drawtext font path:**
```bash
# macOS font locations
FONT_PATH="/System/Library/Fonts/Helvetica.ttc"
# Or for bold Arial:
FONT_PATH="/Library/Fonts/Arial Bold.ttf"
```

---

## PHASE 7: EXPORT — Platform-Specific Renders

**Agent: render the MASTER first at the primary format, then derive all other formats from the master. Never render from source images again — use the assembled video.**

### All-platform export script

```bash
MASTER="$OUT_DIR/captioned.mp4"  # or with_music.mp4 if no captions
[ -f "$MASTER" ] || { echo "FAIL: master not found at $MASTER"; exit 1; }

# 9:16 Short — TikTok, Reels, YT Shorts
ffmpeg -i "$MASTER" \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 15M -maxrate 20M -bufsize 30M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" \
  -y "$OUT_DIR/exports/short_916.mp4"

# 16:9 Long — YouTube, LinkedIn
ffmpeg -i "$MASTER" \
  -c:v libx264 -profile:v high -level 4.1 \
  -b:v 20M -maxrate 25M -bufsize 40M \
  -r 30 -g 60 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1920:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black" \
  -y "$OUT_DIR/exports/long_169.mp4"

# 4:5 Feed — Instagram Feed, Facebook Feed
ffmpeg -i "$MASTER" \
  -c:v libx264 -crf 18 \
  -r 30 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1350:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1350:(ow-iw)/2:(oh-ih)/2:black" \
  -y "$OUT_DIR/exports/feed_45.mp4"

# 1:1 Square — Instagram, Facebook, LinkedIn, Twitter
ffmpeg -i "$MASTER" \
  -c:v libx264 -crf 18 \
  -r 30 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:black" \
  -y "$OUT_DIR/exports/feed_11.mp4"

# 2:3 Pin — Pinterest
ffmpeg -i "$MASTER" \
  -c:v libx264 -crf 18 \
  -r 30 \
  -c:a aac -b:a 192k -ar 48000 \
  -movflags +faststart \
  -vf "scale=1000:1500:force_original_aspect_ratio=decrease:flags=lanczos,pad=1000:1500:(ow-iw)/2:(oh-ih)/2:black" \
  -y "$OUT_DIR/exports/pin_23.mp4"

# TikTok loudness variant (same video, louder audio)
ffmpeg -i "$OUT_DIR/exports/short_916.mp4" \
  -af "loudnorm=I=-11:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -y "$OUT_DIR/exports/tiktok_916.mp4"

echo "=== EXPORTS COMPLETE ==="
ls -lh "$OUT_DIR/exports/"*.mp4
```

### Quality settings per platform

| Platform | Codec | Bitrate (video) | CRF | Audio | Notes |
|:---|:---|:---|:---|:---|:---|
| TikTok | H.264 High | 15M | — | AAC 192k, -11 LUFS | Louder audio, -movflags +faststart |
| Instagram Reels | H.264 High | 15M | — | AAC 192k, -14 LUFS | Same as TikTok but quieter |
| YouTube Shorts | H.264 High | 15M | — | AAC 192k, -14 LUFS | |
| YouTube Long | H.264 High | 20M | — | AAC 192k, -14 LUFS | Higher bitrate for 1080p |
| Instagram Feed | H.264 | — | 18 | AAC 192k, -14 LUFS | CRF mode OK for feed |
| LinkedIn | H.264 | — | 18 | AAC 192k, -14 LUFS | 16:9 or 1:1 only |
| Pinterest | H.264 | — | 18 | AAC 192k, -14 LUFS | 2:3 vertical |
| Twitter/X | H.264 | — | 18 | AAC 192k, -14 LUFS | Max 2:20 length, 16:9 or 1:1 |

---

## PHASE 8: QA — Verify Every Export

```bash
echo "=== QA REPORT ===" > "$OUT_DIR/reports/qa_report.txt"

for output in "$OUT_DIR/exports/"*.mp4; do
  echo "--- $(basename "$output") ---" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 1. File exists and has video stream
  ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$output" | grep -q video \
    && echo "  Video stream: PASS" | tee -a "$OUT_DIR/reports/qa_report.txt" \
    || echo "  Video stream: FAIL" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 2. Resolution
  RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$output")
  echo "  Resolution: $RES" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 3. Duration
  DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$output")
  echo "  Duration: ${DUR}s" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 4. FPS
  FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$output")
  echo "  FPS: $FPS" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 5. Audio sample rate
  SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$output")
  echo "  Sample rate: $SR" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 6. File size
  SIZE=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null)
  echo "  File size: $((SIZE / 1024 / 1024))MB" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
  # 7. Extract QA frames
  for pct in 25 50 75; do
    T=$(echo "$DUR * $pct / 100" | bc -l)
    ffmpeg -ss "$T" -i "$output" -frames:v 1 "$OUT_DIR/reports/qa_$(basename "$output" .mp4)_${pct}pct.png" -y 2>/dev/null
  done
  echo "  QA frames: extracted" | tee -a "$OUT_DIR/reports/qa_report.txt"
  
done

echo ""
echo "=== QA SUMMARY ==="
echo "Review QA frames in: $OUT_DIR/reports/"
echo "Review QA report: $OUT_DIR/reports/qa_report.txt"
echo ""
echo "Three-state verdict required for each export:"
echo "  PASS = resolution correct, FPS=30, audio=48000Hz, duration>0, frames clean"
echo "  FAIL = any check failed → fix → re-render → re-check"
echo "  INCONCLUSIVE = could not verify → NOT PASS → blocks done"
```

---

## SHARPNESS AND CLARITY — Expert Settings

**Why images-to-video often looks soft/blurry, and how to prevent it:**

| Cause | Fix |
|:---|:---|
| FFmpeg default bilinear scaling | Always `flags=lanczos` in scale filter |
| Chroma subsampling on text edges | Use `yuv444p` intermediate, `yuv420p` only on final export |
| JPEG compression artifacts from source | Use PNG sources when possible; if JPEG, don't re-encode more than once |
| Over-zooming with Ken Burns | Keep zoom ≤ 1.15× for app/UI content (brand ceiling) |
| Low bitrate on text-heavy content | Use CRF 14–16 for screen content, not CRF 23+ |
| Missing sharpening after downscale | Always `unsharp=5:5:0.8:3:3:0.4` after any scale operation |
| Linux: missing font hinting | Install `fonts-liberation` + `fonts-dejavu-core` + run `fc-cache -fv` |
| macOS Retina: 2x resolution fed directly | Downscale with lanczos before assembly |

### Maximum sharpness pipeline (for UI/app screenshots)

```bash
# ponytail: this is the gold standard for sharp screen-content video from images
# Use yuv444p intermediate for maximum text edge quality, yuv420p only on final export

ffmpeg -framerate 30 -loop 1 -t "$IMG_DURATION" -i prepared_image.png \
  -vf "zoompan=z='min(zoom+0.001,1.12)':d=${FRAMES}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${TARGET_W}x${TARGET_H}:fps=30,unsharp=5:5:0.8:3:3:0.4" \
  -c:v libx264 -preset slow -crf 14 -pix_fmt yuv444p -r 30 -y \
  intermediate_sharp.mp4

# Then on final export, convert to yuv420p for platform compatibility:
ffmpeg -i intermediate_sharp.mp4 \
  -c:v libx264 -profile:v high -crf 16 -pix_fmt yuv420p -r 30 \
  -c:a copy -movflags +faststart -y \
  final_sharp.mp4
```

---

## LINUX vs macOS — Template 7 Specific Differences

| Step | macOS | Linux |
|:---|:---|:---|
| Image probe | `stat -f%z` for file size | `stat -c%s` for file size |
| Font for drawtext | `/System/Library/Fonts/Helvetica.ttc` or `/Library/Fonts/Arial Bold.ttf` | `/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf` — install if missing |
| Font for subtitles | `FontName=Arial` (always available) | `FontName=DejaVu Sans` (install `fonts-dejavu-core` first) |
| ICC profiles | macOS screenshots embed Display P3 — ffmpeg handles it | Strip ICC with `colorspace` filter to avoid color shift |
| Glob expansion | `images/*.{png,jpg}` works in zsh | May need `shopt -s nullglob` in bash; or use `find` |
| bc precision | Pre-installed | Usually pre-installed; `apt install bc` if missing |
| GPU acceleration | VideoToolbox: `-c:v h264_videotoolbox` (faster but less quality control) | NVENC: `-c:v h264_nvenc` (if NVIDIA GPU available) |

### Linux-only: install all dependencies in one shot
```bash
apt update && apt install -y ffmpeg bc jq fonts-liberation fonts-dejavu-core fontconfig imagemagick python3 python3-pip
pip install openai-whisper pysubs2 2>/dev/null
fc-cache -fv
echo "All dependencies installed"
```

### macOS-only: install all dependencies in one shot
```bash
brew install ffmpeg jq imagemagick python3
pip3 install openai-whisper pysubs2 2>/dev/null
echo "All dependencies installed"
```

---

## COMPLETE EXAMPLE: 8 App Screenshots → TikTok + Reels + YouTube Short

```
1. Set variables:
   IMAGE_DIR="./screenshots"
   OUT_DIR="./output"
   TARGET_W=1080  TARGET_H=1920  (9:16 primary)
   IMG_DURATION=3
   CF=0.5  (crossfade)

2. Phase 0: Inventory
   - Probe all 8 images → confirm 8 files found, note resolutions

3. Phase 1: Preparation
   - Normalize all 8 to 1080×1920 with lanczos + pad + unsharp
   - Linux: strip ICC profiles, verify fonts installed
   - macOS: detect Retina, handled by scale filter

4. Phase 2: Storyboard (5-beat ad mode)
   - 01_hook.png     → Beat 1 (Hook)     → 2s, zoom IN
   - 02_problem.png  → Beat 2 (Problem)  → 3s, zoom IN
   - 03_solution.png → Beat 2 cont.      → 3s, pan L→R
   - 04_proof1.png   → Beat 3 (Proof)    → 3s, zoom IN
   - 05_proof2.png   → Beat 3 cont.      → 3s, minimal drift
   - 06_how1.png     → Beat 4 (How)      → 3s, zoom IN
   - 07_how2.png     → Beat 4 cont.      → 3s, pan U→D
   - 08_cta.png      → Beat 5 (CTA)      → 2.5s, zoom OUT + text overlay

5. Phase 3: Motion → 8 individual clips with Ken Burns

6. Phase 4: Transitions → Crossfade 0.5s between all clips
   Total: 8×3s - 7×0.5s = 20.5s (perfect TikTok/Reels length)

7. Phase 5: Audio → Background music, fade in/out, normalize -14 LUFS
   Also export -11 LUFS variant for TikTok

8. Phase 6: Captions → "Save this clinical tool" on CTA slide

9. Phase 7: Export →
   - short_916.mp4      (9:16, -14 LUFS)  → Reels, YT Shorts
   - tiktok_916.mp4     (9:16, -11 LUFS)  → TikTok
   - feed_45.mp4        (4:5, -14 LUFS)   → IG Feed
   - feed_11.mp4        (1:1, -14 LUFS)   → IG/FB/LinkedIn

10. Phase 8: QA → 7-check verification on each export
    All PASS → done.
```

---

## PRO-TIPS: Images → Video (Template 7 Specific)

1. **First image = strongest visual.** Never start with a logo, title card, or boring setup. The hook image must be the most visually striking.
2. **Alternate zoom direction.** Zoom IN on image 1, zoom OUT on image 2, zoom IN on image 3. Alternating creates visual rhythm without gimmicky transitions.
3. **Text-heavy images need longer duration.** If the image has numbers, labels, or UI text, give it 4–5s instead of 3s. Viewers need time to read.
4. **Don't crossfade between very different images.** If image 1 is bright blue and image 2 is dark red, the crossfade creates an ugly purple middle frame. Use a hard cut or fadeblack instead.
5. **Sharpen AFTER zoompan, not before.** Ken Burns applies scaling internally — sharpening the source image and then zooming it just re-blurs. Apply `unsharp` as the last filter in the chain.
6. **PNG sources only when possible.** JPEG artifacts compound: source JPEG → decode → encode → decode → export = 4 compression rounds. PNG → encode → export = 1 round.
7. **For app UI screenshots: zoom ceiling = 1.15×.** Zooming more than 1.15× on app interfaces starts showing pixel artifacts on text. For non-UI images (photos, illustrations), you can go up to 1.25×.
8. **Background music volume: 10–15% of voiceover.** If no voiceover, music at 70% max loudness. Background music should be felt, not heard.
9. **Test the mute version.** Play the video with volume at zero. If the story is not clear, add more text overlays or captions. On Instagram/Facebook, 85% of video views start muted.
10. **Final frame matters for loops.** If the CTA image has a visual element that connects to the hook image (same color, same shape, same position), the loop feels natural and triggers rewatches.
11. **Linux server: always use `-pix_fmt yuv420p` on final export.** Without this, some ffmpeg builds default to yuv444p which won't play on mobile devices.
12. **macOS: if using `glob` patterns, run in `zsh` (default shell).** Bash on macOS may not expand `{png,jpg}` without `shopt -s extglob`.

---

*Template 7 converts any group of images into a produced, platform-ready video. The agent follows the 8-phase pipeline (Inventory → Preparation → Storyboard → Motion → Transitions → Audio → Captions → Export → QA), detects the OS, and produces verified exports for every target platform.*
TEMPLATE 8

Template 8 (U8) — Voice-Over + SEO Captions Master Package

31 Executable Code Blocks

Verbatim executive directives and code for Template 8 (VO script generation, edge-tts synthesis, dual AR+EN SRT captions, and SEO packaging) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 31 Code Blocks
Paste-ready package · 1010 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
==================================


////////////\\\\\\\\\\\\\\\\\\\\\\\\////////////////////////\\\\\\\\\\\\////
Template-8: Voice-Over Generation + SEO-Content UI-Screen Captions
-------------------------------------------------------------------

**Agent directive: The user has a video and wants you to produce human-quality voice-over audio files (two types: mixed Arabic+English-terms, and pure English), matching timeline-aligned transcripts with SEO content, and caption burn-in compatible with the on-screen UI. You must deliver exactly 5 files in one uniquely named folder per video. Follow the pipeline below phase by phase. Do NOT invent UI text that is not on screen. Every audio must sound human-fluent — not robotic, not AI-interrupted, not clipped.**

---

## THE METHOD — How the AI Agent Produces Voice-Over + SEO Captions

```
PHASE 1: PERCEIVE     → Probe video. Extract key frames. Read on-screen UI text at each timestamp.
PHASE 2: SCRIPT       → Write two timeline-aligned VO scripts (Type-1 mixed AR+EN, Type-2 pure EN).
PHASE 3: GENERATE     → Synthesize audio from scripts using high-quality TTS. Both types.
PHASE 4: SEGMENT      → Split/adjust audio segments to match video timeline beats. Ensure A/V sync.
PHASE 5: MUX + CAPTION → Combine audio with video. Generate SRT. Burn captions into video.
PHASE 6: PACKAGE      → Create the 5-file delivery folder. Generate USAGE_TIPS.md.
PHASE 7: QA           → Verify audio quality, A/V sync, transcript accuracy, anti-hallucination.
```

**Why this order:**
- You cannot write a script until you know what is on screen at each timestamp (Phase 1→2).
- You cannot generate audio until the script is locked (Phase 2→3).
- You cannot sync audio until segment durations are known (Phase 3→4).
- You cannot burn captions until the audio timing is final (Phase 4→5).
- You cannot package until everything is verified (Phase 5→6→7).

---

## TOOL SELECTION — Free-First, Human-Quality

**Agent: choose the best available TTS tool. Prefer free/local. Never use a tool that produces robotic or glitchy output.**

### Recommended tools (priority order)

| Tool | Quality | Languages | Cost | Install |
|:---|:---|:---|:---|:---|
| **edge-tts** (Microsoft Edge) | High — neural voices, natural prosody | Arabic (ar-SA, ar-EG, ar-KW), English (en-US, en-GB, en-AU) | Free | `pip install edge-tts` |
| **OpenAI TTS** | Very high — near-human | English excellent, Arabic limited | Paid (API key) | `pip install openai` |
| **Google Cloud TTS** | High — WaveNet voices | Arabic + English | Free tier then paid | `pip install google-cloud-texttospeech` |
| **piper-tts** | Good — fully offline | English good, Arabic limited models | Free | `pip install piper-tts` |
| **Coqui TTS** | Good — open source | English good, Arabic via XTTS | Free | `pip install TTS` |

**Default choice: `edge-tts`** — free, high quality, supports both Arabic and English with neural voices, no API key needed.

### Best voices for this workflow

```bash
# List all available Arabic voices
edge-tts --list-voices | grep ar-

# Recommended Arabic voices (natural, fluent):
#   ar-SA-HamedNeural     — Saudi male, clear and professional
#   ar-SA-ZariyahNeural   — Saudi female, warm and fluent
#   ar-EG-SalmaNeural     — Egyptian female, natural conversational
#   ar-EG-ShakirNeural    — Egyptian male, authoritative

# List all available English voices
edge-tts --list-voices | grep en-

# Recommended English voices (natural, not robotic):
#   en-US-GuyNeural       — US male, professional narrator
#   en-US-JennyNeural     — US female, clear and warm
#   en-GB-RyanNeural      — UK male, authoritative
#   en-US-AriaNeural      — US female, conversational
```

### Install all VO tools (one-shot)

```bash
OS_TYPE="$(uname -s)"

# Core TTS
pip install edge-tts pysubs2 2>/dev/null
# For reverse-QA (verify generated audio matches script)
pip install openai-whisper 2>/dev/null

# Linux font deps (for caption burn-in)
if [ "$OS_TYPE" = "Linux" ]; then
  apt install -y ffmpeg bc jq fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv
fi

# macOS deps
if [ "$OS_TYPE" = "Darwin" ]; then
  brew install ffmpeg jq 2>/dev/null
fi

echo "All VO tools installed"
```

---

## PHASE 1: PERCEIVE — Probe Video and Extract UI Text

**Agent: you MUST know what is on screen at every key timestamp before writing any script. Do NOT invent UI content.**

### Step 1a: Probe video metadata

```bash
VIDEO="$1"  # input video path
[ -f "$VIDEO" ] || { echo "FAIL: video not found at $VIDEO"; exit 1; }

# Get video facts
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VIDEO")
RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$VIDEO")
FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$VIDEO")
echo "Video: $VIDEO"
echo "Duration: ${DUR}s | Resolution: $RES | FPS: $FPS"
```

### Step 1b: Extract key frames at regular intervals

```bash
# Extract one frame per second for UI text reading
mkdir -p frames_ui_read
TOTAL_SECS=$(echo "$DUR" | cut -d. -f1)
for s in $(seq 0 "$TOTAL_SECS"); do
  ffmpeg -ss "$s" -i "$VIDEO" -frames:v 1 -y "frames_ui_read/frame_${s}s.png" 2>/dev/null
done
echo "Extracted $((TOTAL_SECS + 1)) frames for UI reading"
```

### Step 1c: Extract frames at beat boundaries (for short videos)

```bash
# For short-form videos (< 60s), extract at finer granularity + key moments
for pct in 0 10 20 30 40 50 60 70 80 90 100; do
  T=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i "$VIDEO" -frames:v 1 -y "frames_ui_read/beat_${pct}pct.png" 2>/dev/null
done
```

### Step 1d: Build the UI timeline map

**Agent: look at each extracted frame. For each one, write down:**
1. **Timestamp** (mm:ss or ss.ms)
2. **What is visible** — screen name, buttons, labels, numbers, results, transitions
3. **What changed** from the previous frame — new screen, new value, animation, tap

**Write this into a structured timeline:**

```markdown
## UI Timeline Map — {video_name}

| Timestamp | Screen / UI state | Key elements visible | Action / transition |
|:---|:---|:---|:---|
| 00:00 | App splash / home screen | Logo, main nav buttons | App opens |
| 00:02 | Input form | Text fields: Name, Age, Weight | User starts typing |
| 00:05 | Input form (filled) | Values: "Ahmed", "32", "85kg" | Fields populated |
| 00:08 | Calculate button tap | "Calculate" button highlighted | Button tap animation |
| 00:09 | Results screen | Dose: 250mg, Frequency: 2x/day | Results appear |
| 00:13 | Results detail | Chart, breakdown table | Scroll down |
| 00:18 | Share/Save options | "Share", "Save PDF", "Copy" buttons | Final screen |
| 00:22 | End card / CTA | Logo + "Download Now" | Fade out |
```

**Anti-hallucination checkpoint:** Every row in this table must correspond to an actual frame you extracted. If you cannot read a UI element clearly, write `[unclear]` — do NOT guess.

---

## PHASE 2: SCRIPT — Write Timeline-Aligned Voice-Over Scripts

**Agent: write TWO scripts. Both must follow the UI timeline map exactly. Every line must have a timestamp that matches an actual on-screen event.**

### Script format (both types)

```markdown
## Voice-Over Script — Type [1 or 2]
## Video: {video_name}
## Duration: {duration}s
## Generated: {date}

| Start | End | Text | UI sync note |
|:---|:---|:---|:---|
| 00:00 | 00:02 | [spoken text for this segment] | [what's on screen] |
| 00:02 | 00:05 | [spoken text for this segment] | [what's on screen] |
...
```

### Type-1 script rules — Mixed Arabic + English app terms

**The script is Arabic-dominant. English appears ONLY for:**
- App-specific terms that have no natural Arabic equivalent (e.g., "Calculate", "Dashboard", "BMI")
- UI button labels that appear in English on screen
- Technical terms the target audience uses in English (e.g., "dose", "app")

**What Type-1 sounds like (example):**
```
"هذا التطبيق يساعدك تحسب الجرعة المناسبة. دلوقتي هندخل البيانات — الاسم والعمر والوزن.
بعد ما نضغط Calculate, النتيجة بتظهر: dose 250mg, مرتين في اليوم.
تقدر تعمل Share أو Save PDF من هنا."
```

**NOT this (wrong — forced English blocks):**
```
❌ "Welcome to the app. Now I will show you the features. [then Arabic block]"
```

**Script writing rules:**
1. Arabic sentence structure dominates. English terms drop in naturally mid-sentence.
2. The mix follows what is actually on the screen — if the button says "Calculate" in English, say "Calculate" in English.
3. If the UI is fully Arabic, the VO is fully Arabic for that segment.
4. Each line duration must be speakable in the allotted time — read it aloud mentally, time it.
5. Segment duration should leave ~0.3s breathing room before the next UI event.

### Type-2 script rules — Pure English

**Every word is English. Natural native-speaker narration. Professional tone.**

**What Type-2 sounds like (example):**
```
"This app helps you calculate the right dosage. Let's enter the details —
name, age, and weight. After tapping Calculate, the result appears:
250 milligrams, twice daily. You can share or save the result as a PDF."
```

**Script writing rules:**
1. Fluent, conversational English. Not stiff. Not overly casual.
2. Active voice. Present tense ("the result appears", not "the result will appear").
3. Match the same UI timeline as Type-1 — same beats, same timestamps.
4. SEO-friendly phrasing: use real feature names, app terms, action verbs.
5. Each segment must be speakable within the timestamp window.

### SEO integration in both scripts

**Both transcripts double as SEO content. Build in naturally:**
- Feature names as they appear in the UI (e.g., "dose calculator", "patient profile")
- Action verbs (calculate, enter, save, share, export, customize)
- Problem-solution framing ("struggling with dosage calculations? This app solves it")
- Platform-ready hooks in the first line (for video descriptions, captions)

---

## PHASE 3: GENERATE — Synthesize Human-Quality Audio

### Method A: edge-tts (recommended — free, neural, both languages)

```bash
# ponytail: edge-tts produces the most natural free TTS available.
# It supports SSML for pauses, emphasis, and prosody control.

# === Type-1: Mixed Arabic + English terms ===
# Strategy: Generate Arabic segments and English-term segments separately,
# then stitch them. This avoids the "language switch glitch" that happens
# when a single voice tries to switch languages mid-sentence.

# Option 1: Single Arabic voice for everything (simpler, good for short insertions)
# The Arabic neural voice handles short English terms acceptably
edge-tts \
  --voice "ar-SA-HamedNeural" \
  --rate "+5%" \
  --pitch "+0Hz" \
  --file script_type1_text.txt \
  --write-media "$OUT_DIR/vo_mixed_ar_en_terms_raw.mp3" \
  --write-subtitles "$OUT_DIR/vo_mixed_ar_en_terms_raw.vtt"

# Option 2: Segment-by-segment (best quality — each segment uses the right voice)
# For segments that are pure Arabic:
edge-tts --voice "ar-SA-HamedNeural" --text "هذا التطبيق يساعدك تحسب الجرعة المناسبة" \
  --write-media seg_01_ar.mp3

# For English terms within Arabic context (use Arabic voice — it handles short EN):
edge-tts --voice "ar-SA-HamedNeural" --text "بعد ما نضغط Calculate النتيجة بتظهر" \
  --write-media seg_02_ar_en.mp3

# === Type-2: Pure English ===
edge-tts \
  --voice "en-US-GuyNeural" \
  --rate "+0%" \
  --pitch "+0Hz" \
  --file script_type2_text.txt \
  --write-media "$OUT_DIR/vo_pure_en_raw.mp3" \
  --write-subtitles "$OUT_DIR/vo_pure_en_raw.vtt"
```

### Method B: Python script for segment-by-segment generation with pauses

```python
#!/usr/bin/env python3
"""
generate_vo.py — Generate timeline-aligned voice-over from script segments.
Uses edge-tts. Produces one audio file with correct timing gaps between segments.
"""
import asyncio, edge_tts, json, sys, os, subprocess
from pathlib import Path

# ponytail: single-file script, no frameworks, stdlib + edge-tts only

async def generate_segment(text, voice, output_path, rate="+0%"):
    """Generate one audio segment."""
    communicate = edge_tts.Communicate(text, voice, rate=rate)
    await communicate.save(output_path)

def get_duration(filepath):
    """Get audio duration via ffprobe."""
    cmd = ["ffprobe", "-v", "quiet", "-show_entries", "format=duration",
           "-of", "csv=p=0", filepath]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return float(result.stdout.strip()) if result.stdout.strip() else 0.0

async def generate_full_vo(segments, voice, output_dir, vo_type):
    """
    segments: list of {"start": float, "end": float, "text": str}
    Generates each segment, adds silence gaps to match timeline, concatenates.
    """
    os.makedirs(output_dir, exist_ok=True)
    concat_list = []
    
    for i, seg in enumerate(segments):
        seg_file = f"{output_dir}/seg_{i:03d}.mp3"
        silence_file = f"{output_dir}/silence_{i:03d}.wav"
        
        # Generate speech
        await generate_segment(seg["text"], voice, seg_file)
        seg_dur = get_duration(seg_file)
        
        if seg_dur <= 0:
            print(f"FAIL: segment {i} produced 0-length audio")
            continue
        
        # Calculate gap needed before this segment
        if i == 0 and seg["start"] > 0:
            # Silence before first segment
            subprocess.run([
                "ffmpeg", "-f", "lavfi", "-i",
                f"anullsrc=r=48000:cl=mono:d={seg['start']}",
                "-y", silence_file
            ], capture_output=True)
            concat_list.append(silence_file)
        elif i > 0:
            gap = seg["start"] - segments[i-1]["end"]
            if gap > 0.05:  # only add silence if gap > 50ms
                subprocess.run([
                    "ffmpeg", "-f", "lavfi", "-i",
                    f"anullsrc=r=48000:cl=mono:d={gap}",
                    "-y", silence_file
                ], capture_output=True)
                concat_list.append(silence_file)
        
        concat_list.append(seg_file)
        target_dur = seg["end"] - seg["start"]
        
        # Warn if speech is longer than the timeline window
        if seg_dur > target_dur + 0.5:
            print(f"WARN: segment {i} speech ({seg_dur:.1f}s) > "
                  f"timeline window ({target_dur:.1f}s) — "
                  f"consider shortening text")
    
    # Concatenate all segments with gaps
    list_file = f"{output_dir}/concat_{vo_type}.txt"
    with open(list_file, "w") as f:
        for path in concat_list:
            f.write(f"file '{os.path.abspath(path)}'\n")
    
    final_output = f"{output_dir}/vo_{vo_type}.mp3"
    subprocess.run([
        "ffmpeg", "-f", "concat", "-safe", "0", "-i", list_file,
        "-c:a", "libmp3lame", "-b:a", "192k", "-ar", "48000",
        "-y", final_output
    ], capture_output=True)
    
    final_dur = get_duration(final_output)
    print(f"Generated {vo_type}: {final_output} ({final_dur:.1f}s)")
    return final_output

# Example usage:
# segments_type1 = [
#     {"start": 0.0,  "end": 2.5,  "text": "هذا التطبيق يساعدك تحسب الجرعة المناسبة"},
#     {"start": 2.5,  "end": 5.5,  "text": "دلوقتي هندخل البيانات — الاسم والعمر والوزن"},
#     {"start": 6.0,  "end": 9.0,  "text": "بعد ما نضغط Calculate النتيجة بتظهر"},
#     {"start": 9.5,  "end": 13.0, "text": "dose 250mg مرتين في اليوم"},
#     {"start": 14.0, "end": 18.0, "text": "تقدر تعمل Share أو Save PDF من هنا"},
# ]
# asyncio.run(generate_full_vo(segments_type1, "ar-SA-HamedNeural", "./vo_out", "mixed_ar_en_terms"))
```

### Method C: SSML for natural pauses and emphasis (edge-tts)

```bash
# SSML gives you precise control over pauses, emphasis, and speed within a single generation.
# Use this for the highest quality single-take VO.

cat > script_type2_ssml.xml << 'SSML_EOF'
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
  <voice name="en-US-GuyNeural">
    <prosody rate="0%" pitch="0%">
      This app helps you calculate the right dosage.
      <break time="400ms"/>
      Let's enter the details — name, age, and weight.
      <break time="500ms"/>
      After tapping <emphasis level="moderate">Calculate</emphasis>,
      the result appears instantly.
      <break time="300ms"/>
      <prosody rate="-5%">
        Two hundred and fifty milligrams, twice daily.
      </prosody>
      <break time="400ms"/>
      You can share or save the result as a PDF.
    </prosody>
  </voice>
</speak>
SSML_EOF

edge-tts --voice "en-US-GuyNeural" --file script_type2_ssml.xml \
  --write-media vo_pure_en_ssml.mp3 \
  --write-subtitles vo_pure_en_ssml.vtt
```

### Audio post-processing (ALL generated audio)

```bash
# ponytail: ALWAYS post-process TTS output for broadcast quality

# Step 1: Normalize to -14 LUFS (or -11 for TikTok)
ffmpeg -i vo_raw.mp3 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:a aac -b:a 192k -ar 48000 \
  -y vo_normalized.m4a

# Step 2: Remove any leading/trailing silence (TTS sometimes adds ~0.5s)
ffmpeg -i vo_normalized.m4a \
  -af "silenceremove=start_periods=1:start_silence=0.1:start_threshold=-40dB,areverse,silenceremove=start_periods=1:start_silence=0.1:start_threshold=-40dB,areverse" \
  -c:a aac -b:a 192k -ar 48000 \
  -y vo_trimmed.m4a

# Step 3: Light compression to smooth out TTS volume inconsistencies
ffmpeg -i vo_trimmed.m4a \
  -af "acompressor=threshold=-20dB:ratio=3:attack=5:release=100,loudnorm=I=-14:LRA=11:TP=-1" \
  -c:a aac -b:a 192k -ar 48000 \
  -y vo_final.m4a

# Step 4: Convert to WAV if needed (for editing software compatibility)
ffmpeg -i vo_final.m4a -c:a pcm_s16le -ar 48000 -y vo_final.wav
```

### Quality gate: TTS output check

```bash
# After generating each audio file, immediately verify:
VO_FILE="vo_final.m4a"
[ -f "$VO_FILE" ] || { echo "FAIL: VO file not created"; exit 1; }

VO_DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VO_FILE")
VO_SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$VO_FILE")
echo "VO duration: ${VO_DUR}s (video: ${DUR}s)"
echo "VO sample rate: ${VO_SR}Hz"

# Check: VO should not be longer than video
VO_LONGER=$(echo "$VO_DUR > $DUR + 1" | bc -l)
[ "$VO_LONGER" = "1" ] && echo "WARN: VO is longer than video — trim or speed up"

# Check: sample rate must be 48000
[ "$VO_SR" = "48000" ] || echo "WARN: sample rate is $VO_SR, not 48000 — re-encode"
```

---

## PHASE 4: SEGMENT — Align Audio to Video Timeline

### If VO is slightly too long or short

```bash
# Speed up VO slightly (max 10% — beyond that it sounds unnatural)
SPEED_FACTOR=$(echo "$DUR / $VO_DUR" | bc -l)
echo "Speed adjustment needed: ${SPEED_FACTOR}x"

# Only adjust if within 10% of target
TOO_FAST=$(echo "$SPEED_FACTOR < 0.90" | bc -l)
TOO_SLOW=$(echo "$SPEED_FACTOR > 1.10" | bc -l)

if [ "$TOO_FAST" = "1" ] || [ "$TOO_SLOW" = "1" ]; then
  echo "FAIL: VO length mismatch too large (${SPEED_FACTOR}x). Rewrite script segments."
else
  ffmpeg -i vo_final.m4a \
    -af "atempo=${SPEED_FACTOR},loudnorm=I=-14:LRA=11:TP=-1" \
    -c:a aac -b:a 192k -ar 48000 \
    -y vo_synced.m4a
  echo "Synced VO to video length"
fi
```

### Add padding silence to match video duration exactly

```bash
# If VO is shorter, pad with silence to match video duration
ffmpeg -i vo_final.m4a \
  -af "apad=whole_dur=${DUR}" \
  -c:a aac -b:a 192k -ar 48000 \
  -y vo_padded.m4a
```

---

## PHASE 5: MUX + CAPTION — Combine Audio with Video, Burn Captions

### Step 5a: Mux voice-over into video

```bash
# Mux Type-1 (mixed AR+EN) VO with video
ffmpeg -i "$VIDEO" -i vo_mixed_ar_en_terms.m4a \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -map 0:v:0 -map 1:a:0 \
  -af "apad" -shortest \
  -movflags +faststart \
  -y video_with_vo_type1.mp4

# Mux Type-2 (pure EN) VO with video
ffmpeg -i "$VIDEO" -i vo_pure_en.m4a \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -map 0:v:0 -map 1:a:0 \
  -af "apad" -shortest \
  -movflags +faststart \
  -y video_with_vo_type2.mp4
```

### Step 5b: Generate SRT from transcript

```python
#!/usr/bin/env python3
"""
transcript_to_srt.py — Convert timeline transcript to SRT format.
"""
import sys

def time_to_srt(seconds):
    """Convert seconds to SRT timestamp format HH:MM:SS,mmm"""
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    ms = int((seconds % 1) * 1000)
    return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"

def transcript_to_srt(segments, output_path):
    """
    segments: list of {"start": float, "end": float, "text": str}
    Writes SRT file.
    """
    with open(output_path, "w", encoding="utf-8") as f:
        for i, seg in enumerate(segments, 1):
            f.write(f"{i}\n")
            f.write(f"{time_to_srt(seg['start'])} --> {time_to_srt(seg['end'])}\n")
            f.write(f"{seg['text']}\n\n")
    print(f"SRT written: {output_path} ({len(segments)} cues)")

# Usage:
# segments = [
#     {"start": 0.0, "end": 2.5, "text": "This app helps you calculate the right dosage."},
#     {"start": 2.5, "end": 5.5, "text": "Let's enter the details."},
# ]
# transcript_to_srt(segments, "captions_en.srt")
```

### Step 5c: Burn captions into video

```bash
OS_TYPE="$(uname -s)"

# Detect font
if [ "$OS_TYPE" = "Linux" ]; then
  # Verify font exists
  fc-list | grep -qi "dejavu" || { apt install -y fonts-dejavu-core && fc-cache -fv; }
  FONT_NAME_AR="DejaVu Sans"
  FONT_NAME_EN="DejaVu Sans"
else
  FONT_NAME_AR="Arial"
  FONT_NAME_EN="Arial"
fi

# Burn English captions (Type-2)
ffmpeg -i video_with_vo_type2.mp4 \
  -vf "subtitles=captions_en.srt:force_style='FontName=${FONT_NAME_EN},FontSize=22,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BackColour=&H80000000,BorderStyle=4,Outline=1,Shadow=0,MarginV=60,Alignment=2'" \
  -c:v libx264 -crf 18 -c:a copy \
  -y video_captioned_en.mp4

# Burn Arabic+English captions (Type-1)
# ponytail: Arabic subtitles need RTL-aware rendering.
# edge-tts VTT output handles bidi; ffmpeg subtitles filter supports RTL if the font does.
ffmpeg -i video_with_vo_type1.mp4 \
  -vf "subtitles=captions_ar_en.srt:force_style='FontName=${FONT_NAME_AR},FontSize=22,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BackColour=&H80000000,BorderStyle=4,Outline=1,Shadow=0,MarginV=60,Alignment=2'" \
  -c:v libx264 -crf 18 -c:a copy \
  -y video_captioned_ar_en.mp4
```

### Caption placement — safe zones

```bash
# 9:16 vertical: captions at bottom, above platform UI (IG likes, TT buttons)
# MarginV=60 — safe above bottom navigation
# MarginV=250 — higher up, clear of all platform UI

# 16:9 horizontal: captions at bottom center
# MarginV=40 — standard lower-third position

# 4:5 feed: captions centered vertically in lower third
# MarginV=50
```

---

## PHASE 6: PACKAGE — Create the 5-File Delivery Folder

### Folder naming and structure

```bash
VIDEO_SLUG=$(basename "$VIDEO" | sed 's/\.[^.]*$//' | tr ' ' '_' | tr '[:upper:]' '[:lower:]')
TIMESTAMP=$(date +%Y%m%d-%H%M)
FOLDER_NAME="vo_${VIDEO_SLUG}_${TIMESTAMP}"

mkdir -p "$FOLDER_NAME"

# 1. Copy (or symlink) the source video
cp "$VIDEO" "$FOLDER_NAME/"

# 2. Type-1 mixed AR+EN audio
cp vo_mixed_ar_en_terms.m4a "$FOLDER_NAME/vo_mixed_ar_en_terms.m4a"
# Also provide WAV for editing software
cp vo_mixed_ar_en_terms.wav "$FOLDER_NAME/vo_mixed_ar_en_terms.wav" 2>/dev/null

# 3. Type-1 mixed transcript
cp transcript_mixed_ar_en_terms.md "$FOLDER_NAME/transcript_mixed_ar_en_terms.md"

# 4. Type-2 pure English audio
cp vo_pure_en.m4a "$FOLDER_NAME/vo_pure_en.m4a"
cp vo_pure_en.wav "$FOLDER_NAME/vo_pure_en.wav" 2>/dev/null

# 5. Type-2 pure English transcript
cp transcript_pure_en.md "$FOLDER_NAME/transcript_pure_en.md"

# 6. USAGE_TIPS.md (fifth file — generated below)
# Generated in the next step

echo "Folder created: $FOLDER_NAME"
ls -lh "$FOLDER_NAME/"
```

### Generate USAGE_TIPS.md (the fifth file)

**Agent: generate this file automatically. It must be specific to the actual video — not generic.**

```bash
cat > "$FOLDER_NAME/USAGE_TIPS.md" << 'TIPS_EOF'
# Usage Tips — How to Use This Voice-Over Package

## What's in this folder

| File | Type | Purpose |
|:---|:---|:---|
| `{video}.mp4` | Video | Source video (screen recording / app demo) |
| `vo_mixed_ar_en_terms.m4a` | Audio | Type-1 voice-over: Arabic narration with English app terms |
| `transcript_mixed_ar_en_terms.md` | Transcript | Type-1 script with timestamps + SEO keywords |
| `vo_pure_en.m4a` | Audio | Type-2 voice-over: Pure English narration |
| `transcript_pure_en.md` | Transcript | Type-2 script with timestamps + SEO keywords |
| `USAGE_TIPS.md` | Guide | This file |

## When to use which audio type

### Type-1 (Mixed Arabic + English terms)
**Best for:** Arabic-speaking audience, MENA markets, Arab social media, local medical professionals
- **TikTok / Instagram Reels (Arabic):** Use Type-1 audio for maximum engagement with Arabic audience
- **YouTube (Arabic channel):** Mux Type-1 audio; use transcript for Arabic captions + chapters
- **WhatsApp / Telegram shares:** Arabic VO is more personal and shareable in Arab communities

### Type-2 (Pure English)
**Best for:** Global audience, English-speaking markets, international app stores, LinkedIn
- **YouTube (English channel):** Mux Type-2 audio; use transcript for English captions + chapters
- **App Store / Play Store demo video:** Pure English for global reach
- **LinkedIn / Twitter/X:** Professional English narration for B2B content
- **Website demo:** Embed with English VO for landing pages

## How to use for different platforms

### Social Reels / Shorts (< 60s)
1. Pick the audio type matching your target audience
2. Mux: `ffmpeg -i video.mp4 -i vo_{type}.m4a -c:v copy -c:a aac -ar 48000 -shortest -y output.mp4`
3. Burn captions from transcript (85% of social views are muted)
4. Export at platform-correct resolution (9:16 for TikTok/Reels, 1:1 for feed)

### YouTube / Long-form
1. Use transcript timestamps as YouTube chapter markers in description
2. Upload the SRT as a caption track (auto-translatable)
3. Add transcript keywords to video description for SEO

### Re-edit / Caption Burn-in
1. Open transcript file — each line has a timestamp tied to the UI screen
2. Map transcript lines to your editing timeline
3. Burn SRT using ffmpeg subtitles filter (see Template commands)
4. Adjust MarginV for your target platform safe zone

### Multi-language Publishing
| Audience | Audio | Captions | Description |
|:---|:---|:---|:---|
| Arabic (MENA) | Type-1 mixed | Arabic+EN terms SRT | Arabic with EN feature names |
| English (Global) | Type-2 pure EN | English SRT | Full English |
| Bilingual | Type-1 audio | English SRT overlay | Arabic VO + English text |

### Accessibility / SEO
- Both transcripts include SEO keywords from actual UI features
- Use transcript text as video description on YouTube/Instagram
- Alt-text for thumbnails: extract the hook line from transcript
- Schema markup: use transcript for VideoObject `transcript` property

### Agent Re-use
- Feed transcript files back into Apex AI-Editor for caption burn-in
- Use as input for Hybrid 4.7 publish pipeline (description + hashtags from keywords)
- Type-2 transcript → auto-generate video descriptions for multi-platform posting
- Timeline data → auto-generate chapter markers for YouTube

TIPS_EOF

# Replace placeholder with actual video filename
VIDEO_BASENAME=$(basename "$VIDEO")
sed -i "s/{video}/${VIDEO_BASENAME}/g" "$FOLDER_NAME/USAGE_TIPS.md" 2>/dev/null || \
  sed -i '' "s/{video}/${VIDEO_BASENAME}/g" "$FOLDER_NAME/USAGE_TIPS.md"

echo "USAGE_TIPS.md generated"
```

### Transcript file format (what the .md files look like)

```markdown
# Voice-Over Transcript — Type-2 Pure English
# Video: dose_calculator_demo.mp4
# Duration: 22.5s
# Generated: 2026-08-05

## Script with Timeline

| Start | End | Spoken Text | On-Screen UI |
|:---|:---|:---|:---|
| 00:00 | 00:02 | This app helps you calculate the right dosage. | Splash screen, logo |
| 00:02 | 00:05 | Let's enter the patient details — name, age, and weight. | Input form, empty fields |
| 00:05 | 00:08 | The form is now filled with the patient information. | Fields: Ahmed, 32, 85kg |
| 00:08 | 00:10 | Tap Calculate to get the result. | Calculate button highlighted |
| 00:10 | 00:14 | The recommended dose is 250 milligrams, twice daily. | Results: 250mg, 2x/day |
| 00:14 | 00:18 | Scroll down for the detailed breakdown and chart. | Chart + breakdown table |
| 00:18 | 00:22 | Share the results or save them as a PDF for your records. | Share/Save/Copy buttons |

## SEO Keywords (from UI)
dose calculator, patient dosage, medical app, calculate dose, drug dosage,
BMI calculator, clinical tool, healthcare app, save PDF, share results,
dosage recommendation, twice daily, milligrams

## Platform Hooks
- **TikTok/Reels hook:** "Calculate any drug dose in 3 seconds 💊"
- **YouTube description opener:** "How to calculate patient dosage with [App Name] — step-by-step demo"
- **LinkedIn professional:** "Streamlining clinical dosage calculations with AI-powered tools"
```

---

## PHASE 7: QA — Verify Everything

### Audio quality check

```bash
for VO in "$FOLDER_NAME"/vo_*.m4a; do
  echo "=== $(basename "$VO") ==="
  
  # 1. File exists, size > 0
  SIZE=$(stat -f%z "$VO" 2>/dev/null || stat -c%s "$VO" 2>/dev/null)
  [ "$SIZE" -gt 5000 ] && echo "  File size: PASS ($SIZE bytes)" || echo "  File size: FAIL ($SIZE bytes)"
  
  # 2. Duration > 0, reasonable vs video
  VO_DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VO")
  echo "  Duration: ${VO_DUR}s (video: ${DUR}s)"
  
  # 3. Sample rate = 48000
  SR=$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$VO")
  [ "$SR" = "48000" ] && echo "  Sample rate: PASS (48000)" || echo "  Sample rate: FAIL ($SR)"
  
  # 4. Loudness check
  LOUD=$(ffmpeg -i "$VO" -af "loudnorm=I=-14:LRA=11:TP=-1:print_format=json" -f null - 2>&1 | grep input_i)
  echo "  Loudness: $LOUD"
  
  # 5. Reverse-QA: transcribe the audio back and compare to script
  # (only if whisper is installed)
  if command -v whisper &>/dev/null; then
    echo "  Running reverse transcription QA..."
    whisper "$VO" --model tiny --output_format txt --output_dir /tmp/whisper_qa/ 2>/dev/null
    echo "  Reverse transcript saved for comparison"
  fi
done
```

### Folder completeness check

```bash
echo "=== FOLDER COMPLETENESS ==="
EXPECTED_FILES=6  # video + 2 audio + 2 transcript + tips

ACTUAL=$(ls "$FOLDER_NAME/" | wc -l)
echo "Files in folder: $ACTUAL (expected: $EXPECTED_FILES minimum)"

# Check each required file type
ls "$FOLDER_NAME/"*.mp4 >/dev/null 2>&1 && echo "  Video: PASS" || echo "  Video: FAIL"
ls "$FOLDER_NAME/vo_mixed"* >/dev/null 2>&1 && echo "  Type-1 audio: PASS" || echo "  Type-1 audio: FAIL"
ls "$FOLDER_NAME/transcript_mixed"* >/dev/null 2>&1 && echo "  Type-1 transcript: PASS" || echo "  Type-1 transcript: FAIL"
ls "$FOLDER_NAME/vo_pure"* >/dev/null 2>&1 && echo "  Type-2 audio: PASS" || echo "  Type-2 audio: FAIL"
ls "$FOLDER_NAME/transcript_pure"* >/dev/null 2>&1 && echo "  Type-2 transcript: PASS" || echo "  Type-2 transcript: FAIL"
ls "$FOLDER_NAME/USAGE_TIPS"* >/dev/null 2>&1 && echo "  USAGE_TIPS: PASS" || echo "  USAGE_TIPS: FAIL"

echo ""
echo "Three-state verdict:"
echo "  PASS = all 6 files present, audio quality OK, durations match, sample rate 48kHz"
echo "  FAIL = missing files, broken audio, duration mismatch"
echo "  INCONCLUSIVE = could not verify (e.g., whisper not installed for reverse QA)"
```

### A/V sync verification (after muxing)

```bash
# If the VO was muxed into the video, verify sync
if [ -f "video_with_vo_type2.mp4" ]; then
  V_DUR=$(ffprobe -v quiet -show_entries stream=duration -of csv=p=0 -select_streams v:0 "video_with_vo_type2.mp4")
  A_DUR=$(ffprobe -v quiet -show_entries stream=duration -of csv=p=0 -select_streams a:0 "video_with_vo_type2.mp4")
  DRIFT=$(echo "$V_DUR - $A_DUR" | bc -l)
  echo "A/V drift: ${DRIFT}s"
  DRIFT_OK=$(echo "${DRIFT#-} < 0.5" | bc -l)
  [ "$DRIFT_OK" = "1" ] && echo "A/V sync: PASS" || echo "A/V sync: FAIL (drift > 0.5s)"
fi
```

---

## ANTI-HALLUCINATION RULES — Voice-Over Specific

**These rules are in addition to the global anti-hallucination rules (Section E). Breaking any = FAIL.**

1. **Never invent UI text that is not on screen.** If you cannot read a button label from the frame, say `[unclear UI element]` in the script — do NOT guess.
2. **Never invent numbers, doses, or medical values.** If the app shows "250mg", say "250 milligrams". Do NOT say "200mg" because it "sounds rounder".
3. **Never assume screen order.** The app may show results before input (e.g., editing a saved calculation). Describe what IS on screen at each timestamp.
4. **Never fabricate timestamps.** Every timestamp in the transcript must correspond to an actual frame you extracted in Phase 1.
5. **Never generate VO longer than the video** without explicit user permission. If the script is too long, shorten the text — do NOT speed up the audio beyond 1.10×.
6. **Never mix up Type-1 and Type-2.** Type-1 = Arabic dominant with English terms. Type-2 = pure English. Swapping languages between types is a critical error.
7. **Never use a TTS voice that sounds robotic.** If the TTS output sounds clipped, glitchy, or interrupted, regenerate with a different voice or adjust prosody settings. Never ship robotic audio.
8. **Never skip the reverse-QA step.** If Whisper is available, always transcribe the generated audio back and compare to the original script. Mismatches indicate TTS pronunciation failures.

---

## LINUX vs macOS — Template 8 Specific

| Step | macOS | Linux |
|:---|:---|:---|
| edge-tts install | `pip3 install edge-tts` | `pip install edge-tts` |
| Whisper install | `pip3 install openai-whisper` | `pip install openai-whisper` |
| Font for captions | Arial (built-in) | DejaVu Sans (install `fonts-dejavu-core`) |
| Arabic font support | Built-in Core Text + Arabic fonts | Install `fonts-noto-cjk` or `fonts-arabeyes` for full Arabic glyph coverage |
| SRT Arabic rendering | ffmpeg handles RTL via ICU (usually built-in) | May need `--enable-libfribidi` in ffmpeg build for RTL |
| `stat` syntax | `stat -f%z` | `stat -c%s` |
| `sed -i` syntax | `sed -i ''` (BSD sed) | `sed -i` (GNU sed) |
| Python path | `python3` / `pip3` | `python3` / `pip` |
| Temp dir for whisper QA | `/tmp/whisper_qa/` | `/tmp/whisper_qa/` |

### Arabic font check (Linux)

```bash
if [ "$OS_TYPE" = "Linux" ]; then
  # Check if Arabic glyphs are available
  fc-list :lang=ar | head -5
  if [ $? -ne 0 ] || [ -z "$(fc-list :lang=ar)" ]; then
    echo "WARN: No Arabic fonts found. Installing..."
    apt install -y fonts-noto fonts-noto-cjk fonts-arabeyes 2>/dev/null
    fc-cache -fv
  fi
  echo "Arabic fonts available:"
  fc-list :lang=ar --format="%{family}\n" | sort -u
fi
```

---

## COMPLETE EXAMPLE: App Demo Video → Full VO Package

```
Input: dose_calculator_demo.mp4 (22s, 1080×1920, 9:16)

Phase 1: PERCEIVE
  - Probe: 22s, 1080×1920, 30fps, no audio
  - Extract 23 frames (1 per second)
  - Build UI timeline map: 7 beats (splash → input → fill → tap → result → detail → CTA)

Phase 2: SCRIPT
  - Type-1 mixed (AR+EN terms):
    00:00-02:00  "هذا التطبيق يساعدك تحسب الجرعة المناسبة"
    02:00-05:00  "دلوقتي هندخل البيانات — الاسم والعمر والوزن"
    06:00-09:00  "بعد ما نضغط Calculate النتيجة بتظهر"
    09:50-13:00  "dose 250mg مرتين في اليوم"
    14:00-18:00  "تقدر تعمل Share أو Save PDF من هنا"
    18:00-22:00  "حمل التطبيق دلوقتي — link في البايو"
  
  - Type-2 pure EN:
    00:00-02:00  "This app helps you calculate the right dosage."
    02:00-05:00  "Let's enter the patient details — name, age, and weight."
    06:00-09:00  "Tap Calculate to get the result."
    09:50-13:00  "The recommended dose is 250 milligrams, twice daily."
    14:00-18:00  "Share the results or save them as a PDF."
    18:00-22:00  "Download the app now — link in bio."

Phase 3: GENERATE
  - Type-1: edge-tts --voice ar-SA-HamedNeural
  - Type-2: edge-tts --voice en-US-GuyNeural
  - Post-process: loudnorm -14 LUFS, trim silence, compress, -ar 48000

Phase 4: SEGMENT
  - Both VOs ~21s (video is 22s) — within 10% tolerance
  - Pad with silence: apad=whole_dur=22

Phase 5: MUX + CAPTION
  - Mux Type-1 and Type-2 into separate video copies
  - Generate SRT from both transcripts
  - Burn captions into video (MarginV=60 for 9:16)

Phase 6: PACKAGE
  Folder: vo_dose_calculator_demo_20260805-0256/
    dose_calculator_demo.mp4
    vo_mixed_ar_en_terms.m4a
    transcript_mixed_ar_en_terms.md
    vo_pure_en.m4a
    transcript_pure_en.md
    USAGE_TIPS.md

Phase 7: QA
  - All 6 files present: PASS
  - Audio durations match video: PASS
  - Sample rates 48kHz: PASS
  - Loudness -14 LUFS: PASS
  - A/V sync < 0.5s: PASS
  - Reverse Whisper QA matches script: PASS
  VERDICT: PASS → done
```

---

## CMD — Copy-Ready One-Liner Reference

```bash
# === FULL PIPELINE (copy-paste, fill in VIDEO path) ===

VIDEO="path/to/your_video.mp4"
SLUG=$(basename "$VIDEO" | sed 's/\.[^.]*$//' | tr ' ' '_' | tr '[:upper:]' '[:lower:]')
OUT="vo_${SLUG}_$(date +%Y%m%d-%H%M)"

# 1. Probe
ffprobe -v quiet -print_format json -show_format -show_streams "$VIDEO"

# 2. Extract frames for UI reading
mkdir -p "${OUT}/frames"
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VIDEO")
for s in $(seq 0 $(echo "$DUR" | cut -d. -f1)); do
  ffmpeg -ss "$s" -i "$VIDEO" -frames:v 1 -y "${OUT}/frames/frame_${s}s.png" 2>/dev/null
done

# 3. [AGENT: read frames, write scripts, save as script_type1.txt and script_type2.txt]

# 4. Generate audio
pip install edge-tts 2>/dev/null
edge-tts --voice "ar-SA-HamedNeural" --rate "+5%" --file script_type1.txt \
  --write-media "${OUT}/vo_mixed_ar_en_terms_raw.mp3"
edge-tts --voice "en-US-GuyNeural" --file script_type2.txt \
  --write-media "${OUT}/vo_pure_en_raw.mp3"

# 5. Post-process audio
for raw in "${OUT}"/vo_*_raw.mp3; do
  CLEAN="${raw/_raw.mp3/.m4a}"
  ffmpeg -i "$raw" \
    -af "silenceremove=start_periods=1:start_silence=0.1:start_threshold=-40dB,areverse,silenceremove=start_periods=1:start_silence=0.1:start_threshold=-40dB,areverse,acompressor=threshold=-20dB:ratio=3:attack=5:release=100,loudnorm=I=-14:LRA=11:TP=-1" \
    -c:a aac -b:a 192k -ar 48000 -y "$CLEAN"
done

# 6. Copy video into folder
cp "$VIDEO" "${OUT}/"

# 7. [AGENT: generate transcript_mixed_ar_en_terms.md, transcript_pure_en.md, USAGE_TIPS.md]

# 8. QA
echo "=== FINAL CHECK ==="
ls -lh "${OUT}/"
for f in "${OUT}"/vo_*.m4a; do
  echo "$(basename $f): $(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$f")s, \
$(ffprobe -v quiet -show_entries stream=sample_rate -of csv=p=0 -select_streams a:0 "$f")Hz"
done
```

---

## PRO-TIPS: Voice-Over + SEO Captions (Template 8 Specific)

1. **edge-tts is the sweet spot.** It's free, neural-quality, supports Arabic + English, and runs without GPU. If the user has an OpenAI API key, use `openai.audio.speech` for even higher quality — but edge-tts is the default.
2. **Arabic VO: use a single Arabic voice for the mixed script.** Don't switch between an Arabic voice and an English voice mid-sentence — the Arabic neural voices handle short English terms (Calculate, Save, PDF) naturally. Switching voices creates jarring audio cuts.
3. **Segment-by-segment generation beats single-take for long videos.** For videos over 30s, generate each segment separately with controlled pauses between them. Single-take TTS tends to rush through long scripts.
4. **SSML is your pacing control.** Use `<break time="400ms"/>` for natural pauses between sentences. Use `<prosody rate="-5%">` to slow down important numbers (doses, results). This prevents the "robot reading a list" feel.
5. **Always post-process TTS audio.** Raw TTS has inconsistent volume, leading silence, and sometimes trailing artifacts. The 3-step pipeline (silence trim → compression → loudnorm) fixes all of it.
6. **Never speed up audio beyond 1.10× to fit the video.** If the VO is too long, shorten the script text — don't time-stretch. Listeners notice speed changes above 10%.
7. **Reverse-QA with Whisper catches pronunciation failures.** TTS sometimes garbles medical terms, Arabic words, or numbers. Transcribing the output back and comparing to the script catches these before the user hears them.
8. **Captions must stay inside safe zones.** On 9:16 vertical video, MarginV=60 keeps text above TikTok/Reels bottom UI. MarginV=250 clears all platform overlays (more conservative).
9. **Two SRT files, not one bilingual SRT.** Generate separate SRT files for Type-1 (AR+EN) and Type-2 (EN). Mixing languages in one SRT causes confusion when importing into editors.
10. **SEO keywords come from the actual UI — never from your imagination.** If the app shows "Dose Calculator", use "dose calculator" in the SEO keywords. If it shows "حاسبة الجرعات", use that. Do not invent feature names.
11. **The USAGE_TIPS.md file is NOT generic.** It must reference the actual video filename, the actual features shown, and the actual transcript content. Generic tips = hallucination.
12. **Linux: check Arabic font availability BEFORE generating captions.** Missing Arabic glyphs = empty rectangles in the burned caption. Run `fc-list :lang=ar` first.
13. **Sample rate must be 48000Hz for video mux.** TTS engines often output at 22050 or 24000. Always re-encode to 48000 before muxing or the A/V container will have sync issues.
14. **Type-1 mixed script: Arabic DOMINATES.** English terms appear only for app-specific UI labels that are in English on screen. If a button says "احسب" (Calculate in Arabic), say "احسب" — not "Calculate". Follow the actual UI language.
15. **Deliver WAV alongside M4A when possible.** M4A is for final use (smaller, better quality). WAV is for editors who need lossless re-import into DaVinci/Premiere/CapCut.

---

*Template 8 produces a complete voice-over package for any video: two audio types (Type-1 mixed AR+EN terms, Type-2 pure English), two timeline-aligned transcripts with SEO content, and a USAGE_TIPS guide — all in one uniquely named folder. The agent follows the 7-phase pipeline (Perceive → Script → Generate → Segment → Mux+Caption → Package → QA), uses free TTS tools, and verifies every output before delivery.*
TEMPLATE 9

Template 9 (U9) — AI-Visualization & Content Intelligence Master Package

34 Executable Code Blocks

Verbatim executive directives and code for Template 9 (Visual perception, frame analysis, scene classification, content_intelligence.json) directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 34 Code Blocks
Paste-ready package · 1134 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
==================================


////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\////////////////////////\\\\\\\\\\\\////
Template-9: AI-Visualization and Understanding
-----------------------------------------------

**Agent directive: The user wants you to deeply understand a video's content — frame by frame, scene by scene — so that you can produce accurate transcripts, suitable voice-overs, proper captions, intelligent scene splits, and meaningful content categories. This template turns you from a "blind editor" into a "seeing editor" — an agent that KNOWS what is in the video before making any editing decision. Use this template before U3/U5/U8 for maximum output quality. Every tool, prompt, and method here is free-first and locally runnable.**

---

## THE METHOD — How an AI Agent Sees and Understands Video Content

```
PHASE 1: DEEP PERCEIVE    → Probe metadata + extract dense keyframes + detect scenes automatically.
PHASE 2: VISUAL READ      → Analyze each keyframe: what is on screen, what UI text, what action.
PHASE 3: AUDIO UNDERSTAND → Transcribe speech, detect music/silence, map audio events to timeline.
PHASE 4: SCENE SPLIT      → Segment the video into semantically meaningful scenes with boundaries.
PHASE 5: CATEGORIZE       → Label each scene (tutorial, result-reveal, CTA, transition, etc.).
PHASE 6: INTELLIGENCE MAP → Produce a structured content-intelligence JSON: frames + scenes + transcript + categories.
PHASE 7: DOWNSTREAM FEED  → Feed the intelligence map into U3 (enhance), U5 (viral), U8 (voice-over), or manual editing.
PHASE 8: QA               → Verify completeness: every second accounted for, no hallucinated content.
```

**Why this order:**
- You cannot analyze frames until you know the video's duration, FPS, and resolution (Phase 1).
- You cannot categorize scenes until you have read what is in them (Phase 2→3→4→5).
- You cannot generate accurate voice-over or captions without the intelligence map (Phase 6→7).
- You cannot trust any output without verification (Phase 8).

**Key insight: the agent's native vision (looking at extracted frames) is the primary tool. Scripts are for numeric measurement only. Never write Python just to describe what an image shows — YOU can see it.**

---

## TOOL STACK — Free, Local, No API Keys Required

| Tool | Purpose | Install (macOS) | Install (Linux) | Cost |
|:---|:---|:---|:---|:---|
| **ffmpeg / ffprobe** | Frame extraction, scene detection, metadata, silence detection | `brew install ffmpeg` | `apt install ffmpeg` | Free |
| **Whisper** | Speech-to-text with word-level timestamps | `pip install openai-whisper` | `pip install openai-whisper` | Free |
| **OpenCV (cv2)** | Frame differencing, histogram analysis, OCR prep | `pip install opencv-python` | `pip install opencv-python` | Free |
| **Tesseract OCR** | Read text from UI screenshots when agent vision is uncertain | `brew install tesseract` | `apt install tesseract-ocr` | Free |
| **pytesseract** | Python wrapper for Tesseract | `pip install pytesseract` | `pip install pytesseract` | Free |
| **pyscenedetect** | Automatic scene boundary detection | `pip install scenedetect[opencv]` | `pip install scenedetect[opencv]` | Free |
| **jq** | JSON processing for intelligence maps | `brew install jq` | `apt install jq` | Free |
| **Agent native vision** | Looking at extracted frames directly (primary analysis tool) | Built-in | Built-in | Free |

### Install all tools (one-shot)

```bash
OS_TYPE="$(uname -s)"

pip install openai-whisper opencv-python pytesseract scenedetect[opencv] pysubs2 2>/dev/null

if [ "$OS_TYPE" = "Linux" ]; then
  apt install -y ffmpeg bc jq tesseract-ocr fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv
elif [ "$OS_TYPE" = "Darwin" ]; then
  brew install ffmpeg jq tesseract 2>/dev/null
fi

echo "All AI-visualization tools installed"
```

---

## PHASE 1: DEEP PERCEIVE — Dense Keyframe Extraction + Scene Detection

**Agent: standard probing (Template 1–8 style) gives you duration and resolution. Deep perceive gives you the video's visual DNA — one representative frame per visual change.**

### Step 1a: Standard probe (always first)

```bash
VIDEO="$1"
[ -f "$VIDEO" ] || { echo "FAIL: video not found"; exit 1; }

DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VIDEO")
RES=$(ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 -select_streams v:0 "$VIDEO")
FPS=$(ffprobe -v quiet -show_entries stream=r_frame_rate -of csv=p=0 -select_streams v:0 "$VIDEO")
HAS_AUDIO=$(ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$VIDEO" | grep -c audio)
CODEC=$(ffprobe -v quiet -show_entries stream=codec_name -of csv=p=0 -select_streams v:0 "$VIDEO")
COLOR_SPACE=$(ffprobe -v quiet -show_entries stream=color_transfer -of csv=p=0 -select_streams v:0 "$VIDEO")

echo "=== VIDEO DNA ==="
echo "Duration: ${DUR}s | Resolution: $RES | FPS: $FPS"
echo "Audio: $HAS_AUDIO stream(s) | Codec: $CODEC | Color: $COLOR_SPACE"
```

### Step 1b: Dense keyframe extraction (1 frame per second + scene changes)

```bash
mkdir -p keyframes

# Method 1: Regular interval extraction (1 per second — baseline)
TOTAL_SECS=$(echo "$DUR" | cut -d. -f1)
for s in $(seq 0 "$TOTAL_SECS"); do
  ffmpeg -ss "$s" -i "$VIDEO" -frames:v 1 -y "keyframes/sec_$(printf '%04d' $s).png" 2>/dev/null
done
echo "Extracted $((TOTAL_SECS + 1)) per-second frames"

# Method 2: Scene-change detection frames (only frames where the visual changes significantly)
# ponytail: scene threshold 0.3 = moderate sensitivity; lower = more frames, higher = fewer
ffmpeg -i "$VIDEO" \
  -vf "select='gt(scene,0.3)',showinfo" \
  -vsync vfr -frame_pts 1 \
  "keyframes/scene_%04d.png" 2>&1 | grep "showinfo" | \
  awk -F'pts_time:' '{print $2}' | awk '{print $1}' > keyframes/scene_timestamps.txt

echo "Scene-change frames: $(wc -l < keyframes/scene_timestamps.txt)"
```

### Step 1c: Adaptive keyframe density (smart — more frames during action, fewer during static)

```bash
# ponytail: this extracts MORE frames when visual content changes rapidly
# and FEWER when the screen is mostly static. Best balance of coverage vs. volume.

python3 -c "
import subprocess, json, os

video = '$VIDEO'
dur = float('$DUR')

# Get scene change timestamps
cmd = ['ffprobe', '-v', 'quiet', '-show_frames', '-select_streams', 'v:0',
       '-show_entries', 'frame=pts_time,pict_type', '-of', 'json', video]
# ponytail: full frame analysis is expensive for long videos — cap at 120s
# For longer videos, use Method 1 (1fps) + Method 2 (scene-change) instead
if dur > 120:
    print('Video > 120s — use per-second + scene-change extraction instead')
else:
    # Extract I-frames only (keyframes) — much faster than all frames
    os.system(f'ffmpeg -skip_frame nokey -i \"{video}\" -vsync vfr -frame_pts 1 keyframes/iframe_%04d.png 2>/dev/null')
    iframe_count = len([f for f in os.listdir('keyframes') if f.startswith('iframe_')])
    print(f'I-frames extracted: {iframe_count}')
"
```

### Step 1d: Automated scene boundary detection with PySceneDetect

```bash
# ponytail: pyscenedetect is the gold standard for scene splitting — free, accurate, fast
# It detects both hard cuts and gradual transitions (fades, dissolves)

python3 -c "
from scenedetect import detect, ContentDetector, AdaptiveDetector
from scenedetect import open_video

video = open_video('$VIDEO')

# ContentDetector: best for hard cuts + obvious transitions
# threshold=27 is a good default; lower = more scenes detected
scenes = detect('$VIDEO', ContentDetector(threshold=27))

print(f'Detected {len(scenes)} scenes:')
for i, scene in enumerate(scenes):
    start = scene[0].get_seconds()
    end = scene[1].get_seconds()
    duration = end - start
    print(f'  Scene {i+1}: {start:.2f}s → {end:.2f}s ({duration:.1f}s)')
" 2>/dev/null || echo "pyscenedetect not installed — falling back to ffmpeg scene detection"
```

### Step 1e: Extract representative frame per detected scene

```bash
# After scene detection, extract one frame from the middle of each scene
# This gives you the "best representative" of what each scene contains

python3 -c "
from scenedetect import detect, ContentDetector, open_video
import subprocess, os

os.makedirs('keyframes/scenes', exist_ok=True)
scenes = detect('$VIDEO', ContentDetector(threshold=27))

for i, scene in enumerate(scenes):
    mid = (scene[0].get_seconds() + scene[1].get_seconds()) / 2
    out = f'keyframes/scenes/scene_{i+1:03d}_at_{mid:.1f}s.png'
    subprocess.run([
        'ffmpeg', '-ss', str(mid), '-i', '$VIDEO',
        '-frames:v', '1', '-y', out
    ], capture_output=True)
    print(f'Scene {i+1}: frame at {mid:.1f}s → {out}')

print(f'Total scene frames: {len(scenes)}')
" 2>/dev/null
```

---

## PHASE 2: VISUAL READ — Analyze What Is On Every Frame

**Agent: this is where YOUR native vision is the primary tool. Look at the extracted frames. For each one, describe what you see — screen content, UI elements, text, numbers, actions, transitions.**

### Agent prompt for visual analysis (use this system prompt when analyzing frames)

```
SYSTEM PROMPT — Video Frame Analysis Mode

You are analyzing extracted video frames to build a content-intelligence map.
For each frame, report EXACTLY what you see. Follow these rules:

1. DESCRIBE what is visible — screen name, buttons, labels, numbers, charts,
   images, people, text overlays, colors, layout.
2. READ all on-screen text character by character. If text is unclear, write [unclear].
   NEVER guess or invent text content.
3. IDENTIFY the UI state — is this a form, a result screen, a menu, a loading state,
   an animation mid-frame, a transition between screens?
4. NOTE what changed from the previous frame — new screen, new value, scroll position,
   tap/click highlight, animation progress, transition type (cut, fade, slide).
5. RATE the frame's editorial value:
   - HOOK CANDIDATE: visually striking, result reveal, "aha" moment
   - CONTENT: standard informational frame, good for narration
   - TRANSITION: mid-transition, not suitable as a standalone frame
   - DEAD: blank, loading spinner, duplicate of adjacent frame
6. FLAG any issues:
   - Face visible (if faceless brand policy applies)
   - Watermark or unwanted overlay
   - Blurry text that needs re-capture
   - Sensitive data (personal info, real patient data) that must be redacted
```

### Automated OCR backup (when agent vision can't read small text)

```bash
# ponytail: Tesseract OCR as a fallback for tiny UI text the agent can't resolve
# Only use when agent says [unclear] — native vision is always preferred

for frame in keyframes/scenes/scene_*.png; do
  echo "=== $(basename "$frame") ==="
  tesseract "$frame" stdout --psm 6 2>/dev/null | head -20
  echo "---"
done > keyframes/ocr_results.txt

echo "OCR results saved to keyframes/ocr_results.txt"
```

### Automated frame metrics (numeric measurements only — not descriptions)

```python
#!/usr/bin/env python3
"""
frame_metrics.py — Extract numeric visual metrics from keyframes.
Agent uses native vision for descriptions; this script provides NUMBERS only.
ponytail: single file, stdlib + cv2, no frameworks
"""
import cv2, os, json, sys

def analyze_frame(path):
    """Return numeric metrics for a single frame. Never describes content."""
    img = cv2.imread(path)
    if img is None:
        return {"error": f"could not read {path}"}

    h, w = img.shape[:2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Brightness (mean luminance 0-255)
    brightness = float(gray.mean())

    # Contrast (std dev of luminance)
    contrast = float(gray.std())

    # Sharpness (Laplacian variance — higher = sharper)
    sharpness = float(cv2.Laplacian(gray, cv2.CV_64F).var())

    # Dominant color (mode of the BGR histogram)
    # ponytail: approximate — exact mode needs large histogram, this is fast
    avg_color = img.mean(axis=(0, 1)).tolist()  # [B, G, R]

    # Edge density (% of pixels that are edges — indicates text/UI density)
    edges = cv2.Canny(gray, 50, 150)
    edge_density = float(edges.mean() / 255 * 100)

    # Face detection (Haar cascade — fast, free, good enough for presence check)
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
    )
    faces = face_cascade.detectMultiScale(gray, 1.1, 4, minSize=(30, 30))

    return {
        "file": os.path.basename(path),
        "resolution": f"{w}x{h}",
        "brightness": round(brightness, 1),
        "contrast": round(contrast, 1),
        "sharpness": round(sharpness, 1),
        "avg_color_bgr": [round(c, 1) for c in avg_color],
        "edge_density_pct": round(edge_density, 1),
        "faces_detected": len(faces),
        "face_regions": [{"x": int(x), "y": int(y), "w": int(fw), "h": int(fh)}
                         for (x, y, fw, fh) in faces],
    }

if __name__ == "__main__":
    frame_dir = sys.argv[1] if len(sys.argv) > 1 else "keyframes/scenes"
    results = []
    for f in sorted(os.listdir(frame_dir)):
        if f.lower().endswith((".png", ".jpg", ".jpeg")):
            path = os.path.join(frame_dir, f)
            metrics = analyze_frame(path)
            results.append(metrics)
            # Print summary line for quick review
            print(f"{metrics.get('file', f)}: "
                  f"bright={metrics.get('brightness', '?')} "
                  f"contrast={metrics.get('contrast', '?')} "
                  f"sharp={metrics.get('sharpness', '?')} "
                  f"edges={metrics.get('edge_density_pct', '?')}% "
                  f"faces={metrics.get('faces_detected', '?')}")

    # Save full results as JSON
    output_path = os.path.join(frame_dir, "frame_metrics.json")
    with open(output_path, "w") as fout:
        json.dump(results, fout, indent=2)
    print(f"\nFull metrics: {output_path}")
```

```bash
# Run it
python3 frame_metrics.py keyframes/scenes
```

### Visual similarity detection (find duplicate/near-duplicate frames)

```python
#!/usr/bin/env python3
"""
dedupe_frames.py — Flag near-duplicate frames (static segments where nothing changes).
ponytail: uses perceptual hash (average hash) — O(n²) but n is small (< 300 frames typically).
Upgrade path: use VP-tree or FAISS for videos with thousands of frames.
"""
import cv2, os, sys

def avg_hash(img, size=16):
    """Compute average hash of an image — 64-bit perceptual fingerprint."""
    resized = cv2.resize(img, (size, size), interpolation=cv2.INTER_AREA)
    gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
    mean = gray.mean()
    return sum(1 << i for i, px in enumerate(gray.flatten()) if px > mean)

def hamming(h1, h2):
    """Hamming distance between two hashes."""
    return bin(h1 ^ h2).count('1')

if __name__ == "__main__":
    frame_dir = sys.argv[1] if len(sys.argv) > 1 else "keyframes"
    threshold = int(sys.argv[2]) if len(sys.argv) > 2 else 5  # lower = stricter

    frames = sorted([f for f in os.listdir(frame_dir)
                     if f.lower().endswith((".png", ".jpg"))])

    hashes = {}
    for f in frames:
        img = cv2.imread(os.path.join(frame_dir, f))
        if img is not None:
            hashes[f] = avg_hash(img)

    dupes = []
    frame_list = list(hashes.keys())
    for i in range(len(frame_list) - 1):
        dist = hamming(hashes[frame_list[i]], hashes[frame_list[i + 1]])
        if dist <= threshold:
            dupes.append((frame_list[i], frame_list[i + 1], dist))

    if dupes:
        print(f"Near-duplicate pairs (hamming ≤ {threshold}):")
        for a, b, d in dupes:
            print(f"  {a} ↔ {b} (distance={d})")
        print(f"\n{len(dupes)} duplicate pairs found — these segments are visually static.")
        print("Tip: static segments can be shortened or speed-ramped in editing.")
    else:
        print("No near-duplicate consecutive frames — every frame is visually distinct.")
```

```bash
python3 dedupe_frames.py keyframes 5
```

---

## PHASE 3: AUDIO UNDERSTAND — Transcribe, Detect Music, Map Silence

**Agent: video understanding is not just visual. Audio tells you WHERE narration exists, WHERE music plays, WHERE silence gaps are, and WHAT is being said.**

### Step 3a: Full transcription with word-level timestamps

```bash
# ponytail: word_timestamps=True is mandatory for accurate caption alignment
# Use --model small for technical/medical vocabulary; base for general content
whisper "$VIDEO" --model small --language en \
  --output_format json --word_timestamps True \
  --output_dir keyframes/

# For Arabic/mixed content:
whisper "$VIDEO" --model small --language ar \
  --output_format json --word_timestamps True \
  --output_dir keyframes/

# For auto-detection (when you don't know the language):
whisper "$VIDEO" --model small \
  --output_format json --word_timestamps True \
  --output_dir keyframes/
```

### Step 3b: Silence detection — map dead air

```bash
# Find all silence gaps — these are natural scene boundaries or dead moments
ffmpeg -i "$VIDEO" \
  -af "silencedetect=noise=-30dB:d=0.3" \
  -f null - 2>&1 | grep "silence_" > keyframes/silence_map.txt

echo "Silence segments found: $(grep -c 'silence_start' keyframes/silence_map.txt)"
cat keyframes/silence_map.txt
```

### Step 3c: Audio activity classification

```bash
# Detect whether each second has speech, music, silence, or noise
# ponytail: ffmpeg volumedetect + silence detection gives a rough audio activity map
# For precise speech/music separation, use demucs (free) — but that's heavy, skip unless needed

python3 -c "
import subprocess, re

# Parse silence map
silences = []
with open('keyframes/silence_map.txt') as f:
    starts = []
    for line in f:
        m_start = re.search(r'silence_start: ([\d.]+)', line)
        m_end = re.search(r'silence_end: ([\d.]+)', line)
        if m_start:
            starts.append(float(m_start.group(1)))
        if m_end and starts:
            silences.append((starts.pop(), float(m_end.group(1))))

dur = float('$DUR')
total_secs = int(dur)

# Build per-second activity map
print('Second | Activity')
print('-------|--------')
for s in range(total_secs + 1):
    is_silent = any(start <= s < end for start, end in silences)
    label = 'SILENCE' if is_silent else 'ACTIVE'
    print(f'  {s:4d}  | {label}')

silent_total = sum(end - start for start, end in silences)
print(f'\nTotal silence: {silent_total:.1f}s / {dur:.1f}s ({silent_total/dur*100:.0f}%)')
"
```

### Step 3d: Detect background music presence

```bash
# Quick heuristic: if the audio has consistent energy even during "speech gaps",
# there's likely background music present
ffmpeg -i "$VIDEO" \
  -af "astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.RMS_level" \
  -f null - 2>&1 | grep "RMS_level" | \
  awk -F= '{print $2}' > keyframes/rms_levels.txt

python3 -c "
levels = [float(l.strip()) for l in open('keyframes/rms_levels.txt') if l.strip()]
if not levels:
    print('No RMS data — video may have no audio')
else:
    avg = sum(levels) / len(levels)
    low_points = [l for l in levels if l < avg - 10]
    # If fewer than 20% of samples drop 10dB below average, music is likely present
    music_likely = len(low_points) < len(levels) * 0.2
    print(f'Avg RMS: {avg:.1f}dB')
    print(f'Low-energy samples: {len(low_points)}/{len(levels)} ({len(low_points)/len(levels)*100:.0f}%)')
    print(f'Background music likely: {\"YES\" if music_likely else \"NO\"}')" 2>/dev/null
```

---

## PHASE 4: SCENE SPLIT — Segment Video into Meaningful Scenes

**Agent: a "scene" is not just a visual cut. It's a semantically coherent unit — one topic, one action, one screen state. Two methods: automated (fast) and agent-guided (accurate).**

### Method A: Automated scene split with PySceneDetect (fast, good first pass)

```bash
# This gives you cuts at visual boundaries — hard cuts, fades, dissolves
python3 -c "
from scenedetect import detect, ContentDetector
import json

scenes = detect('$VIDEO', ContentDetector(threshold=27))
result = []
for i, scene in enumerate(scenes):
    result.append({
        'scene': i + 1,
        'start': round(scene[0].get_seconds(), 2),
        'end': round(scene[1].get_seconds(), 2),
        'duration': round(scene[1].get_seconds() - scene[0].get_seconds(), 2),
    })

with open('keyframes/scene_splits.json', 'w') as f:
    json.dump(result, f, indent=2)

print(f'Auto-detected {len(result)} scenes')
for s in result:
    print(f\"  Scene {s['scene']}: {s['start']:.2f}s → {s['end']:.2f}s ({s['duration']:.1f}s)\")
" 2>/dev/null
```

### Method B: Agent-guided semantic split (more accurate — agent reviews auto-splits)

```
AGENT PROMPT — Semantic Scene Splitting

You have:
1. Auto-detected scene boundaries (from PySceneDetect or ffmpeg scene filter)
2. Extracted keyframes for each scene
3. A Whisper transcript with word timestamps
4. A silence map

Your job: MERGE or SPLIT the auto-detected scenes to create SEMANTICALLY meaningful segments.

Rules:
- MERGE consecutive scenes that show the same logical action (e.g., scrolling through a list
  triggers multiple scene-change detections, but it's ONE action).
- SPLIT scenes that contain two distinct logical actions even if visually similar (e.g., the
  user fills field 1 then field 2 — same screen, different actions).
- Each final scene should answer: "What is the user doing/seeing in this segment?"
- Name each scene with a descriptive label: "input_form_fill", "result_reveal",
  "settings_menu_browse", "cta_end_card".
- Mark the editorial value: HOOK / CONTENT / TRANSITION / DEAD.
- Flag any scene that is too long (> 8s for short-form, > 30s for long-form) — it probably
  needs internal pattern interrupts or should be sub-divided.
```

### Method C: Split the video file into scene clips (for editing or re-assembly)

```bash
# Read scene_splits.json and cut the video into individual scene files
python3 -c "
import json, subprocess

with open('keyframes/scene_splits.json') as f:
    scenes = json.load(f)

import os
os.makedirs('keyframes/scene_clips', exist_ok=True)

for s in scenes:
    outfile = f\"keyframes/scene_clips/scene_{s['scene']:03d}_{s['start']:.1f}s-{s['end']:.1f}s.mp4\"
    subprocess.run([
        'ffmpeg', '-i', '$VIDEO',
        '-ss', str(s['start']), '-to', str(s['end']),
        '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy',
        '-y', outfile
    ], capture_output=True)
    # Verify
    size = os.path.getsize(outfile) if os.path.exists(outfile) else 0
    status = 'PASS' if size > 1000 else 'FAIL'
    print(f\"Scene {s['scene']}: {outfile} ({size} bytes) — {status}\")

print(f'Split into {len(scenes)} clips')
"
```

---

## PHASE 5: CATEGORIZE — Label Each Scene Semantically

**Agent: categories are how you (and downstream templates like U8 voice-over) know WHAT each segment is about and HOW to treat it.**

### Standard content categories

| Category | Description | VO treatment | Edit treatment |
|:---|:---|:---|:---|
| `hook` | First impression, scroll-stopper | Bold opening statement | Fastest pacing, zoom-in |
| `problem` | Pain point, "why this matters" | Problem framing, empathy | Moderate pace, context |
| `demo_input` | User entering data / interacting | Step-by-step narration | Normal pace, highlight fields |
| `demo_action` | Tap/click/submit moment | Call out the action | Quick zoom on button/element |
| `result_reveal` | Numbers, charts, output appear | Emphasis on values | Slow zoom, hold longer |
| `detail_scroll` | Scrolling through content/list | Summary narration | Speed ramp 1.3-1.5× |
| `settings` | App configuration, preferences | Brief mention or skip | Speed ramp or cut |
| `transition` | Screen transition, loading | No narration | Cut entirely or speed up |
| `cta` | Call to action, end card | CTA statement | Hold, add text overlay |
| `dead` | Blank screen, spinner, duplicate | Silence | Cut entirely |
| `error_state` | Error message, failed action | Explain recovery | Keep if relevant, cut if not |
| `tutorial_step` | Step N of M in a walkthrough | "Step N: do X" narration | Numbered text overlay |
| `comparison` | Before/after, A vs B | Comparative narration | Side-by-side or sequential |
| `social_proof` | Reviews, ratings, testimonials | Quote or reference | Highlight/zoom |

### Agent prompt for categorization

```
AGENT PROMPT — Scene Categorization

For each scene in the scene_splits.json, assign:
1. CATEGORY — one of the standard categories above (or create a custom one if needed).
2. PRIORITY — 1 (must include) / 2 (should include) / 3 (can cut if too long).
3. SUGGESTED_DURATION — how long this scene SHOULD be in the final edit.
   Shorter than actual = speed ramp. Longer than actual = extend with Ken Burns or hold.
4. VO_NOTE — one sentence describing what the voice-over should say over this scene.
5. CAPTION_NOTE — what text overlay (if any) should appear.

Output as JSON:
{
  "scene": 1,
  "start": 0.0,
  "end": 2.5,
  "category": "hook",
  "priority": 1,
  "suggested_duration": 2.0,
  "vo_note": "Open with a bold statement about the app's value",
  "caption_note": "Calculate any dose in 3 seconds",
  "editorial_value": "HOOK CANDIDATE",
  "faces_detected": false,
  "on_screen_text": ["Dose Calculator", "Enter patient data"]
}
```

### Automated category assignment (heuristic — agent reviews and corrects)

```python
#!/usr/bin/env python3
"""
auto_categorize.py — Assign categories to scenes based on position, duration,
audio activity, and frame metrics. Agent should review and correct.
ponytail: heuristic rules, not ML. Good enough for 80% of scenes; agent fixes the rest.
"""
import json, os

def categorize_scenes(scene_file, metrics_file=None, silence_file=None, total_dur=0):
    with open(scene_file) as f:
        scenes = json.load(f)

    n = len(scenes)
    for i, s in enumerate(scenes):
        dur = s['duration']
        pos_pct = s['start'] / total_dur * 100 if total_dur else 0

        # Position-based heuristics
        if i == 0 or pos_pct < 10:
            s['category'] = 'hook'
            s['priority'] = 1
        elif i == n - 1 or pos_pct > 90:
            s['category'] = 'cta'
            s['priority'] = 1
        elif dur < 0.5:
            s['category'] = 'transition'
            s['priority'] = 3
        elif dur < 1.0:
            s['category'] = 'demo_action'
            s['priority'] = 2
        elif 30 < pos_pct < 70:
            s['category'] = 'result_reveal'  # middle of video = likely the payoff
            s['priority'] = 1
        else:
            s['category'] = 'demo_input'
            s['priority'] = 2

        s['suggested_duration'] = min(dur, 5.0)  # cap at 5s per scene for short-form
        s['vo_note'] = f"[Agent: describe what happens at {s['start']:.1f}s]"
        s['caption_note'] = ""
        s['editorial_value'] = 'CONTENT'

    output_path = scene_file.replace('.json', '_categorized.json')
    with open(output_path, 'w') as f:
        json.dump(scenes, f, indent=2)
    print(f"Categorized {n} scenes → {output_path}")
    print("⚠️  Agent: review and correct these heuristic labels!")
    return scenes

if __name__ == "__main__":
    categorize_scenes(
        'keyframes/scene_splits.json',
        total_dur=float('${DUR:-30}')
    )
```

```bash
python3 auto_categorize.py
# Then: agent reviews keyframes/scene_splits_categorized.json and corrects labels
```

---

## PHASE 6: INTELLIGENCE MAP — Structured Content Understanding Output

**Agent: this is the master output of Template 9. A single JSON file that captures EVERYTHING known about the video. Downstream templates (U3, U5, U8) consume this file instead of re-analyzing from scratch.**

### Intelligence map schema

```json
{
  "video": {
    "path": "path/to/video.mp4",
    "duration": 22.5,
    "resolution": "1080x1920",
    "fps": "30/1",
    "codec": "h264",
    "has_audio": true,
    "color_space": "bt709",
    "hdr": false
  },
  "scenes": [
    {
      "scene": 1,
      "start": 0.0,
      "end": 2.5,
      "duration": 2.5,
      "category": "hook",
      "priority": 1,
      "editorial_value": "HOOK CANDIDATE",
      "on_screen_text": ["Dose Calculator", "Fast & Accurate"],
      "description": "App splash screen with logo and tagline",
      "faces_detected": false,
      "brightness": 180.5,
      "sharpness": 450.2,
      "vo_note": "This app calculates drug doses in seconds",
      "caption_note": "Calculate any dose in 3 seconds 💊"
    }
  ],
  "transcript": {
    "language": "en",
    "segments": [
      {
        "start": 0.5,
        "end": 2.3,
        "text": "This is the dose calculator",
        "words": [
          {"word": "This", "start": 0.5, "end": 0.7},
          {"word": "is", "start": 0.7, "end": 0.8}
        ]
      }
    ]
  },
  "audio_map": {
    "has_speech": true,
    "has_music": false,
    "silence_segments": [
      {"start": 2.3, "end": 3.0},
      {"start": 8.5, "end": 9.2}
    ],
    "total_silence_pct": 12
  },
  "seo_keywords": ["dose calculator", "drug dosage", "medical app", "patient safety"],
  "content_summary": "22-second app demo showing dose calculation workflow: splash → input form → calculate → results → share options",
  "recommended_edits": {
    "speed_ramp_candidates": [{"scene": 3, "reason": "slow form fill, speed 1.3x"}],
    "cut_candidates": [{"scene": 5, "reason": "loading spinner, 1.2s of dead air"}],
    "hook_frame": "keyframes/scenes/scene_001_at_1.2s.png",
    "thumbnail_frame": "keyframes/scenes/scene_004_at_9.5s.png"
  }
}
```

### Generate the intelligence map

```bash
# ponytail: this assembles all Phase 1-5 outputs into one structured JSON
python3 -c "
import json, os

# Load all available data
scenes = []
if os.path.exists('keyframes/scene_splits_categorized.json'):
    with open('keyframes/scene_splits_categorized.json') as f:
        scenes = json.load(f)
elif os.path.exists('keyframes/scene_splits.json'):
    with open('keyframes/scene_splits.json') as f:
        scenes = json.load(f)

transcript = {}
# Whisper outputs JSON with the same basename as input
video_base = os.path.splitext(os.path.basename('$VIDEO'))[0]
whisper_json = f'keyframes/{video_base}.json'
if os.path.exists(whisper_json):
    with open(whisper_json) as f:
        transcript = json.load(f)

silence_segs = []
if os.path.exists('keyframes/silence_map.txt'):
    import re
    starts = []
    with open('keyframes/silence_map.txt') as f:
        for line in f:
            m_s = re.search(r'silence_start: ([\d.]+)', line)
            m_e = re.search(r'silence_end: ([\d.]+)', line)
            if m_s: starts.append(float(m_s.group(1)))
            if m_e and starts: silence_segs.append({'start': starts.pop(), 'end': float(m_e.group(1))})

frame_metrics = []
if os.path.exists('keyframes/scenes/frame_metrics.json'):
    with open('keyframes/scenes/frame_metrics.json') as f:
        frame_metrics = json.load(f)

# Assemble
intel_map = {
    'video': {
        'path': '$VIDEO',
        'duration': float('$DUR'),
        'resolution': '$RES',
        'fps': '$FPS',
        'has_audio': int('$HAS_AUDIO') > 0 if '$HAS_AUDIO'.isdigit() else False,
    },
    'scenes': scenes,
    'transcript': transcript,
    'audio_map': {
        'silence_segments': silence_segs,
        'total_silence_pct': round(sum(s['end'] - s['start'] for s in silence_segs) / max(float('$DUR'), 0.01) * 100, 1)
    },
    'frame_metrics': frame_metrics,
    'seo_keywords': [],   # Agent fills from on-screen text
    'content_summary': '', # Agent writes after reviewing all data
    'recommended_edits': {},
}

outpath = 'keyframes/content_intelligence.json'
with open(outpath, 'w') as f:
    json.dump(intel_map, f, indent=2)
print(f'Intelligence map: {outpath}')
print(f'Scenes: {len(scenes)} | Transcript segments: {len(transcript.get(\"segments\", []))} | Silence gaps: {len(silence_segs)}')
"
```

### Agent completion prompt — fill in the intelligence map

```
AGENT PROMPT — Complete the Intelligence Map

You have generated keyframes/content_intelligence.json with automated data.
Now complete it:

1. Open each scene's representative frame. Describe what you see in the
   "description" field. List all on-screen text in "on_screen_text".

2. Write a "content_summary" — one paragraph summarizing the entire video's
   narrative arc.

3. Extract "seo_keywords" from actual on-screen UI text and transcript words.
   Never invent keywords that aren't grounded in real content.

4. Fill "recommended_edits":
   - speed_ramp_candidates: scenes that are slow/repetitive
   - cut_candidates: scenes that are dead air / loading / transitions
   - hook_frame: the single most visually striking frame path
   - thumbnail_frame: the best frame for a video thumbnail

5. Review each scene's "category" and "priority". Correct any heuristic
   mis-labels. Ensure the first scene is "hook" and the last is "cta"
   unless the video structure genuinely differs.

6. Write "vo_note" for each scene — one sentence the voice-over should say.
   Ground every note in what is actually visible on screen.

7. Write "caption_note" for scenes that benefit from text overlay — hooks,
   results, CTAs. Not every scene needs a caption.

Save the completed file back to keyframes/content_intelligence.json.
```

---

## PHASE 7: DOWNSTREAM FEED — Using the Intelligence Map

**Agent: the intelligence map replaces "guessing" in every downstream template. Here's how each template consumes it.**

### Feed into U8 (Voice-Over + SEO Captions)

```bash
# The intelligence map provides:
# - Per-scene vo_note → becomes the VO script (Phase 2 of U8 is already done)
# - Transcript → skip Whisper step in U8 if already transcribed
# - SEO keywords → go directly into transcript .md files
# - Scene boundaries → define VO segment timing
#
# Agent: when running U8 after U9, read content_intelligence.json INSTEAD of
# extracting frames and writing scripts from scratch. The work is done.

echo "U8 integration: read keyframes/content_intelligence.json for scene-aligned VO scripts"
jq '.scenes[] | {scene, start, end, vo_note, caption_note}' keyframes/content_intelligence.json
```

### Feed into U3 (General Enhance)

```bash
# The intelligence map provides:
# - speed_ramp_candidates → which scenes to speed up
# - cut_candidates → which scenes to remove (dead air, loading)
# - silence_segments → where to cut or add pattern interrupts
# - editorial_value per scene → prioritize enhancement effort
#
# Agent: when running U3 after U9, use recommended_edits to drive enhancement decisions
# instead of guessing what to speed up or cut.

echo "U3 integration: recommended cuts and speed ramps"
jq '.recommended_edits' keyframes/content_intelligence.json
```

### Feed into U5 (Viral Pipeline + Final Gate)

```bash
# The intelligence map provides:
# - hook_frame → verify the hook is the strongest visual moment
# - category=hook scene → is it < 1.3s for TikTok? < 2s for Reels?
# - loop potential → does the CTA scene visually connect back to the hook?
# - faces_detected → faceless brand compliance check per scene
#
# Agent: when running U5 after U9, the hook analysis is already done.
# Focus on loop engineering and final QA gate.

echo "U5 integration: hook analysis"
jq '.scenes[] | select(.category == "hook")' keyframes/content_intelligence.json
```

### Export scene clips for manual editing (DaVinci, Premiere, CapCut)

```bash
# Some editors prefer pre-split clips with descriptive names
# The intelligence map category labels become clip names

python3 -c "
import json, subprocess, os

with open('keyframes/content_intelligence.json') as f:
    data = json.load(f)

os.makedirs('keyframes/labeled_clips', exist_ok=True)

for s in data.get('scenes', []):
    cat = s.get('category', 'unknown')
    idx = s.get('scene', 0)
    outfile = f\"keyframes/labeled_clips/{idx:03d}_{cat}_{s['start']:.1f}s-{s['end']:.1f}s.mp4\"
    subprocess.run([
        'ffmpeg', '-i', '$VIDEO',
        '-ss', str(s['start']), '-to', str(s['end']),
        '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy',
        '-y', outfile
    ], capture_output=True)
    print(f\"{os.path.basename(outfile)}: {s.get('duration', '?')}s — {cat}\")
"
```

---

## PHASE 8: QA — Verify Content Intelligence Completeness

```bash
echo "=== CONTENT INTELLIGENCE QA ==="

# 1. Intelligence map exists and is valid JSON
[ -f "keyframes/content_intelligence.json" ] && echo "  Map file: PASS" || echo "  Map file: FAIL"
jq empty keyframes/content_intelligence.json 2>/dev/null && echo "  Valid JSON: PASS" || echo "  Valid JSON: FAIL"

# 2. Every second is accounted for (no gaps in scene coverage)
python3 -c "
import json
with open('keyframes/content_intelligence.json') as f:
    data = json.load(f)
scenes = data.get('scenes', [])
if not scenes:
    print('  Scene coverage: FAIL (no scenes)')
else:
    dur = data['video']['duration']
    covered = sum(s.get('duration', s['end'] - s['start']) for s in scenes)
    pct = covered / dur * 100 if dur else 0
    status = 'PASS' if pct >= 95 else 'FAIL' if pct < 80 else 'WARN'
    print(f'  Scene coverage: {status} ({pct:.0f}% of {dur:.1f}s covered)')
    # Check for gaps
    for i in range(len(scenes) - 1):
        gap = scenes[i+1]['start'] - scenes[i]['end']
        if gap > 0.5:
            print(f'  ⚠️ Gap: {scenes[i][\"end\"]:.1f}s → {scenes[i+1][\"start\"]:.1f}s ({gap:.1f}s uncovered)')
"

# 3. All scenes have categories
python3 -c "
import json
with open('keyframes/content_intelligence.json') as f:
    scenes = json.load(f).get('scenes', [])
missing_cat = [s['scene'] for s in scenes if not s.get('category')]
if missing_cat:
    print(f'  Categories: FAIL (missing on scenes: {missing_cat})')
else:
    print(f'  Categories: PASS (all {len(scenes)} scenes labeled)')
"

# 4. No hallucinated content — check that on_screen_text fields exist
python3 -c "
import json
with open('keyframes/content_intelligence.json') as f:
    scenes = json.load(f).get('scenes', [])
empty_text = [s['scene'] for s in scenes if not s.get('on_screen_text') and s.get('category') not in ('transition', 'dead')]
if empty_text:
    print(f'  On-screen text: WARN (empty for content scenes: {empty_text} — agent should review frames)')
else:
    print(f'  On-screen text: PASS')
"

# 5. Keyframes directory has expected files
SEC_FRAMES=$(ls keyframes/sec_*.png 2>/dev/null | wc -l)
SCENE_FRAMES=$(ls keyframes/scenes/scene_*.png 2>/dev/null | wc -l)
echo "  Per-second frames: $SEC_FRAMES"
echo "  Scene frames: $SCENE_FRAMES"
[ "$SEC_FRAMES" -gt 0 ] && echo "  Frame extraction: PASS" || echo "  Frame extraction: FAIL"

echo ""
echo "Three-state verdict:"
echo "  PASS = intelligence map complete, all scenes categorized, no gaps"
echo "  FAIL = missing scenes, empty categories, broken JSON"
echo "  INCONCLUSIVE = agent has not reviewed frames yet (heuristic-only categories)"
```

---

## LINUX vs macOS — Template 9 Specific

| Step | macOS | Linux |
|:---|:---|:---|
| Tesseract install | `brew install tesseract` | `apt install tesseract-ocr` |
| Tesseract languages | `brew install tesseract-lang` (for Arabic OCR) | `apt install tesseract-ocr-ara` |
| OpenCV install | `pip3 install opencv-python` | `pip install opencv-python-headless` (headless on servers) |
| PySceneDetect | `pip3 install scenedetect[opencv]` | `pip install scenedetect[opencv]` |
| Whisper GPU | CPU-only on most Macs (M1/M2 use MPS) | CUDA with `pip install torch` first for 5-10× speed |
| Face detection models | Bundled with OpenCV | Bundled with OpenCV |
| `stat` syntax | `stat -f%z` for file size | `stat -c%s` for file size |
| Font for OCR | Built-in fonts, Tesseract uses its own | Install `fonts-liberation` for better glyph coverage |

### Linux-only: headless OpenCV

```bash
# On Linux servers without a display, use headless OpenCV
pip install opencv-python-headless 2>/dev/null
# This avoids the "cannot open display" error when importing cv2
```

### macOS: Apple Silicon GPU acceleration for Whisper

```bash
# M1/M2/M3 Macs can use MPS (Metal Performance Shaders) for Whisper
# Just install PyTorch with MPS support — Whisper auto-detects it
pip3 install torch torchvision torchaudio 2>/dev/null
# Then run Whisper normally — it will use MPS if available
whisper input.mp4 --model small --device mps
```

---

## COMPLETE EXAMPLE: App Demo Video → Full Content Intelligence

```
Input: dose_calculator_demo.mp4 (22s, 1080×1920, 30fps, no audio)

Phase 1: DEEP PERCEIVE
  - Standard probe: 22s, 1080×1920, 30/1, h264, no audio, bt709
  - Per-second extraction: 23 frames (sec_0000.png → sec_0022.png)
  - Scene detection: 7 scenes detected (threshold=27)
  - Scene frames: 7 representative frames extracted

Phase 2: VISUAL READ
  - Agent reviews each scene frame natively (no Python for descriptions)
  - Scene 1 (0.0-2.5s): Splash screen — "Dose Calculator" logo, blue gradient
  - Scene 2 (2.5-5.5s): Input form — Name, Age, Weight fields (empty)
  - Scene 3 (5.5-8.0s): Input form — Fields filled: "Ahmed", "32", "85kg"
  - Scene 4 (8.0-9.5s): Calculate button — highlighted with tap animation
  - Scene 5 (9.5-13.0s): Results — "250mg", "2x/day", green checkmark
  - Scene 6 (13.0-18.0s): Detail view — breakdown chart, scrolling
  - Scene 7 (18.0-22.0s): Share screen — "Share", "Save PDF", "Copy" buttons
  - OCR backup used for small chart labels in scene 6

Phase 3: AUDIO UNDERSTAND
  - No audio track → skip transcription, mark all as SILENCE
  - This video needs voice-over (feed to U8)

Phase 4: SCENE SPLIT
  - Auto-detected 7 scenes match the logical flow
  - No merges needed; no splits needed
  - Scene clips exported to keyframes/scene_clips/

Phase 5: CATEGORIZE
  - Scene 1: hook (splash) — priority 1
  - Scene 2: demo_input (empty form) — priority 2
  - Scene 3: demo_input (filled form) — priority 2
  - Scene 4: demo_action (calculate tap) — priority 1
  - Scene 5: result_reveal (dose result) — priority 1
  - Scene 6: detail_scroll (chart) — priority 2, speed_ramp 1.3×
  - Scene 7: cta (share options) — priority 1

Phase 6: INTELLIGENCE MAP
  - content_intelligence.json generated with all data
  - SEO keywords: dose calculator, drug dosage, patient weight, medical app
  - Content summary: "22s app demo showing dose calculation from input to shareable result"
  - Recommended: speed ramp scene 6, hook frame = scene 5 result reveal

Phase 7: DOWNSTREAM FEED
  - Ready for U8: scene vo_notes provide complete VO script skeleton
  - Ready for U3: speed ramp scene 6, no cuts needed
  - Ready for U5: hook = scene 1 (2.5s — trim to 1.5s for TikTok)
  - Labeled clips exported for manual editing

Phase 8: QA
  - Map file: PASS
  - Valid JSON: PASS
  - Scene coverage: PASS (100% of 22s covered)
  - Categories: PASS (all 7 scenes labeled)
  - On-screen text: PASS (all content scenes have text)
  - Frame extraction: PASS (23 per-second + 7 scene frames)
  VERDICT: PASS → intelligence map ready for downstream use
```

---

## PRO-TIPS: AI-Visualization and Understanding (Template 9 Specific)

1. **Agent native vision is ALWAYS the primary tool.** Never write Python to describe what an image shows. You can SEE the frames. Scripts are for numeric measurements only (brightness, sharpness, face detection coordinates). If you find yourself writing `"the image appears to show..."` in Python, stop — just look at the frame yourself.
2. **PySceneDetect threshold=27 is a sensible default.** Lower (e.g., 15) detects subtitle changes and minor movements as scene breaks — too noisy. Higher (e.g., 40) misses gradual transitions. Start at 27, review results, adjust if needed.
3. **Whisper `--model small` beats `--model base` for technical content.** Medical terms, app-specific vocabulary, and accented speech are 30-40% more accurate with `small`. The speed difference (2× slower) is worth it. Use `base` only for general conversational content or when speed matters more than accuracy.
4. **Silence map + scene boundaries = natural VO segmentation.** When feeding into U8, silence gaps are where the VO pauses should be. Scene boundaries are where the VO topic shifts. This eliminates the "flat narration" problem where TTS reads everything at the same pace.
5. **Duplicate frame detection reveals speed-ramp candidates.** If 5 consecutive seconds produce near-identical frames (hamming distance ≤ 3), that segment is visually static and should be speed-ramped in U3. The viewer gains nothing from watching a static screen at 1× speed.
6. **Face detection with Haar cascades has ~15% false positive rate.** Treat face detection as a flag to review, not a definitive answer. Always visually confirm before applying blur or flagging a face-leak violation. OpenCV Haar is fast but imprecise — good enough for flagging, not for automated decisions.
7. **OCR is a fallback, not the primary method.** Tesseract OCR on screen recordings is 70-85% accurate — enough to verify unclear text, but not enough to replace agent vision for content description. Use OCR only when the agent says `[unclear]` on a specific text element.
8. **The intelligence map is the single source of truth.** Once generated, all downstream templates should read from `content_intelligence.json` rather than re-probing, re-transcribing, or re-extracting. This prevents drift between templates and saves processing time.
9. **For long videos (> 2 minutes), use per-second + scene-change extraction, not full I-frame dump.** I-frame extraction on a 5-minute video at 30fps can produce 150+ keyframes — too many to review. Per-second (300 frames) + scene-change (10-30 frames) gives you full coverage without overwhelming the analysis.
10. **Auto-categorization is 80% accurate. Always review.** The heuristic rules (position-based: first=hook, last=CTA, short=transition) are right most of the time, but they miss non-standard video structures (e.g., a video that starts with the result, then explains how). Always do a human-in-the-loop pass on categories.
11. **Audio activity classification catches "music-only" segments.** If a segment has consistent RMS energy but no speech (per Whisper), it's likely a music interlude or transition. These are good candidates for speed-ramping or cutting in U3, and they should have no VO narration in U8.
12. **Labeled scene clips are gold for editors.** When you export clips named `003_result_reveal_9.5s-13.0s.mp4`, any human editor can immediately find the scene they need without scrubbing through the full video. This is the difference between "AI helped" and "AI saved me 30 minutes."
13. **Arabic OCR on Linux needs `tesseract-ocr-ara`.** Default Tesseract on Linux has English only. For Arabic UI text, install the Arabic language pack: `apt install tesseract-ocr-ara`. Then use `tesseract image.png stdout -l ara` or `tesseract image.png stdout -l ara+eng` for mixed.
14. **Content intelligence feeds SEO directly.** The `on_screen_text` fields across all scenes, combined with Whisper transcript, produce a complete keyword corpus grounded in actual content — not invented marketing terms. Use this corpus for U8 SEO keywords, video descriptions, and hashtag generation.
15. **NEVER claim the intelligence map is complete without Phase 8 QA.** The map is only valid if: (a) every second of the video is covered by a scene, (b) every content scene has a category, (c) every content scene has on_screen_text, and (d) the JSON is valid. INCONCLUSIVE = agent has not reviewed all frames yet.

---

*Template 9 turns the AI agent from a blind command executor into a seeing content analyst. The 8-phase pipeline (Deep Perceive → Visual Read → Audio Understand → Scene Split → Categorize → Intelligence Map → Downstream Feed → QA) produces a structured content_intelligence.json that feeds directly into U3 (enhance), U5 (viral), and U8 (voice-over). Every tool is free and local. The agent's native vision is the primary analysis method — scripts are for numbers, not descriptions.*
LIGHT STACK

Light Stack Optimization & Fast Execution Package

13 Executable Code Blocks

Verbatim executive directives and code for Light Stack optimization & Gemini review decisions directly from Hybrid templates for media-assets management.md.

📋 EXECUTIVE TEMPLATE DIRECTIVES & CODE (VERBATIM FROM HYBRID TEMPLATES MD) Source: Hybrid templates for media-assets management.md · 13 Code Blocks
Paste-ready package · 445 lines from original file
SYSTEM INSTRUCTIONS & GLOBAL INVARIANTS (from Hybrid templates for media-assets management.md):
====================================================================================
Hybrid templates for media-assets management.md

9:16
----
### A) Cognitive pipeline depth (Perceive → Interpret → Compose → Realize → Critique/QA)
Run as a mental loop even when not installing Apex modules:
1. PERCEIVE — Before editing: probe media (duration, res, fps, HDR?, audio streams). For stills: brightness/contrast/sharpness/faces if available. Cache what you learned (notes or JSON). Never invent duration/size.
2. INTERPRET — Match assets to intent beats (ad: 5 beats OR narrative: 7). Ask: which image/screen is Hook / Problem / Proof / How / CTA? Meaning over pretty.
3. COMPOSE — Choose motion/transition per beat (Ken Burns target, hard cut default, zoom only within brand ceiling). Every effect needs a WHY.
4. REALIZE — Render with hard invariants: image inputs -framerate 30; mux apad + -shortest; final audio -ar 48000; HDR→SDR before grade; numbered outputs 01_… for stable sort.
5. CRITIQUE / QA — Extract frames at ~25/50/75% of duration; mute-test hook; only then mark done. Max 3 re-render attempts with evidence, then stop and escalate input quality.
Checkpoint: after each of the five stages, write one line of state (path + decision). Prevents context decay.

### B) Tiered QA / false-positive defense (never claim done on hope)
Tier-0 (deterministic, always first — free, no API):
- File exists, size > trivial, has video stream, duration > 0
- Resolution matches target canvas (1080x1920 / 1920x1080 / 1080x1350 / 1080x1080)
- FPS ≈ 30 (or intentional 60); no watermark; A/V start_time ~0 if both present
Three-state law (non-negotiable):
- PASS = measured and met
- FAIL = measured and failed → fix root cause → re-render → re-check
- INCONCLUSIVE = could not measure → NOT PASS (blocks "done")
final_gate style: all critical checks PASS before shipping. Never promote INCONCLUSIVE to PASS.
Anti-mutation: do not weaken checks, skip QA, hardcode PASS, or invent green results.
Self-heal bound: max 3 fix loops with written evidence (what failed, what changed).

### C) Vision / VLM / theme seeds / brain (lightweight, free-first — no crash if missing)
Vision is enhancement, never a hard dependency:
- Tier free-first: (1) local OpenCV/metrics if available (2) free cloud caption if key exists (3) else agent native eyes on QA frames only
- If ALL vision fails → continue with ffprobe + mute test + frame extract. Do NOT block pipeline.
VLM / judge protocol (when agent can see frames):
- Score FAILED / PROPER / EXCELLENT on meaning, readability of UI/numbers, face-leak, grade drift across cuts
- FAILED or face-leak on faceless series = FAIL even if pixels "look sharp"
Theme seeds (memory of what works):
- When a cut is EXCELLENT or strong PASS, save one line: app + kit + hook style + zoom used + platform → reuse next time (theme_seeds habit)
- Do not auto-reuse a seed that conflicts with brand §1 ceilings
Brain / multi-pass habit (orchestrator-lite, no extra processes):
- Pass 1: structure (hook, beats, CTA)
- Pass 2: retention (interrupts, captions, loop)
- Pass 3: verify (Tier-0 + frames + mute)
Never skip Pass 3.

use these tips to work well, and below codes may help you

### E) Anti-Hallucination Rules (MANDATORY — apply to ALL templates)

**Agent: these rules prevent you from inventing information. Breaking any of them = immediate FAIL.**

1. **Never invent duration, resolution, FPS, or file size.** Always run `ffprobe` and read the actual output. If ffprobe fails, report INCONCLUSIVE — do NOT estimate.
2. **Never fabricate QA results.** Run the actual check commands. Copy-paste the real terminal output. If a command errors out, that check is INCONCLUSIVE, not PASS.
3. **Never assume a file exists.** Before referencing any file in a command, verify it exists: `[ -f "$FILE" ] && echo EXISTS || echo MISSING`. If missing, stop and report.
4. **Never guess crop values.** Extract one frame first (`ffmpeg -i input.mp4 -frames:v 1 frame_check.png -y`), measure the actual chrome/dock height from the image, then crop.
5. **Never invent medical numbers, app data, or user content.** If the source material shows a number on screen, read it exactly. If you cannot read it, say so.
6. **Never claim a render succeeded without verifying the output file.** After every `ffmpeg` command, immediately check: file exists, size > 0, has video stream, duration > 0.
7. **Never skip a step because you think it is unnecessary.** Follow the template order. If a step truly does not apply (e.g., HDR→SDR on non-HDR source), log WHY you skipped it.
8. **Never re-use values from a previous render.** Each new render must re-probe its own input. Cached values from earlier steps may be stale if the file was re-encoded.
9. **Never write ffmpeg commands from memory without checking syntax.** If unsure about a filter name or parameter, use `ffmpeg -filters | grep <name>` to verify it exists on this system.
10. **If ANY command returns a non-zero exit code, STOP.** Read the error message. Diagnose. Fix. Do NOT silently continue with a broken intermediate file.

**Post-render verification (run after EVERY ffmpeg output):**
```bash
# ponytail: 4-line instant sanity check — add after every ffmpeg render
OUT="$1"  # pass output filename
[ -f "$OUT" ] || { echo "FAIL: file not created"; exit 1; }
SIZE=$(stat -f%z "$OUT" 2>/dev/null || stat -c%s "$OUT" 2>/dev/null)
[ "$SIZE" -gt 1000 ] || { echo "FAIL: file too small ($SIZE bytes) — render likely failed"; exit 1; }
ffprobe -v quiet -show_entries stream=codec_type -of csv=p=0 "$OUT" | grep -q video || { echo "FAIL: no video stream"; exit 1; }
echo "PASS: $OUT exists, ${SIZE} bytes, has video stream"
```

### F) Image Production Rules (for thumbnails, social images, still exports)

**Agent: when the user asks for images (thumbnails, social posts, stills from video), follow these rules to produce sharp, viral-quality images.**

1. **Resolution minimums:** Thumbnails = 1280×720 (YouTube) or 1080×1920 (9:16 story/pin). Social images = 1080×1080 minimum. Never export below these.
2. **Format:** PNG for maximum quality / transparency. JPEG at quality 95+ for social upload (smaller file, negligible loss). WebP for web.
3. **Text on images:** Use high-contrast colors (white text + black outline, or vice versa). Minimum font size 48px at 1080p. Text must be inside safe zones.
4. **Sharpness:** Always apply `unsharp=5:5:0.8:3:3:0.4` after any downscale. Screen content loses edge definition without it.
5. **No upscaling.** If the source is 720p, the output is 720p. Upscaling = blur. Report the limitation.
6. **Color space:** sRGB for all web/social images. Never export in Adobe RGB or ProPhoto — colors will look wrong on phones.
7. **Thumbnail from video — extract the highest-impact frame:**
```bash
# Extract frame at the moment of peak visual interest (usually the result/payoff)
# Step 1: Find the payoff timestamp (usually 60-80% through the video)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 input.mp4)
PAYOFF=$(echo "$DUR * 0.7" | bc -l)
# Step 2: Extract at native resolution with sharpening
ffmpeg -ss "$PAYOFF" -i input.mp4 -frames:v 1 \
  -vf "unsharp=5:5:0.8:3:3:0.4" \
  -q:v 2 thumbnail_raw.png -y
# Step 3: Resize for YouTube (if needed) with lanczos
ffmpeg -i thumbnail_raw.png \
  -vf "scale=1280:720:flags=lanczos,unsharp=5:5:0.8:3:3:0.4" \
  thumbnail_yt.jpg -y
```
8. **Social image from still — add safe-zone padding:**
```bash
# 1:1 social image with padding (Instagram feed)
ffmpeg -i source.png \
  -vf "scale=1080:1080:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_square.jpg -y

# 9:16 story/pin image
ffmpeg -i source.png \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease:flags=lanczos,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white,unsharp=5:5:0.8:3:3:0.4" \
  social_story.jpg -y
```

### G) Vision Agent Prompt — Paste to Grok / AGY / Any Multimodal AI (apply to ALL templates)

**Agent: when you need a multimodal AI (Grok, Gemini Flash, GPT-4o, etc.) to analyze video frames, paste ONE of these prompts BEFORE sending the frames. These prompts prevent hallucination, enforce exact text reading, and structure output for downstream templates (U3/U5/U8/U9).**

**Prompt 1 — Single Frame Analysis (paste before each frame or batch of frames):**

```
You are analyzing a video frame extracted from a screen recording or app demo.
Follow these rules EXACTLY — breaking any rule = hallucination = FAIL.

1. READ ALL TEXT: Spell out every word visible on screen — buttons, labels, numbers,
   headers, input values, placeholder text, error messages. Character by character.
   If text is too small or blurry → write [unclear]. NEVER guess or complete partial text.

2. NUMBERS ARE SACRED: If you see 250mg, report 250mg. Not "about 250" or "approximately
   200mg". Medical/financial values are read exactly or marked [unclear].

3. DESCRIBE WHAT YOU SEE — NOT WHAT YOU KNOW:
   - Say "blue button labeled 'Calculate'" — not "this is probably a calculate function"
   - Say "three input fields: Name, Age, Weight" — not "a typical patient form"
   - Your training data about this app is IRRELEVANT. Only the pixels matter.

4. IDENTIFY THE UI STATE:
   - Screen name / page title
   - State: form | result | menu | loading | transition | error | splash | settings
   - Primary action available to the user right now

5. CHANGES: If you have a previous frame, state what changed:
   New screen | New value | Scroll position | Button highlighted | Animation | Keyboard open

6. FACES: Report if any human face is visible (even partial/reflected). Yes/No.

7. RATE THIS FRAME:
   HOOK = visually striking, result reveal, aha moment, scroll-stopper
   CONTENT = informational, good for narration, shows a step
   TRANSITION = mid-screen-change, motion blur, not standalone
   DEAD = blank, loading spinner, duplicate of adjacent frame

8. FLAG ISSUES: watermark | blurry text | sensitive data | face visible | dark/overexposed

OUTPUT FORMAT (use exactly this):
Screen: [name]
State: [form/result/menu/loading/transition/error/splash/settings]
Text visible: [list ALL readable text, line by line]
Numbers: [exact numbers if any, or "none"]
Changed from previous: [delta, or "first frame"]
Faces: [yes/no]
Rating: [HOOK/CONTENT/TRANSITION/DEAD]
Issues: [list, or "none"]
One-line summary: [what is happening in this frame]
```

**Prompt 2 — Batch Frame Understanding (paste once, then send 8-15 frames together):**

```
You are building a content map from extracted video frames. These frames are in
chronological order from a screen recording / app demo video.

For ALL frames combined, produce:

1. SCENE LIST: Group consecutive frames into scenes. Each scene = one logical action.
   Format: Scene N (frames X-Y): "[what happens]" — [HOOK/CONTENT/TRANSITION/DEAD]

2. NARRATIVE ARC: One paragraph describing the video's story from start to end.
   Only reference what you can SEE. Do not infer features not shown.

3. TEXT INVENTORY: Every unique piece of on-screen text across all frames.
   Mark which frame(s) each text appears in.

4. VO SCRIPT SKELETON: For each scene, write ONE sentence a voice-over narrator would say.
   Ground every sentence in visible content. Never describe features you can't see.

5. SEO KEYWORDS: Extract keywords from visible text only (button labels, headings,
   feature names). Never invent marketing keywords.

6. THUMBNAIL PICK: Which single frame is best for a video thumbnail? Why?

7. ISSUES:
   - Any face visible? Which frame?
   - Any frame too dark/blurry/overexposed?
   - Any sensitive data (real names, emails, phone numbers)?
   - Any duplicate/redundant frames that show the same thing?

RULES:
- Only describe what is VISIBLE in the frames.
- If a frame is blurry or unclear, say so — do not guess.
- If you recognize the app from training data, IGNORE that knowledge. Describe pixels only.
- Numbers are exact. "250mg" is 250mg, never "about 250."
```

**Prompt 3 — Quick QA Check (paste when verifying a rendered video — send 3 frames at 25%/50%/75%):**

```
You are QA-checking a rendered video. I am showing you 3 frames extracted at
25%, 50%, and 75% of the video duration.

For each frame, verify:
1. Is the video content visible and sharp? (not black, not corrupt, not frozen)
2. Are captions/subtitles readable? (correct font, correct position, not cut off)
3. Is the aspect ratio correct? (no stretch, no black bars unless expected)
4. Is there any visual artifact? (encoding glitch, color banding, frame tear)

Verdict per frame: PASS / FAIL / INCONCLUSIVE (with reason)
Overall verdict: PASS only if all 3 frames PASS.
```

**When to use which prompt:**
| Situation | Prompt | Why |
|:---|:---|:---|
| Analyzing video frame by frame to build intelligence map (U9) | Prompt 1 per frame | Maximum detail per frame |
| Quick video understanding for VO script or edit plan (U8, U3) | Prompt 2 with 8-15 frames | Efficient batch analysis |
| Verifying a rendered/exported video (all templates QA phase) | Prompt 3 with 3 frames | Fast PASS/FAIL gate |
| Unknown video, first encounter | Prompt 2 first, then Prompt 1 on unclear scenes | Broad then deep |

**Anti-hallucination enforcement for vision agents:**
- If the agent says "this appears to be" or "this is likely" about clearly visible content → re-prompt: "Describe what you SEE, not what you think."
- If the agent describes features not shown in any frame → re-prompt: "Only reference visible content. Which frame shows this?"
- If numbers differ between agent response and visible text → the FRAME is truth, agent is wrong. Re-prompt with crop of the number.
- If agent provides a confident description but the frame is blurry → INCONCLUSIVE. Do not accept confident answers about unclear content.

### D) Platform Adaptation — Linux vs macOS (apply to ALL templates)

**Agent: before running any command, detect the OS and adapt accordingly.**

**Detect OS first:**
```bash
OS_TYPE="$(uname -s)"  # Darwin = macOS, Linux = Linux
```

**Screen recording — preventing blurry text on Linux:**
- Linux headless/VPS servers have NO display server by default. Screen recording requires a virtual framebuffer.
- Blurry text in Linux screen recordings happens because of: (1) low virtual resolution, (2) missing fonts, (3) wrong pixel format, (4) low bitrate encoding.

```bash
# Linux: set up virtual display with HIGH resolution + proper color depth
# ponytail: Xvfb resolution must match or exceed target canvas to avoid upscale blur
if [ "$OS_TYPE" = "Linux" ]; then
  # Install fonts for sharp text rendering
  apt install -y fonts-liberation fonts-dejavu-core fontconfig 2>/dev/null
  fc-cache -fv

  # Virtual framebuffer — use 2x target resolution for crisp downscale
  Xvfb :99 -screen 0 2160x3840x24 -ac &
  export DISPLAY=:99

  # Screen capture with ffmpeg — force high quality
  ffmpeg -video_size 2160x3840 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**macOS screen recording:**
```bash
if [ "$OS_TYPE" = "Darwin" ]; then
  # macOS: use native AVFoundation — sharp Retina capture
  # List devices first to find screen index
  ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -i screen

  # Capture at native Retina resolution, then downscale with lanczos
  ffmpeg -f avfoundation -framerate 30 -capture_cursor 0 -i "1:none" \
    -c:v libx264 -preset slow -crf 14 -pix_fmt yuv420p \
    -vf "scale=1080:1920:flags=lanczos" \
    raw_capture.mp4
fi
```

**Linux text sharpness rules (MANDATORY on Linux):**
1. Always use `-vf "scale=...:flags=lanczos"` — never default bilinear. Lanczos preserves text edges.
2. Capture at 2x target resolution minimum, then downscale. Upscaling = blur.
3. Use CRF 14–16 for screen content (not CRF 18 — text needs more bits than natural video).
4. Install system fonts before any text rendering: `apt install fonts-liberation fonts-dejavu-core`
5. For subtitle burn-in on Linux, verify the font exists: `fc-list | grep -i arial` — if missing, substitute with DejaVu Sans.
6. Use `-tune stillimage` for mostly-static screen content (improves text sharpness at same bitrate).

**macOS text sharpness rules:**
1. Retina displays capture at 2x — always downscale with lanczos, never crop at 2x resolution.
2. Crop browser chrome AFTER downscale (chrome pixel height differs at Retina vs logical resolution).
3. macOS `screencapture` CLI can grab stills: `screencapture -x -R0,0,1080,1920 frame.png`

**Tool availability differences:**
| Tool | macOS install | Linux install | Notes |
|:---|:---|:---|:---|
| FFmpeg | `brew install ffmpeg` | `apt install ffmpeg` | Same commands work on both |
| bc | Pre-installed | `apt install bc` (usually pre-installed) | Same |
| jq | `brew install jq` | `apt install jq` | Same |
| Whisper | `pip install openai-whisper` | `pip install openai-whisper` | Linux GPU = faster |
| ImageMagick | `brew install imagemagick` | `apt install imagemagick` | Same |
| Xvfb | N/A (not needed) | `apt install xvfb` | Linux-only, for headless screen capture |
| fonts | Built-in | `apt install fonts-liberation fonts-dejavu-core` | MUST install on Linux |

====================================================================================
EXECUTIVE TEMPLATE DIRECTIVES & CODE (from Hybrid templates for media-assets management.md):
====================================================================================
==================================




---

# Light-Stack Optimization Tips — Heavy vs Light Media Pipeline (Gemini 3.6 Reviewed)

**Agent directive: These tips replace heavy/costly pipeline defaults with lighter alternatives that produce the SAME output quality for UI/app demo workflows. Read every table row before choosing a tool. When in doubt, pick the lighter option — upgrade to heavy only with explicit evidence that the light path failed.**

**Source:** Grok architectural analysis + Gemini 3.6 senior-dev review.
**Applies to:** ALL templates (U1–U9), all platforms (macOS + Linux VPS).

---

## 1) Speech-to-Text / Captions (heavy: Whisper medium/large + CUDA)

| Heavy | What it does | Lighter alternative | Same function? | Why better/lighter |
|:---|:---|:---|:---|:---|
| Whisper medium/large + torch | Transcribe spoken audio | Don't STT if you already have the script (U8 edge-tts path) | Captions from VO | Script → SRT is free, exact, no model download |
| Whisper local | Captions for existing spoken video | Agent listens / reads transcript you already wrote | Rough captions | Zero local ML if VO was generated by you |
| Whisper local | Captions when only the video has speech | faster-whisper base/tiny (CTranslate2) | Yes | Same idea as Whisper, much less RAM/CPU |
| Whisper local | Online shortcut | OpenAI Whisper API (if key exists) | Yes | Offloads compute from Mac/VPS |
| Whisper word timestamps | Kinetic captions | edge-tts --write-subtitles VTT when VO is TTS | Often better | Timestamps match generated speech; free, light |

**Recommendation:**

1. **Preferred:** VO/scripts first → edge-tts + VTT/SRT (no Whisper needed at all).
2. **If you must STT local:** faster-whisper + base (or tiny for short clips).
3. **If key available and VPS is weak:** Whisper API instead of local medium/large.
4. **Avoid:** local medium/large unless accuracy on messy real human speech is mandatory.

**CLI — Script-to-SRT (skip Whisper entirely when you wrote the VO script):**

```bash
# ponytail: If you generated the VO with edge-tts, the VTT is already perfect — convert to SRT
# This replaces the ENTIRE Whisper pipeline for TTS-generated audio
edge-tts --voice ar-SA-HamedNeural --rate=-8% \
  --text "$(cat script.txt)" \
  --write-media voiceover.wav \
  --write-subtitles captions.vtt

# Convert VTT → SRT (ffmpeg handles this natively)
ffmpeg -i captions.vtt captions.srt -y
echo "SRT generated from TTS timestamps — zero Whisper, zero GPU, perfect sync"
```

**CLI — faster-whisper fallback (only when you have unknown human speech):**

```bash
# ponytail: faster-whisper base uses ~1/10th the RAM of Whisper large
# Install: pip install faster-whisper
python3 -c "
from faster_whisper import WhisperModel
model = WhisperModel('base', compute_type='int8')  # ponytail: int8 = even lighter on CPU
segments, info = model.transcribe('input.mp4', word_timestamps=True)
import json
words = []
for seg in segments:
    for w in seg.words:
        words.append({'word': w.word.strip(), 'start': round(w.start, 3), 'end': round(w.end, 3)})
with open('whisper_words.json', 'w') as f:
    json.dump(words, f, indent=2)
print(f'Transcribed {len(words)} words')
"
```

---

## 2) Text-to-Speech (heavy: Coqui / XTTS; paid-heavy: OpenAI TTS always)

| Heavy / costly | What it does | Lighter alternative | Same function? | Why |
|:---|:---|:---|:---|:---|
| Coqui / XTTS | Local high-end TTS | edge-tts neural voices | Yes for product demos | No GPU, free, excellent AR+EN for UI VO |
| OpenAI TTS always | Highest "studio" EN | edge-tts Andrew/Ava @ −10…−12% | Near-same for demos | Free; pacing fix beats "more expensive model" |
| Segment dual-voice stitch | Mixed AR+EN | Single AR voice speaking EN terms | Same Type-1 intent | Less glue/QA; fewer AI seams |
| Piper (if quality weak) | Offline EN | Keep edge-tts for quality; Piper only if offline-only | Quality tradeoff | Piper lighter but often worse than edge-tts |

**Recommendation:**

- **Default always:** edge-tts (AR: Shakir/Salma; EN: Andrew/Ava).
- **Use OpenAI TTS** only when EN must be premium marketing VO and key is free for you to burn.
- **Skip Coqui/XTTS** for this workflow — heavier, rarely better than edge-tts for app demos.

**CLI — Standard edge-tts (the default for ALL templates):**

```bash
# Arabic VO (Shakir for male, Salma for female)
edge-tts --voice ar-SA-HamedNeural --rate=-8% \
  --text "$(cat script_ar.txt)" \
  --write-media vo_ar.wav \
  --write-subtitles vo_ar.vtt

# English VO (Andrew for male, Ava for female)
edge-tts --voice en-US-AndrewNeural --rate=-10% \
  --text "$(cat script_en.txt)" \
  --write-media vo_en.wav \
  --write-subtitles vo_en.vtt

# ponytail: Single voice for mixed AR+EN is better than dual-voice stitch
# Arabic neural voices handle English terms (mg, kg, app names) cleanly
```

---

## 3) "Understand Video" / Vision (heavy: OpenCV stack, free cloud cascade, dense frames)

| Heavy | What it does | Lighter alternative | Same function? | Why |
|:---|:---|:---|:---|:---|
| Local OpenCV pipelines | Metrics, faces | Agent native vision on 8–12 frames | Meaning/UI text | Agent already paid; no OpenCV install |
| Free cloud caption chain | Auto-describe | Same agent vision on extracted JPGs | Yes | One hop; no multi-provider cascade |
| 1 frame every second | Full UI map | 10–12% ladder + key transitions | Yes for demos | 10 frames ≪ 75; still anti-hallucination |
| Dense + contact sheet always | Triage | Contact sheet first, dense only if form-heavy | Yes | Triage is cheap; density on demand |
| Full re-watch every encode | QA | 3 frames: 25/50/75% only | QA intent | Hybrid Tier already; stay there |

**Recommendation:**

- **Perceive:** percent ladder (or every 5s for complex UI), agent reads frames.
- **QA:** 25/50/75% only — 3 frames is the sweet spot.
- **Skip OpenCV** unless you need numeric sharpness scores without the agent.

> **⚠️ Gemini 3.6 Correction — Frame Density for UI Demos:**
> Grok's 10-frame ladder is correct for long videos and initial triage, but **WRONG for short UI/app demos (< 60s)**. A 10-frame ladder on a 30s demo = 1 frame every 3s. If a medical app shows form fill (3s), tap (4s), and dose popup (5s), a 10-frame ladder misses 2 of those 3 steps.
>
> **The winning compromise:**
> 1. **Dense 1 fps** for short UI/App Demos (< 60s) where every button tap and number matters.
> 2. **Sparse 10-frame ladder** only for long vlogs, scenic clips, or initial triage.

**CLI — Adaptive frame extraction (auto-selects density by duration):**

```bash
# ponytail: Auto-select dense vs sparse based on video length
# Short UI demos (< 60s) → 1fps (every tap matters)
# Long videos (≥ 60s) → percent ladder (10-12 frames total)
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$INPUT")
DUR_INT=$(printf "%.0f" "$DUR")

mkdir -p frames_analysis

if [ "$DUR_INT" -lt 60 ]; then
  # Short demo: dense 1fps — capture every UI state
  ffmpeg -i "$INPUT" -vf "fps=1" -q:v 2 "frames_analysis/sec_%04d.png" -y
  echo "Dense extraction: 1fps for ${DUR_INT}s demo ($(ls frames_analysis/*.png | wc -l) frames)"
else
  # Long video: percent ladder — 10 frames at even intervals
  for pct in 5 10 20 30 40 50 60 70 80 95; do
    T=$(echo "$DUR * $pct / 100" | bc -l)
    ffmpeg -ss "$T" -i "$INPUT" -frames:v 1 "frames_analysis/pct_${pct}.png" -y
  done
  echo "Sparse extraction: 10 frames for ${DUR_INT}s video"
fi
```

**CLI — Contact sheet triage (one-image overview before deep analysis):**

```bash
# ponytail: contact sheet = quick visual triage before committing to dense extraction
ffmpeg -i "$INPUT" -vf "fps=1/5,scale=320:-1,tile=5x4" -frames:v 1 contact_sheet.png -y
echo "Contact sheet generated — review before choosing dense or sparse path"
```

---

## 4) Screen Capture Sharpness (heavy: 2× Xvfb + slow preset always)

| Heavy | What it does | Lighter alternative | Same function? | Why |
|:---|:---|:---|:---|:---|
| Capture 2× then downscale always | Sharp text | Native target res (e.g. 1920×1080) + Chrome --app + scale factor 1 | Same or better UI | Avoids double work; sharp if DPI correct |
| -preset slow always | Smaller files | veryfast/faster + CRF 12–14 for screen | Same sharpness | CRF dominates text quality more than preset |
| Full long tour re-record for every short | Assets | One sharp master → ffmpeg trim/crop variants | Same outputs | One heavy capture, many light cuts |

**Recommendation:**

- **VPS:** 1920×1080 (or 1080×1920) Xvfb, Chrome --app, CRF 12 capture / 14 deliver, preset veryfast–medium.
- **2× capture** only if text still soft after target-res approach.
- **Mac:** native capture + lanczos downscale only when Retina forces it.

**CLI — VPS sharp screen capture (light path):**

```bash
# ponytail: CRF 12-14 dominates text sharpness, not -preset slow
# Start with native res + veryfast — upgrade to 2x only if text still soft
if [ "$(uname -s)" = "Linux" ]; then
  apt install -y fonts-liberation fonts-dejavu-core fontconfig xvfb 2>/dev/null
  fc-cache -fv

  # Native target resolution — no 2x overhead
  Xvfb :99 -screen 0 1920x1080x24 -ac &
  export DISPLAY=:99

  # CRF 12 = sharp text; veryfast = 5-10x faster than slow; same visual quality
  ffmpeg -video_size 1920x1080 -framerate 30 -f x11grab -i :99 \
    -c:v libx264 -preset veryfast -crf 12 -pix_fmt yuv420p \
    -tune stillimage \
    raw_capture.mp4
fi
```

**CLI — Master capture → trim variants (one record, many outputs):**

```bash
# ponytail: Record once, cut many — never re-record for each short
# Master capture → trim clips with -c:v copy (zero re-encode)
ffmpeg -i master_capture.mp4 -ss 3.0 -to 18.0 -c:v copy -c:a copy clip_hook.mp4 -y
ffmpeg -i master_capture.mp4 -ss 8.5 -to 22.0 -c:v copy -c:a copy clip_result.mp4 -y
echo "Trimmed 2 clips from master — zero re-encode, zero quality loss"
```

---

## 5) Encode / Multi-Platform (heavy: U6 full chain, many re-encodes)

| Heavy | What it does | Lighter alternative | Same function? | Why |
|:---|:---|:---|:---|:---|
| U6 everything every time | All formats | One master (16:9 or 9:16) + 1–2 crops | Most publish needs | 80% value, 20% cost |
| Re-encode video for every VO | Mux | -c:v copy + new audio | Same picture | Seconds vs minutes |
| Loudnorm every intermediate | Loudness | Loudnorm once on final | Same delivery | Avoid multi-pass audio thrash |
| Brand grade every export | Look | Grade once on master | Same look | Then crop only |

**Recommendation:**

- **Pipeline:** master sharp → VO mux (video copy) → loudnorm once → optional 9:16 crop.
- **Full multi-platform pack** only when publishing all channels that day.

**CLI — Lean publish pipeline (master → mux → norm → crop):**

```bash
# ponytail: The entire publish pipeline in 4 commands, zero redundant re-encodes

# 1. Mux VO onto graded master — VIDEO COPY (0.5s, not minutes)
ffmpeg -i graded_master.mp4 -i voiceover.wav \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  -af "apad" -shortest \
  muxed.mp4

# 2. Loudnorm ONCE on the final muxed file
ffmpeg -i muxed.mp4 \
  -af "loudnorm=I=-14:LRA=11:TP=-1" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  final_master.mp4

# 3. Crop to 9:16 (if needed) — single re-encode
ffmpeg -i final_master.mp4 \
  -vf "crop=ih*9/16:ih,scale=1080:1920:flags=lanczos" \
  -c:v libx264 -crf 18 -c:a copy \
  final_916.mp4

# That's it. Three files: master, 16:9 normalized, 9:16 crop.
# Full U6 pack only when ALL channels publish the same day.
```

---

## 6) Music / Mix (heavy: complex duck chains)

| Heavy | Lighter | Same function? |
|:---|:---|:---|
| Multi-band duck, long filter graphs | No music for UI demos, or music at fixed low volume (0.15–0.25) | Cleaner product demos |
| Always music+VO | VO-only for clinical/app tours | Better clarity for numbers |

**Recommendation:** Prefer VO-only for medical/app UI; music only for viral shorts, simple volume mix.

**CLI — Simple fixed-volume music mix (when music is wanted):**

```bash
# ponytail: Fixed volume mix — no ducking, no sidechain, no filter graph complexity
# Music at 15-25% is inaudible enough to not compete with VO
ffmpeg -i vo_final.mp4 -i background_music.mp3 \
  -filter_complex "[1:a]volume=0.18[bg];[0:a][bg]amix=inputs=2:duration=first[aout]" \
  -map 0:v -map "[aout]" \
  -c:v copy -c:a aac -b:a 192k -ar 48000 \
  with_music.mp4

# ponytail: For medical/clinical app demos → skip this entirely. VO-only = cleaner.
```

---

## 7) Scene Detection / "Smart Cuts" (heavy: full scene detect + many clips)

| Heavy | Lighter | Same function? |
|:---|:---|:---|
| ffmpeg scene detect whole file | Beat table from vision (agent reads frames) | Categories/splits |
| Auto-split everything | Split 3–5 intentional clips from beat table | Shorts set |

**Recommendation:** Agent beat table > automated scene detect for UI demos (UI changes aren't classic "scenes" — dropdowns, scrolls, and toasts trigger false scene breaks in PySceneDetect).

**CLI — Agent beat table approach:**

```bash
# ponytail: Instead of automated scene detect, agent reads frames and writes a beat table
# This produces better cuts for UI demos than pixel-difference algorithms

# Step 1: Extract sparse frames for agent review
DUR=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$INPUT")
for pct in 10 25 40 55 70 85; do
  T=$(echo "$DUR * $pct / 100" | bc -l)
  ffmpeg -ss "$T" -i "$INPUT" -frames:v 1 "beat_frame_${pct}.png" -y
done

# Step 2: Agent reviews frames, writes beat table (JSON or simple text):
# beat_table.txt format:
#   0.0-3.5  hook       "Splash screen with app logo"
#   3.5-8.0  problem    "Empty form — user hasn't entered data"
#   8.0-13.0 proof      "Result screen showing 250mg dose"
#   13.0-18.0 how       "Detail chart breakdown"
#   18.0-22.0 cta       "Share/Save buttons"

# Step 3: Cut clips from beat table (agent generates these commands)
# ffmpeg -i input.mp4 -ss 0.0 -to 3.5 -c:v copy -c:a copy clip_hook.mp4 -y
# ffmpeg -i input.mp4 -ss 8.0 -to 13.0 -c:v copy -c:a copy clip_proof.mp4 -y
echo "Beat table written — agent cuts intentional clips, not pixel-triggered splits"
```

---

## 8) Side-by-Side "Heavy vs Light" Cheat Sheet

| Job | Avoid (heavy) | Prefer (light) | Output quality |
|:---|:---|:---|:---|
| Captions for TTS VO | Whisper medium | edge-tts VTT / script→SRT | Better alignment |
| Captions for human speech video | Whisper large local | faster-whisper base or Whisper API | Same / good enough |
| Type-1/2 VO | Coqui, always OpenAI TTS | edge-tts neural + rate −8…−12% | Same or better for demos |
| Understand UI video | 1 fps + OpenCV + cloud cascade | 10 frames + agent vision (sparse for long; dense 1fps for <60s demos) | Same for scripting |
| Sharp screen capture | 2× always + slow preset | Target res + --app + CRF 12–14 + veryfast | Same/better if done right |
| Multi-format export | Full U6 always | 1 master + mux copy + 1 crop | Same for main channels |
| QA | Re-render loops forever | Tier-0 + 3 frames + max 3 heals | Safer, lighter |

**Agent rule:** Before starting any pipeline, check this cheat sheet. If the heavy column matches what you're about to do, switch to the light column. Justify in writing if you pick heavy.

---

## 9) Recommended "Light Stack" (same functions as Hybrid heavy path)

```
Agent API (you already have)
  → frames (ladder / contact sheet — dense 1fps for <60s UI demos)
  → scripts + SEO + SRT
edge-tts (VO EN + AR, --write-subtitles for free SRT)
ffmpeg/ffprobe (edit, mux -c:v copy, crop, burn/soft subs, loudnorm)
Optional: faster-whisper base OR Whisper API (only if speech isn't from your script)
Optional: OpenAI TTS (rare EN premium only)
```

**NOT needed for same product outcomes:**
- Coqui/XTTS (heavy GPU TTS — edge-tts covers it)
- local Whisper medium+ (script→SRT or faster-whisper base covers it)
- OpenCV mandatory pipelines (agent vision covers it)
- 1 fps always on long videos (percent ladder covers it)
- 2× capture always (target res + CRF 12 covers it)
- full U6 every job (master + 1 crop covers it)
- complex ducking chains (VO-only or fixed volume covers it)
- PySceneDetect for UI demos (agent beat table covers it)

**CLI — Light stack self-check (run before any pipeline to verify tools):**

```bash
#!/usr/bin/env bash
# ponytail: Verify light stack is ready — run once per environment
echo "=== Light Stack Self-Check ==="

# Required
command -v ffmpeg    >/dev/null && echo "  ffmpeg:        PASS" || echo "  ffmpeg:        FAIL (required)"
command -v ffprobe   >/dev/null && echo "  ffprobe:       PASS" || echo "  ffprobe:       FAIL (required)"
command -v edge-tts  >/dev/null && echo "  edge-tts:      PASS" || echo "  edge-tts:      FAIL (pip install edge-tts)"
command -v bc        >/dev/null && echo "  bc:            PASS" || echo "  bc:            FAIL (apt install bc)"

# Optional (nice to have, not blockers)
python3 -c "from faster_whisper import WhisperModel" 2>/dev/null \
  && echo "  faster-whisper: PASS (optional)" \
  || echo "  faster-whisper: SKIP (install only if STT needed: pip install faster-whisper)"

command -v whisper   >/dev/null \
  && echo "  whisper-cli:   PASS (optional)" \
  || echo "  whisper-cli:   SKIP (faster-whisper preferred)"

echo ""
echo "Light stack ready if ffmpeg + ffprobe + edge-tts all PASS."
echo "Everything else is optional and situational."
```

---

## Gemini 3.6 Senior Dev Review — Section-by-Section Evaluation

**Overall verdict:** The light-stack architectural recommendations are **90% brilliant and highly practical**, with **1 specific area (frame sampling density)** where dense extraction is superior for short UI/App demos.

### Detailed Grades

| Section | Grade | Verdict | Key Insight |
|:---|:---|:---|:---|
| 1) Speech-to-Text / Captions | **10/10** | ADOPT 100% | Running Whisper on audio you just created from a script is pure redundant compute. edge-tts --write-subtitles gives perfect alignment for free in 0.1s. |
| 2) Text-to-Speech (TTS) | **10/10** | ADOPT 100% | Coqui/XTTS requires heavy VRAM, complex Python, CUDA — virtually zero quality gain for app demos. edge-tts runs anywhere without GPU. Single AR voice for mixed AR+EN avoids jarring audio seams. |
| 3) "Understand Video" / Vision | **7/10** | ADAPT | 10-frame ladder correct for long videos. **WRONG for short UI demos.** A 30s demo with 10 frames = 1 frame/3s, missing 2 of 3 critical app states. Keep 1fps for <60s demos. |
| 4) Screen Capture Sharpness | **10/10** | ADOPT 100% | `-preset slow` does NOT make video sharper — preset only affects compression efficiency. CRF controls quality. CRF 12 + veryfast = razor-sharp text in a fraction of encoding time. |
| 5) Encode / Multi-Platform | **10/10** | ADOPT 100% | Re-encoding 1080p just to swap audio takes minutes and degrades quality. `-c:v copy` swaps audio in <0.5s with zero quality loss. |
| 6) Music / Audio Mix | **10/10** | ADOPT 100% | Background music creates cognitive fatigue and obscures spoken numbers in medical demos. Pure VO yields higher conversion and retention. |
| 7) Scene Detection | **9/10** | ADOPT | PySceneDetect triggers false cuts on scrolling lists and dropdown opens. AI vision agent understands logical app steps ("Form Fill", "Result Reveal") better than pixel-difference algorithms. |

### Decision Matrix — How to Treat Each Tip

| Tip | Action | Agent Rule |
|:---|:---|:---|
| Script → SRT directly | **ADOPT 100%** | If VO was generated by TTS → skip Whisper, use VTT/SRT from edge-tts |
| edge-tts as primary TTS | **ADOPT 100%** | Default to edge-tts for ALL templates. No GPU, free, excellent quality. |
| -c:v copy for audio muxing | **ADOPT 100%** | Never re-encode video just to add VO. Always `-c:v copy`. |
| CRF 12–14 + veryfast | **ADOPT 100%** | Sharp screen text without slow encoding. CRF > preset for text quality. |
| VO-only for medical/app demos | **ADOPT 100%** | Keep audio clean and clinical. Music only for viral shorts. |
| 10-Frame Ladder for Vision | **ADAPT** | Use 10-frame ladder for triage/long videos. Keep 1fps for short UI demos (<60s). |
| Agent beat table > scene detect | **ADOPT** | Use agent vision for logical scene boundaries. PySceneDetect = pixel noise on UI. |
| One master + trim variants | **ADOPT 100%** | Record once, trim with `-c:v copy`. Never re-record for each short. |

---

### Light-Stack Pipeline Order (copy-paste checklist for agent)

```
1. PROBE       → ffprobe input (duration, res, fps, audio?)
2. ARCHIVE     → cp raw to raw_archive/
3. PERCEIVE    → Extract frames (1fps if <60s, ladder if ≥60s)
                  Agent reads frames natively (no Python for descriptions)
4. SCRIPT      → Write VO script from frame analysis
5. VO          → edge-tts (AR or EN) + --write-subtitles → .wav + .vtt
6. SRT         → ffmpeg -i captions.vtt captions.srt (no Whisper!)
7. GRADE       → Brand grade on raw video (once, on master)
8. MUX         → -c:v copy + new audio (zero video re-encode)
9. LOUDNORM    → loudnorm once on final muxed file (not intermediates)
10. CROP       → Optional 9:16 crop from 16:9 master (or vice versa)
11. CAPTIONS   → Burn SRT or soft-sub into final
12. QA         → Tier-0 checks + 3 frames at 25/50/75% + mute test
13. EXPORT     → movflags +faststart, CRF 18, H.264
```

**Not in this pipeline (intentionally removed):**
- Whisper (VO came from your script)
- Coqui/XTTS (edge-tts covers it)
- OpenCV pipelines (agent vision covers it)
- PySceneDetect (agent beat table covers it)
- Multi-band ducking (VO-only or fixed volume)
- Multiple re-encodes (one master, mux copy, crop once)

---

*Light-stack tips reviewed and graded by Gemini 3.6 senior dev analysis. Frame density correction (keep 1fps for short UI demos) is the single deviation from Grok's original architecture — adopt everything else as-is. The light stack produces identical output quality with 60-80% less compute, fewer dependencies, and simpler debugging.*
Package copied to clipboard! Ready to paste into AI agent.