vault-sync: 2026-10-18 12:01 (jimmy)

This commit is contained in:
jimmy
2026-10-18 12:01:17 -07:00
parent 181ea8c537
commit 1e8f560d24
43 changed files with 873 additions and 59 deletions
@@ -0,0 +1,21 @@
---
tags: [video-stack, decisions]
updated: 2026-10-16
---
# Decisions
Back to [[Video Stack - Overview]]
| Date | Decision | Reason |
|---|---|---|
| 2026-10-15 | Business voice = **Gemini Kore** (B6), style "friendly, polished product explainer, conversational, upbeat but credible" | User picked B6 from B1-B10 ([[Test Scorecards]]) |
| 2026-10-15 | Political voice = **Gemini Charon** (P1), newscaster style ("measured, authoritative newscaster, confident, serious") | User picked P1; authoritative male fits political ads |
| 2026-10-15 | Local fallbacks: Chatterbox cloning the user's own recording (`C:\tts\voices\user_announcer_ref.wav`) and Qwen3-TTS | Free on the 3060; real human reference beats Kokoro refs |
| 2026-10-15 | **HyperFrames** default renderer, Remotion fallback | Rendered Prop 1 first try in 31 s; one plain HTML file per scene is easier for local models |
| 2026-10-15 | Music ~12 dB under the voice with ducking; final mix -16 LUFS | Voice clarity; consistent loudness |
| 2026-10-15 | User samples and approves voice and music (and script) before any build | Avoid wasted builds; user taste drives choices |
| 2026-10-15 | Political ads: serious civic style, no cheesy stings | User: "This is a political video. The music does not work." |
| 2026-10-15 | Only ONE llama-server on TonyB (Qwen, port 1922) | 12 GB 3060 VRAM; TTS must fit alongside |
| 2026-10-15 | Only tonyjbala Gitea credentials | User rule |
| 2026-10-15 | Each video gets a unique design | Avoid template sameness |
| 2026-10-15 | Political stack kept separate from business stack | Different rules (sourcing, disclaimers, tone) |
| 2026-10-16 | Music = **ACE-Step 1.5** (local, MIT) as primary generator via `C:\tts\acestep_music.py`; code-synth `gen.py` fallback; ElevenLabs Music optional. Music approval gate (Gate 3) unchanged | User: include it in the stack; code-synth music "didn't really do anything for the feel"; ACE-Step is free on the 3060 (~25 s per 45 s track) |
@@ -0,0 +1,353 @@
---
tags: [video-stack, spec]
updated: 2026-10-16
source: explainer-video-stack/STACK.md
---
# STACK.md — Everything Hermes needs to produce Grok-Bot-quality explainer videos
Audience: the Hermes **`explainer`** profile on **TonyB** (Windows 11, RTX 3060 12 GB), running a cheap model (e.g. `gpt-6-luna`).
This is the single source of truth. When another doc disagrees with this one, **this one wins**.
Repos: `tonyjbala/explainer-video-stack` (business explainers) and `tonyjbala/political-explainer-stack` (ballot/political ads).
Worked example to copy: `C:\video\examples\prop1ad` (HyperFrames, 45 s, 9:16).
Last updated: 2026-10-15.
---
## 1. Overview and the quality bar
A "Grok-Bot-quality" video is a **30–90 s explainer or ad (default 45 s, 9:16, 1080x1920)** that passes all six tests:
| # | Bar | What it means in practice |
|---|---|---|
| 1 | **Verified facts** | Every number and claim has an opened, primary source (LAO, official voter guide, SOS, agency data, product docs). Opinion is labelled or phrased as opinion. A short source line sits under every stat on screen. |
| 2 | **Natural VO** | Written for the ear (Section 5.1). No misreads (Whisper-checked), no odd pauses, no read-aloud stage directions. Political = authoritative male (Gemini **Charon**); business = firm, friendly female (Gemini **Kore**). |
| 3 | **Music that adds feel** | Timed to the beats: pulse/tension under the problem or cost, a swell under the CTA, resolves on the cut. A generic bed that "doesn't do anything" is a fail (Prop 1 lesson). |
| 4 | **Kinetic but clean design** | One idea per scene, huge numbers, staggered entrances, a palette made for the topic, nothing inside the platform UI safe zones, no dead half-screens. |
| 5 | **Perfect captions** | Burned-in, 3-word groups, active word highlighted, timed from Whisper word timestamps, spelled correctly ("$500M", "Prop 1", not "Prop.", not "$5"). |
| 6 | **Broadcast loudness** | Music ~12 dB under voice with sidechain ducking; final **-16 LUFS integrated, true peak ≤ -1.5 dBTP**, AAC 192k 48 kHz; last word ends ~2 s before the cut. |
Plus: three approval gates (script, voice, music) **before** any build, and nothing is ever posted or sent by you.
---
## 2. Hardware and software inventory (verified on TonyB 2026-10-15 unless marked)
| Item | Version / path | Notes |
|---|---|---|
| GPU | NVIDIA GeForce RTX 3060, **12288 MiB**, driver 610.88 | Qwen alone uses ~6.4 GB baseline (≈11.5 GB with KV in use). TTS models need 3.9–5.0 GB. |
| LLM | `C:\llama\llama-server.exe`, **Qwen3.8-35B-A3B-Q4_K_M on 127.0.0.1:1922** | The only model the video work assumes. Health: `http://127.0.0.1:1922/health`. |
| Node | **v24.18.0** (HyperFrames needs Node ≥ 22) | |
| HyperFrames | `npx --yes hyperframes@0.8.162 <cmd>` (pinned in `hf\ad\package.json`) | Default renderer. Set `$env:HYPERFRAMES_SKIP_SKILLS="1"` so npx doesn't install its bundled agent skills. Render sends anonymous telemetry: run `npx hyperframes telemetry disable` once. Never run `hyperframes publish` (it uploads). |
| Remotion | `C:\video\template` (16:9 `Main` + 9:16 `MainVertical`, 30 fps) | Fallback renderer only. Copy per project with `xcopy /E /I`. |
| ffmpeg | **9.0.1 essentials (gyan.dev)** on PATH | Mixing, loudness, muxing. |
| Chatterbox | venv **`C:\tts\chatterbox`** (uv, Python 3.11, torch 2.6 cu124) → `C:\tts\chatterbox\Scripts\python.exe`; driver `C:\tts\tts.py` | Local fallback voice; also runs Whisper check (`--check-only`). ~1x realtime, 3.9 GB. |
| Qwen3-TTS | venv **`C:\tts\qwen3tts`** (uv, torch 2.6 cu124, `qwen-tts`) → `C:\tts\qwen3tts\Scripts\python.exe`; 1.7B VoiceDesign + 1.7B Base | Second local option; voice-from-description. Slow (~2.5–5 min per 15–30 s), 4.3–5.0 GB, no flash-attn on Windows. Runner: `C:\tts\vt\vt.py` (env `VREF`/`VTAG`; engines `cb|qwen|qclone`). |
| Whisper | **faster-whisper `small.en`, int8, CPU** | Built into `tts.py`; produces `words.json` `{"1": [[word,start,end],...]}`. |
| Kokoro | kokoro-onnx (box/legacy) | Fast fallback voice for drafts only. **Never use a Kokoro clip as a clone reference** (refs in `C:\tts\voices\kokoro_*_ref.wav` are legacy; don't use them). |
| Human voice ref | `C:\tts\voices\user_announcer_ref.wav` (Anthony, recorded with consent; 20.65 s mono 24 kHz) | The only approved clone reference. |
| Gemini TTS | `C:\tts\gemini_tts.py` (stdlib only; repo copy `scripts/tts/gemini_tts.py`) | **Default voice engine.** Section 2.1. |
| **ACE-Step 1.5** | `C:\Users\fight\Projects\ACE-Step-1.5` (v1.5.0, commit ca1e85f, uv `.venv`, torch 2.7.1 cu128). Checkpoints: `acestep-v15-turbo` (DiT), `acestep-5Hz-lm-0.6B` (+1.7B present, not used on 12 GB), `Qwen3-Embedding-0.6B`. Server: schtasks **`ACEStepServer`** → `start_acestep_api.bat` → Gradio UI + REST API on **http://127.0.0.1:7860** (log `acestep_server.log`). Wrapper **`C:\tts\acestep_music.py`** (repo copy `scripts/music/acestep_music.py`) | **Primary music generator (local, free, MIT).** ~22–30 s per 45 s instrumental track on the 3060. Section 2.2. |
| Music (code, fallback) | `scripts/music/gen.py` (options A–C), `gen2.py` (D–F) in explainer-video-stack; `compose.py` presets in `C:\video\template` | numpy synth, royalty-free. Use only if ACE-Step is down or misses twice. Writes full track + 20 s preview. |
| Music (paid, optional) | `C:\video\template\el_music.py "<prompt>" <seconds> out.mp3` | ElevenLabs Music, needs `ELEVENLABS_API_KEY`; instrumental only. Optional third choice. |
### 2.1 Gemini TTS (`gemini_tts.py`)
```
python C:\tts\gemini_tts.py script.json out.wav [--voice Charon] [--style "..."] [--gap 0.2] [--each DIR] [--whole] [--model gemini-3.8-flash-tts] [--fallback-model gemini-3.8-flash-lite-tts]
```
- `script.json` = JSON list of lines (or a .txt, one sentence per line). Default: one call per sentence, joined with 200 ms gaps. `--each DIR` also saves `DIR\s01.wav…`.
- `--whole` = **one API call for the whole script** (use this by default; it saves quota).
- On a 429 quota error it retries automatically on `gemini-3.8-flash-lite-tts` (`--fallback-model ""` disables).
- **Presets (user-approved Oct 2026):**
- Political (sample P1): `--voice Charon` (default style "measured, authoritative newscaster, confident, serious").
- Business (sample B6): `--voice Kore --style "friendly, polished product explainer, conversational, upbeat but credible" --whole`.
- **Quota (free tier):** flash-tts = **10 requests/day**; flash-lite-tts = 3 requests/minute. A per-sentence political ad can eat a whole day. Paid tier costs about $0.01–0.03/min.
- **Key:** `GEMINI_API_KEY` from the environment. Put `GEMINI_API_KEY=...` in `C:\Users\fight\AppData\Local\hermes\profiles\explainer\.env` (or `setx GEMINI_API_KEY "..."`). Never print it, echo it, or commit it.
- Output: 24 kHz mono 16-bit WAV.
---
### 2.2 ACE-Step 1.5 music (`acestep_music.py`)
- **What:** open-source text-to-music model (ACE-Step 1.5, turbo DiT + 0.6B planning LM). Code and weights are **MIT licensed** (`LICENSE`, `pyproject.toml`, checkpoint model card) → commercial use OK, output is ours. Still: never prompt "in the style of <artist>", never upload copyrighted reference audio.
- **Runs as:** a persistent local server (Gradio UI for humans + REST API for scripts) on `127.0.0.1:7860`, launched with `--enable-api --init_service true --config_path acestep-v15-turbo --lm_model_path acestep-5Hz-lm-0.6B --offload_to_cpu true`. Check: `curl http://127.0.0.1:7860/health` → `"status":"ok"`. If down: `schtasks /Run /TN ACEStepServer` (models load in ~80 s). Do not start a second copy.
- **VRAM coexistence:** it shares the 3060 with the one Qwen llama-server (port 1922). Idle ~8.5–10 GB total, **peak ~11.7 / 12 GB during generation** (CPU offload is on). It is not an LLM, so it does not break the one-model rule, but: never run ACE-Step generation at the same time as Qwen3-TTS or Chatterbox (OOM risk) — generate music, then voice, one job at a time. Never start Gemma or a second llama-server.
- **Command (one track = full + 20 s preview, -18 LUFS mp3):**
```powershell
$AP="C:\Users\fight\Projects\ACE-Step-1.5\.venv\Scripts\python.exe"
& $AP C:\tts\acestep_music.py --prompt "serious civic political ad underscore, cinematic orchestral, low strings, steady timpani pulse, building to a resolute swell, no vocals" --duration 47 --bpm 80 --key "D minor" --out C:\video\myproj\music\optA
# -> optA.mp3 (full, -18 LUFS two-pass loudnorm), optA_preview.mp3 (last 20 s; --preview-start N to choose), optA_raw.wav (use raw/full in the mix)
# options: --seed N (repeatable), --steps 8 (turbo default), --thinking (LM planning; slower), --lufs -18
```
Prints JSON with `gen_seconds`. Always instrumental (sends `lyrics="[Instrumental]"`).
- **Prompt tips (ACE-Step expects comma-separated tags, not sentences):** genre/use, mood, instruments, tempo feel, arc, then `no vocals`. e.g. business: `modern, warm, upbeat corporate tech, light synth pulse, piano, no vocals` (`--bpm 110`); political: as above (`--bpm 70-90`, minor key). Put BPM/key in the flags, not the prompt. Keep it to 6–12 tags. For 3 options at Gate 3, vary the tags (e.g. strings vs piano vs brass) or seeds. It doesn't follow exact timestamps, so pick the take whose swell lands near the CTA, or trim/offset the start so it does. Seconds-level timing still comes from the edit.
- **Measured (2026-10-16):** 45 s track in 22–30 s wall time; output 48 kHz stereo.
## 3. The end-to-end pipeline (with gates)
```
Brief → Research/fact-check → Script (2 variants) → [GATE 1 script]
→ Voice sample → [GATE 2 voice] → Music previews x3 → [GATE 3 music]
→ Full VO + Whisper check → Scene design → HyperFrames build + lint + stills
→ Mix → Final QA → Deliver (9:16 mp4, plus 16:9 re-layout if asked) → [GATE 4 final approval]
```
Project folder: `C:\video\<project>\` with `brief.md`, `vo_review.md`, `script.json`, `vo\`, `music\`, `hf\ad\`, `out\`, `snaps\`.
### Step 1 — Brief → `brief.md`
Topic, audience, length (default 45 s), aspect (9:16 first), sponsor / "Paid for by" name, stance (political), CTA, tone, deadline.
Political: use `political-explainer-stack\templates\brief.md`.
**Done when:** every field filled or explicitly "ask user".
### Step 2 — Research and fact-check → `vo_review.md`
- Open every source you cite. Prefer primary: LAO ballot analysis, CA SOS Quick Guide / Official Voter Guide, UC Berkeley IGS, agency data, the product's own docs.
- Table columns: beat | original line | suggested accurate line | status (✅/⚠️) + why | source URL. Then a "Sources" list.
- Watch for the Prop 1 traps: a figure that applies to only part of the measure ($500–600M/yr is for the $10B part only), "inflation-adjusted" qualifiers, endorsements you cannot verify (EDC GOP's own slate was not online; attribute only to a guide you actually have).
- Label opinion lines ("doesn't change the rules that limit supply").
**Done when:** every number has a URL, every ⚠️ has a fix.
### Step 3 — Script → `script.json` (2 variants)
Rules in Section 5.1. Structure (political, 5 beats): **hook (0–7 s) → breakdown → cost/stakes → concerns/proof → vote call**. Business: hook → problem → how it works → proof/benefit → CTA.
~**105 words max for 45 s** (≤150 wpm, ~2 s spare). One JSON string per beat/scene. Numbers spelled as spoken.
**GATE 1:** send both variants (plain text, with word counts and estimated seconds). Stop until the user picks or edits.
### Step 4 — Voice sample → **GATE 2**
Render line 1 (or the first ~15 s) with the preset voice (Section 4.4). If the user wants options, render 2–3 voices of the same line.
**Done when:** the user approves one voice by name.
### Step 5 — Music previews → **GATE 3**
Write a timed music brief first (e.g. "0–6 s sparse tension; 15–30 s driving low pulse under the cost; 38–45 s resolute swell, resolve on the cut"). Make **3 distinct options** with **ACE-Step** (code synth only as fallback), 20 s previews of the most important section, mp3. One-line description each.
**Done when:** the user picks one (or asks for changes; iterate).
### Step 6 — Full VO + Whisper check → `vo\v1..vN.wav`, `vo\words.json`
Render the whole script with the approved voice, one file per line (or `--whole` then Whisper). Check transcript vs script; re-roll any misread or awkward line.
**Done when:** every line matches (digit-vs-word differences are fine), no pause > ~0.6 s inside a sentence, total VO ≤ length − 2 s.
### Step 7 — Scene design
Pick palette, type, motion for **this topic** (Section 5). Plan one scene per VO line, with start = line start − 0.1 s.
Safe zones 9:16: **top 250 px, bottom 430 px, right 90 px** clear. Content box x 80–910, y 380–1260. Captions at y≈1350.
### Step 8 — HyperFrames build
Scene-per-`<div>` pattern from `prop1ad\build.py`: one Python script writes `hf\ad\index.html` from `words.json` + scene HTML. Lint, snapshot stills at key timestamps, look at them, fix, then render silent.
**Done when:** lint clean, stills at hook / each scene / final 2 s look right, `out\silent.mp4` is exactly the target length.
### Step 9 — Mix
Exact ffmpeg in Section 4.9. **Done when:** I = -16 ±1 LUFS, TP ≤ -1.5 dBTP, voice clearly on top, music audible in gaps and swelling at the CTA.
### Step 10 — Final QA → deliver → **GATE 4**
Run the checklist (Section 4.10). Deliver `out\<project>_45s_vertical.mp4` (H.264 + AAC). If a 16:9 version is wanted, **re-lay out** the scenes for 1920x1080 (new positions, side-by-side panels); never just crop or pad the vertical. Send the path + report to the user; never post it.
---
## 4. Exact commands (PowerShell on TonyB)
### 4.0 Setup per project
```powershell
$P = "C:\video\myproj"
New-Item -ItemType Directory -Force $P,"$P\vo","$P\music","$P\out","$P\snaps" | Out-Null
Copy-Item -Recurse C:\video\examples\prop1ad\hf "$P\hf" # HyperFrames project skeleton
Copy-Item C:\video\examples\prop1ad\build.py "$P\build.py" # then edit scenes/palette
$env:HYPERFRAMES_SKIP_SKILLS = "1"
# Preflight: exactly one llama-server, on 1922
Get-CimInstance Win32_Process -Filter "Name='llama-server.exe'" | % { "$($_.ProcessId) $($_.CommandLine)" }
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
```
Done: one llama-server line (port 1922). If you see more, **report it to the user; don't stop anything yourself.**
### 4.1 Long jobs (anything > ~2 min: Qwen3-TTS, Chatterbox full runs, renders)
Remote-shell children die on disconnect, so use a one-time scheduled task:
```powershell
schtasks /create /tn OneTime_VO /tr "cmd /c C:\video\myproj\run_vo.cmd > C:\video\myproj\vo\log.txt 2>&1" /sc once /st 23:59 /it /f
schtasks /run /tn OneTime_VO
# poll: Get-Content C:\video\myproj\vo\log.txt -Tail 5 (until it prints DONE)
schtasks /delete /tn OneTime_VO /f
```
### 4.4 Voice
```powershell
cd $P
# Political sample (Gemini Charon), line 1 only:
python C:\tts\gemini_tts.py sample.json vo\sample.wav --voice Charon --whole
# Business sample (Gemini Kore):
python C:\tts\gemini_tts.py sample.json vo\sample.wav --voice Kore --style "friendly, polished product explainer, conversational, upbeat but credible" --whole
# Full VO, one file per line (political; costs N requests):
python C:\tts\gemini_tts.py script.json vo\voice.wav --voice Charon --each vo
# Local fallback (no key / quota gone): Chatterbox with the consented human ref, one line per call
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --ref C:\tts\voices\user_announcer_ref.wav --ex 0.55 --cfg 0.4
# Re-roll one bad line:
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --ref C:\tts\voices\user_announcer_ref.wav --ex 0.55 --cfg 0.4 --only 3 --seed 22
```
Whisper check / words.json (works on any WAVs named `v1.wav..vN.wav`; rename Gemini's `s01.wav` → `v1.wav` first):
```powershell
Get-ChildItem vo\s*.wav | % { $n=[int]($_.BaseName.Substring(1)); Copy-Item $_.FullName "vo\v$n.wav" }
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --check-only
```
Done: every HEARD line matches WANT (digits vs words OK). Join lines with 200 ms silence for `vo\voice.wav` if you rendered per line (gemini_tts.py does this already).
### 4.5 Music previews
```powershell
# PRIMARY: ACE-Step (Section 2.2), 3 options with different tags/seeds
$AP="C:\Users\fight\Projects\ACE-Step-1.5\.venv\Scripts\python.exe"
foreach($o in @(@("A","<tags A>, no vocals"),@("B","<tags B>, no vocals"),@("C","<tags C>, no vocals"))){ & $AP C:\tts\acestep_music.py --prompt $o[1] --duration 47 --out C:\video\myproj\music\opt$($o[0]) }
# FALLBACK code-synth options (edit the arc/section times to the brief first)
cd C:\video\myproj\music
python <explainer-video-stack>\scripts\music\gen.py # A–C
python <explainer-video-stack>\scripts\music\gen2.py # D–F
# or presets: $env:MUSIC_SECONDS="47"; C:\tts\chatterbox\Scripts\python.exe C:\video\template\compose.py civic_tension cinematic_build
# or ElevenLabs (if key set): C:\tts\chatterbox\Scripts\python.exe C:\video\template\el_music.py "dark low strings, slow heartbeat pulse, builds to resolute swell at 38s" 47 optA.mp3
ffmpeg -y -i optionA_full.wav -ss 25 -t 20 -af "afade=t=in:d=0.5,afade=t=out:st=19:d=1" -b:a 160k optionA_preview.mp3
```
Music length = video length + ~2 s.
### 4.8 Build, lint, stills, render (HyperFrames)
```powershell
cd $P
python build.py # writes hf\ad\index.html
cd hf\ad
npx --yes hyperframes@0.8.162 lint
npx --yes hyperframes@0.8.162 snapshot --at 1,5,12,24,35,43 -o ..\..\snaps
# LOOK at every PNG in snaps\ (Read tool). Fix, rebuild, re-snapshot only what changed.
npx --yes hyperframes@0.8.162 render -o ..\..\out\silent.mp4 -q delivery
ffprobe -v error -show_entries format=duration:stream=width,height -of csv=p=0 ..\..\out\silent.mp4
```
Done: lint 0 errors; stills OK; 1080x1920; duration = target (e.g. 45.0).
### 4.9 Mix (exact command; same as `scripts/mix.sh`)
```powershell
cd $P
ffmpeg -v error -y -i vo\voice.wav -i music\approved.wav -filter_complex "[1:a]volume=-12dB[m];[0:a]asplit=2[v][sc];[m][sc]sidechaincompress=threshold=0.05:ratio=6:attack=20:release=300[md];[v][md]amix=inputs=2:duration=longest:normalize=0,loudnorm=I=-16:TP=-1.5:LRA=11[a]" -map "[a]" -ar 48000 out\mix.wav
ffmpeg -v error -y -i out\silent.mp4 -i out\mix.wav -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k -ar 48000 -shortest out\myproj_45s_vertical.mp4
ffmpeg -hide_banner -i out\myproj_45s_vertical.mp4 -af ebur128=peak=true -f null - 2>&1 | Select-String "I:|LRA:|Peak:" | Select-Object -Last 3
```
If the VO doesn't start at 0 s, delay it first: `-af "adelay=200|200"` on the voice or build `voice.wav` with the exact line offsets used in `build.py` (`ST` list).
Done: I -16 ±1, Peak ≤ -1.5, listen once end to end.
### 4.10 Final QA checklist
- [ ] Every fact sourced; source line on screen; "Paid for by …" + "Demo, not official" if political/demo.
- [ ] Gates 1–3 approved (script, voice, music).
- [ ] Whisper matches script; listened end to end; no awkward pauses.
- [ ] Captions match audio word for word, 3-word groups, active word highlighted, inside safe zones.
- [ ] Exact duration; last word ends ≥ ~2 s before the cut; music resolves on the cut.
- [ ] -16 LUFS ±1, TP ≤ -1.5 dBTP; music audible but never masking VO.
- [ ] H.264 + AAC, 1080x1920, file name ends in `.mp4`, plays on a phone.
- [ ] Exactly one llama-server (1922) still running; no leftover scheduled tasks.
- [ ] Score the run in `scorecard.md` (copy, voice, music, visuals, speed, cost; 1–5).
---
## 5. Design system
### 5.1 Copywriting (write for the ear)
1. Sentences ≤ 12 words. One idea per line; one line = one breath.
2. Numbers as spoken: "ten billion dollars", "twenty twenty-six", "Prop One". Never two numbers in one sentence if avoidable (Prop 1 line 3 had four → awkward).
3. No comma chains or read-aloud lists; split them.
4. Contractions (it's, don't, you'll). Lead with the stake in the first 2 s.
5. 1 s breath before the CTA. ≤150 wpm; ~105 words per 45 s.
6. Two variants every time. Read aloud (TTS) and time against words.json before designing.
### 5.2 Palette, type, motion — chosen per topic
- Start from the topic's emotion: civic/serious (navy, red, cream), local/warm (greens, sand), tech/fintech (deep ink + one electric accent). One dark base, one light text, **one accent**, one highlight color for the active caption word. Check contrast (white on base ≥ 7:1).
- Type: one heavy sans (`"Liberation Sans", Arial` weight 800–900 for numbers/headlines, 700 for subs). Sizes for 1080x1920: kicker 30 px letter-spaced caps; sub 46 px; hero numbers **128–280 px**; notes/sources 26–28 px (≥26 px).
- Motion (GSAP): each scene has `a1/a2/a3` elements entering at **+0.2 / +0.7 / +1.2 s**, `opacity 0→1, y 40→0, 0.5 s, power2.out`. No bounces or spins for political. Business can add a count-up or slide, still short (≤0.6 s).
- Never reuse Prop 1's look unchanged for another topic.
### 5.3 Captions
Built from Whisper `words.json`: group **3 words**, show from first word start to min(next group start, last word end + 0.4 s); active word tweens to the highlight color (`#FFD34D` in Prop 1) over 0.08 s at its timestamp. 60 px, weight 800, centered, `text-shadow:0 4px 12px #000`, box x 80–910 at y 1350. Clean tokens: "Prop." → "Prop", fix currency splits ("$5"+"00" → "$500"), attach "%"/"." to the previous token.
### 5.4 Kinetic numbers and data callouts
One number per beat, hero size; proportional bars (`$10B` 820 px vs `$1.25B` 103 px); `stat` blocks with a 10 px accent rule; "✕" concern cards (max 3); a source note under each.
### 5.5 End cards
Held ≥ 5 s. Political: big **NO/YES** block in the accent color, "ON PROP N", sponsor, close line, bordered box "Paid for by <exact committee name> · Demo, not official / Source: …". Business: logo/name, one-line value, CTA (URL), no clutter.
### 5.6 Political vs business
| | Political | Business |
|---|---|---|
| Voice | Gemini **Charon**, authoritative male | Gemini **Kore**, firm friendly female |
| Music | dark/urgent or resolute; pulse under cost, swell under vote call | warm/tech pulse, upbeat but not cheesy |
| Look | civic navy/red/cream, flag stripe, no playful motion | topic-fitted brand palette, livelier motion OK |
| Compliance | "Paid for by", demo label, FPPC check before any real use | product claims sourced |
| Repo | political-explainer-stack | explainer-video-stack |
---
## 6. Hard rules
1. **One llama-server only: Qwen on 1922.** Never start Gemma (1923) or any second model alongside. Don't stop or start models without asking the user. (Don't run `C:\llama\restore_llama_servers.bat` — the TonyB copy still starts Gemma too.)
2. **Long runs via one-time `schtasks`**, then delete the task.
3. **Never post, send, publish, upload or share** without explicit approval. No `hyperframes publish`.
4. **Gitea: tonyjbala account only.** Never use Ty / Ty_Tech credentials.
5. **No Kokoro-made clone references.** Clone only from `C:\tts\voices\user_announcer_ref.wav` (consented) or documented public-domain audio. Never clone an identifiable person without written consent.
6. **Keep tokens low:** reuse templates, read files only as needed, stills instead of test renders, regenerate only the broken line.
7. Never print or commit keys (`GEMINI_API_KEY`, `ELEVENLABS_API_KEY`); no media files (mp4/wav/mp3) in git.
---
## 7. Known pitfalls and fixes
| Pitfall | Fix |
|---|---|
| **Blank HyperFrames scenes** — scenes render empty because the GSAP selector doesn't match (e.g. targeting `.a1` without the scene id, or an id that doesn't exist), so elements stay at the CSS start state / are never animated in. | Every scene div needs a unique `id="sN"` + `class="scene clip"` + `data-start`/`data-duration`; animate with `"#sN .a1"`. Register `window.__timelines["main"]=tl` and `tl.set({},{},<duration>)`. Run `lint`, then `snapshot` mid-scene to confirm content is visible. |
| **Gemini reads directions aloud** ("Say in a calm voice: …" gets spoken) | Text = only words to be spoken. Delivery goes in `--style` (sent as `speech_metadata.style`). systemInstruction is rejected. |
| **Gemini quota (429)** — 10/day on flash-tts | Use `--whole`; script auto-falls back to Flash-Lite (3/min). The daily quota resets once a day (observed around 4:50 PM PT). Fallback: Chatterbox with human ref. |
| **Whisper number formatting** ("350,000" vs "three hundred fifty thousand") | Not a misread. Compare normalized; for captions show digits (they read better on screen). |
| **Awkward VO** (odd pauses, flat delivery) | Shorter sentences, fewer numbers per line; one sentence per call; re-roll seed; never a Kokoro reference. |
| **Tight ending** (slower voice runs into the cut) | Budget ~2 s spare; trim words, or speed VO ≤3% (`atempo=1.03`); never cut the last word. |
| **Windows mp4 extension** — Explorer hides extensions, so a file can end up `name.mp4.mp4` or without `.mp4` and won't play on the phone | Always pass an explicit `-o ...\name.mp4`; verify with `Get-ChildItem out` (shows full names) and `ffprobe`. |
| HyperFrames npx installs skills / telemetry | `$env:HYPERFRAMES_SKIP_SKILLS="1"`; `npx hyperframes telemetry disable`. |
| Remote shell jobs die on disconnect | schtasks (Rule 2). |
---
## 8. How a cheap model should work
1. **One small step at a time.** Do one numbered step from Section 3, write its output to a file, then verify it (read the file back, run the check command) before the next.
2. **Use the "Done when" lines and the QA checklist literally.** If a check fails, fix only that thing.
3. **Copy, don't invent.** Start from `prop1ad\build.py`, the templates, and the exact commands above; change values, not structure.
4. **Stop at every GATE** and ask the user in one short message with the files/paths. Don't build ahead of approvals.
5. **Small context.** Don't paste whole files or logs; use `-Tail`, `Select-String`, and stills.
6. **When unsure, ask.** If a source can't be opened, a tool errors twice, or VRAM is short: report it and ask; don't improvise around a rule.
7. **Report at the end:** mp4 path, duration, LUFS/TP, voice + settings, final script, sources, claims softened, scorecard.
---
## Appendix — file map
- `C:\tts\` — `gemini_tts.py`, `tts.py` (Chatterbox + Whisper), `gen3060.py`, `chatterbox\`, `qwen3tts\`, `vt\vt.py`, `voices\user_announcer_ref.wav`
- `C:\video\` — `examples\prop1ad`, `template` (Remotion), `voice_tests` (B1–B10, P1–P4, NOTES.md), `COPYWRITING.md`, `HERMES_PROMPT.md`, `STACK.md` (this file)
- Hermes profile — `C:\Users\fight\AppData\Local\hermes\profiles\explainer\` (`SOUL.md`, `STACK.md`, `.env`, `skills\media\explainer-video\SKILL.md`)
- Obsidian — `C:\Users\fight\Documents\Obsidian Vault\Video Stack\Full Stack Spec.md`
## Prop 39 critique: lessons (2026-10-16)
Source: Hermes's "Yes on 39" 1:38 16:9 draft (`C:\video\prop39`). Length 1:38 was intended — not a defect.
### 1. Captions lost every space ("areyou?Many", "governmentissuedID")
**Root cause:** `C:\video\prop39\build_hf.py` line 35 built word spans separated by a space, but the `.cap` CSS rule (line 171) was `display:flex;justify-content:center`. In a flex container each `<span>` becomes a flex item and whitespace-only text between items is discarded, so the words render glued together.
- [ ] `.cap` is `display:block` (never flex/grid). Center with `text-align:center`.
- [ ] Word spans joined with `' '.join(...)` — real space text nodes.
- [ ] Run `python scripts/qa/check_captions.py hf/ad/index.html vo/words.json` — must print `CAPTION CHECK OK`.
- [ ] Look at 3 stills with captions on screen and read them aloud.
### 2. Safe zones
- 16:9 1920x1080: title-safe 5% → 96px left/right, 54px top/bottom. Nothing (DRAFT tag, logos, header) outside it.
- Captions bottom edge ≥ 12% from the bottom (≥130px at 1080p), so player bars don't cover them.
- 9:16 1080x1920: top 250px, bottom 430px, right 90px are UI zones. Content and captions stay inside.
- [ ] Export stills of first scene, a caption-heavy scene and end card; check against the zones.
### 3. Voice was flat (uniform pauses)
- Political = Gemini **Charon**; business = **Kore**. Steer delivery in `--style`, never in the text.
- Natural punctuation, contractions ("it's", "don't"), mix short and long sentences.
- Vary gaps between sentences 150–400ms (longer before the big point, shorter in lists). Never one fixed gap.
- [ ] Listen to the whole VO once, start to finish, before building visuals.
### 4. Persuasive copy — pick the mode first
- Decide: **neutral explainer** or **advocacy ad**. Write it in the brief and stick to it.
- Advocacy ("Yes on X") argues from line 1. No hedges like "other access concerns remain debated" or "laws vary" — those belong only in neutral explainers.
- Lead with the strongest lines (e.g. "When the process feels loose, people stop trusting it", "A secure election is a fair election").
- Cut procedural scenes (e.g. "best-efforts" data/reporting) that kill momentum.
### 5. Pacing
- 10–12s static slides feel like a deck. Something visible changes every 3–5s: a build, kinetic number, highlight, camera push, or cut.
- [ ] No scene element is static >5s (check the timeline).
### 6. Facts and disclosure
- Every stat (e.g. "36 states", Gallup/Pew numbers) has a source URL in `vo_review.md` / `sources.md`.
- No chart labeled "illustrative" presented as data: use real numbers with citation, or remove.
- A real "Paid for by ..." line replaces any "sponsor disclosure pending" placeholder before publishing.
## Pre-render QA checklist (Hermes must run all, every video)
1. [ ] **Captions:** `check_captions.py` passes; 3 stills read correctly with spaces.
2. [ ] **Safe zones:** stills checked (16:9: 96/54px, captions ≥12% up; 9:16: 250/430/90).
3. [ ] **Voice:** full listen; varied pauses; Whisper check has no misreads.
4. [ ] **Facts:** every number/claim has a source in `vo_review.md`; no "illustrative" data.
5. [ ] **Mode:** brief says neutral or advocacy; no copy contradicts it.
6. [ ] **Pacing:** visual change every ≤5s.
7. [ ] **Disclosure:** real "Paid for by" end card (political); DRAFT tag inside safe zone or removed for final.
Report each item's result to the user before delivering.
@@ -0,0 +1,65 @@
---
tags: [video-stack, lessons]
updated: 2026-10-15
---
# Lessons Learned
Back to [[Video Stack - Overview]]
- A Kokoro-made reference makes Chatterbox clones flat with odd pauses. Use a real human reference (the user's recording worked well; leave more headroom, it peaked at -0.1 dBFS).
- Generate one sentence at a time (best of several seeds, ~200 ms gaps) to avoid strange pauses.
- Copywriting: short spoken sentences, numbers written as spoken words, ~105 words per 45 s, leave ~2 s spare. Number-heavy long lines made Prop 1 sound awkward.
- Gemini reads directions aloud unless they go in the style field.
- Music must add feel: civic_tension and piano B were rejected; option A (dark low strings, slow heartbeat pulse) chosen for Prop 1. If code-composed music misses twice, use a richer generator.
- ElevenLabs free tier was blocked for TTS ("unusual activity"); not retried.
- HyperFrames is easier for local models than Remotion (plain HTML per scene).
- Pick an authoritative male voice for political ads.
## Prop 39 critique: lessons (2026-10-16)
Source: Hermes's "Yes on 39" 1:38 16:9 draft (`C:\video\prop39`). Length 1:38 was intended — not a defect.
### 1. Captions lost every space ("areyou?Many", "governmentissuedID")
**Root cause:** `C:\video\prop39\build_hf.py` line 35 built word spans separated by a space, but the `.cap` CSS rule (line 171) was `display:flex;justify-content:center`. In a flex container each `<span>` becomes a flex item and whitespace-only text between items is discarded, so the words render glued together.
- [ ] `.cap` is `display:block` (never flex/grid). Center with `text-align:center`.
- [ ] Word spans joined with `' '.join(...)` — real space text nodes.
- [ ] Run `python scripts/qa/check_captions.py hf/ad/index.html vo/words.json` — must print `CAPTION CHECK OK`.
- [ ] Look at 3 stills with captions on screen and read them aloud.
### 2. Safe zones
- 16:9 1920x1080: title-safe 5% → 96px left/right, 54px top/bottom. Nothing (DRAFT tag, logos, header) outside it.
- Captions bottom edge ≥ 12% from the bottom (≥130px at 1080p), so player bars don't cover them.
- 9:16 1080x1920: top 250px, bottom 430px, right 90px are UI zones. Content and captions stay inside.
- [ ] Export stills of first scene, a caption-heavy scene and end card; check against the zones.
### 3. Voice was flat (uniform pauses)
- Political = Gemini **Charon**; business = **Kore**. Steer delivery in `--style`, never in the text.
- Natural punctuation, contractions ("it's", "don't"), mix short and long sentences.
- Vary gaps between sentences 150–400ms (longer before the big point, shorter in lists). Never one fixed gap.
- [ ] Listen to the whole VO once, start to finish, before building visuals.
### 4. Persuasive copy — pick the mode first
- Decide: **neutral explainer** or **advocacy ad**. Write it in the brief and stick to it.
- Advocacy ("Yes on X") argues from line 1. No hedges like "other access concerns remain debated" or "laws vary" — those belong only in neutral explainers.
- Lead with the strongest lines (e.g. "When the process feels loose, people stop trusting it", "A secure election is a fair election").
- Cut procedural scenes (e.g. "best-efforts" data/reporting) that kill momentum.
### 5. Pacing
- 10–12s static slides feel like a deck. Something visible changes every 3–5s: a build, kinetic number, highlight, camera push, or cut.
- [ ] No scene element is static >5s (check the timeline).
### 6. Facts and disclosure
- Every stat (e.g. "36 states", Gallup/Pew numbers) has a source URL in `vo_review.md` / `sources.md`.
- No chart labeled "illustrative" presented as data: use real numbers with citation, or remove.
- A real "Paid for by ..." line replaces any "sponsor disclosure pending" placeholder before publishing.
## Pre-render QA checklist (Hermes must run all, every video)
1. [ ] **Captions:** `check_captions.py` passes; 3 stills read correctly with spaces.
2. [ ] **Safe zones:** stills checked (16:9: 96/54px, captions ≥12% up; 9:16: 250/430/90).
3. [ ] **Voice:** full listen; varied pauses; Whisper check has no misreads.
4. [ ] **Facts:** every number/claim has a source in `vo_review.md`; no "illustrative" data.
5. [ ] **Mode:** brief says neutral or advocacy; no copy contradicts it.
6. [ ] **Pacing:** visual change every ≤5s.
7. [ ] **Disclosure:** real "Paid for by" end card (political); DRAFT tag inside safe zone or removed for final.
Report each item's result to the user before delivering.
@@ -0,0 +1,10 @@
---
tags: [video-stack, project]
updated: 2026-10-15
---
# GeoLocal Explainer
Back to [[Video Stack - Overview]]
Status: final, landscape (16:9) + vertical (9:16).
- Music: option 6 "arc" (ElevenLabs Music), ducked, final -16.1 LUFS.
- Remotion composition, 6 scenes. Example in explainer-video-stack `examples/geolocal`.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, stub]
updated: 2026-10-15
---
# MiArc
Back to [[Video Stack - Overview]]
Example subject for business explainers. Repo: https://gitea.vps1.afterthedemo.com/Ty_Tech/MiArc. Details TBD.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, stub]
updated: 2026-10-15
---
# Pocket Change
Back to [[Video Stack - Overview]]
Example subject for business explainers. Details TBD.
@@ -0,0 +1,12 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 1 Ad
Back to [[Video Stack - Overview]]
Status: v2, 45 s vertical, HyperFrames. EDC GOP "Vote No on Prop 1".
- Voice: male am_michael-based voice; music A (dark low strings, heartbeat pulse).
- Feedback: visuals good; voice awkward with the script; copy needs refinement.
- Next: re-voice with Gemini Charon and tighter copy (~105 words).
- Example: `C:\video\examples\prop1ad`.
@@ -0,0 +1,11 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 38 Ad
Back to [[Video Stack - Overview]]
Status: v2 + vertical, EDC GOP demo (not official).
- Voice: Chatterbox with Kokoro am_michael ref (speed 1.05).
- Music: ElevenLabs Music, 45 s; ~11.6 dB under voice; final -16.1 LUFS, 45.1 s.
- Example: `examples/prop38ad` in both repos.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 38 Explainer
Back to [[Video Stack - Overview]]
Status: done (earlier Voting Guide Series explainer). Brief kept as an example in the repos. Details TBD.
@@ -0,0 +1,16 @@
# Prop 39 Ad (Yes on 39, voter ID)
- **Made by:** Hermes (explainer profile), Oct 2026
- **Files:** `C:\video\prop39` (HyperFrames build `build_hf.py` -> `hf\ad\index.html`; deliveries in `out\delivery`)
- **Format:** 16:9, 1:38 (intended length), also vertical variants
- **Status:** draft - needs fixes before publishing
## Critique (2026-10-16)
- Captions missing spaces - root cause `.cap` was `display:flex` (build_hf.py line 171). Fixed in project (backup `build_hf.py.bak-20261016`) and stack templates; needs rebuild + re-render.
- DRAFT tag at frame edge; captions too low (bottom:40px -> now 130px).
- Flat voice with uniform pauses.
- Copy reads neutral until the end; drop hedges ("other access concerns remain debated", "laws vary"); cut scene 5 (best-efforts data/reporting); build around "When the process feels loose, people stop trusting it" and "A secure election is a fair election."
- Source "36 states" and Gallup/Pew; drop "illustrative" chart or cite real data; replace "sponsor disclosure pending" with a real Paid for by line.
See [[Lessons Learned]] -> Prop 39 critique: lessons.
@@ -0,0 +1,35 @@
---
tags: [video-stack, scorecard]
updated: 2026-10-15
---
# Test Scorecards
Back to [[Video Stack - Overview]] - details in `C:\video\voice_tests` / NOTES.md. All clips ~-16 LUFS, Whisper-checked with no misreads.
## Political (Prop 1 opening) - pick: **P1**
| ID | Engine / voice | Gen time |
|---|---|---|
| **P1** | Gemini flash-tts, Charon, newscaster style | 6.4 s |
| P2 | Qwen3-TTS VoiceDesign, "deep, authoritative American male political ad narrator" | 145 s |
| P3 | Chatterbox, user ref | 86 s |
| P4 | Qwen3-TTS clone, user ref | 161 s |
## Business (GeoLocal paragraph) - pick: **B6**
| ID | Engine / voice | Gen time |
|---|---|---|
| B1 | Chatterbox, user ref | 130 s |
| B2 | Qwen3-TTS VoiceDesign, warm American male narrator | 229 s |
| B3 | Kokoro am_michael | ~7 s (CPU) |
| B4 | Qwen3-TTS clone, user ref | 304 s |
| B5 | Gemini Flash-Lite, Achird (male) | API |
| **B6** | Gemini Flash-Lite, Kore (female, "Firm") | API |
| B7 | Gemini Flash-Lite, Algieba (male) | API |
| B8 | Gemini Flash-Lite, Zephyr (female) | API |
| B9 | Qwen3-TTS VoiceDesign, young female tech narrator | 280 s |
| B10 | Qwen3-TTS VoiceDesign, relaxed male podcast host | 266 s |
## Stack comparison template (score 1-5)
| Date | Video | Producer | Copy | Voice | Music | Visuals | Speed | Cost | Notes |
|---|---|---|---|---|---|---|---|---|---|
| | | Grok Bot | | | | | | | |
| | | Hermes explainer | | | | | | | |
| | | Regis | | | | | | | |
@@ -0,0 +1,39 @@
---
tags: [video-stack, setup]
updated: 2026-10-16
---
# Tools and Setup
Back to [[Video Stack - Overview]]
## Paths on TonyB
- `C:\tts\tts.py` - Chatterbox voice script (`--ref <wav>` for cloning)
- `C:\tts\gemini_tts.py` - Gemini TTS; `--style` for directions, `--whole` (whole script in one request), `--fallback-model` (switch to Lite when the daily limit is hit)
- `C:\tts\qwen3tts` - Qwen3-TTS venv (1.7B VoiceDesign / Base clone)
- `C:\tts\chatterbox` - Chatterbox venv
- `C:\tts\voices\user_announcer_ref.wav` - user's own announcer recording (20.65 s, mono 24 kHz)
- `C:\tts\vt\vt.py` - voice-test runner
- `C:\video\template` - video template
- `C:\video\HERMES_PROMPT.md`, `C:\video\COPYWRITING.md`
- `C:\video\examples\prop1ad` - worked example
- `C:\video\voice_tests` - bake-off samples
- Hermes profile: `C:\Users\fight\AppData\Local\hermes\profiles\explainer` - start with `explainer chat`
## GPU (RTX 3060, 12 GB)
- Qwen llama-server on :1922 uses ~6.4 GB baseline. Never start Gemma (:1923) alongside it.
- Peak VRAM seen: Chatterbox 3.9 GB, Qwen3-TTS VoiceDesign ~4.3-4.5 GB, Qwen3-TTS clone 5.0 GB. Run TTS models one at a time.
- Long runs: one-time scheduled tasks (`schtasks /create ... /sc once`, `/run`), delete afterwards.
## Gemini
- Free tier: 10 requests/day on flash-tts (resets ~4:50 PM PT); flash-lite-tts 3/min. Script auto-falls back to Lite.
- Style must go in `speech_metadata.style` (interactions endpoint), never in the text.
- `GEMINI_API_KEY` goes in the explainer profile `.env` (user step; not confirmed done).
## Music: ACE-Step 1.5 (primary, added 2026-10-16)
- Install: `C:\Users\fight\Projects\ACE-Step-1.5` (v1.5.0, commit ca1e85f, `.venv`). License **MIT** (code + weights): commercial use OK.
- Models: `acestep-v15-turbo` DiT + `acestep-5Hz-lm-0.6B` (1.7B LM also on disk, unused).
- Server: scheduled task `ACEStepServer` -> `start_acestep_api.bat` -> Gradio UI + REST API at http://127.0.0.1:7860 (`/health`, `/release_task`, `/query_result`). Log: `acestep_server.log`. Restart: `schtasks /Run /TN ACEStepServer` (~80 s to load). Does not start at boot.
- Wrapper: `C:\Users\fight\Projects\ACE-Step-1.5\.venv\Scripts\python.exe C:\tts\acestep_music.py --prompt "<tags>, no vocals" --duration 47 [--bpm N --key "D minor" --seed N] --out <path-no-ext>` -> full mp3 + 20 s preview at -18 LUFS.
- Speed: 22-30 s per 45 s instrumental. VRAM: shares the 3060 with Qwen (1922); peak ~11.7/12 GB during generation (CPU offload on). Don't run it at the same time as Qwen3-TTS/Chatterbox.
- Test tracks: `C:\video\music\acestep\acestep_business.mp3`, `acestep_political.mp3`.
- Fallbacks: code-synth `scripts/music/gen.py`/`gen2.py`; ElevenLabs `el_music.py` (optional, paid).
@@ -0,0 +1,28 @@
---
tags: [video-stack, moc]
updated: 2026-10-15
---
# Video Stack - Overview
## Goals
- Polished explainer and ad videos, with 9:16 vertical shorts as the main format (16:9 masters when needed).
- Political **Voting Guide Series** (EDC GOP) kept separate from business explainers.
- Find the best stack per purpose by scored test runs (see [[Test Scorecards]]).
## Split workflow
- **Grok Bot** designs the workflow, templates, and prompts.
- The **Hermes "explainer" profile** on TonyB (`explainer chat`) does the production work on cheap inference (`gpt-6-luna`).
- Regis (Meta Muse agent) can be given the same brief for head-to-head comparisons.
## Stacks
- Business: [explainer-video-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/explainer-video-stack) (GeoLocal, MiArc, Pocket Change type subjects)
- Political: [political-explainer-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/political-explainer-stack) (templated brief/script, sourcing, "Paid for by" card)
- Older: [video-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/video-stack) (first version, holds the original HERMES_PROMPT)
## Notes
- [[Full Stack Spec]] — the complete STACK.md (tools, pipeline, commands, rules)
- [[Decisions]]
- [[Tools and Setup]]
- [[Lessons Learned]]
- [[Test Scorecards]]
- Projects: [[GeoLocal Explainer]], [[Prop 38 Explainer]], [[Prop 38 Ad]], [[Prop 1 Ad]], [[MiArc]], [[Pocket Change]]
@@ -0,0 +1,35 @@
---
type: project
status: active
date: "2026-10-18"
tags: [project, video-stack]
---
# Video Stack — Status
## What it is
Tony's video production program: 9:16 explainer/ad shorts (16:9 masters on demand), split
into two separate stacks — a **business** explainer stack (GeoLocal, MiArc, Pocket Change
type subjects) and a **political** "Voting Guide Series" (EDC GOP). Relocated from a
root-level `Video Stack/` folder into `04_projects/` during the 2026-10-18 defrag.
## Current state
- **Outputs:** 6 short projects — [[GeoLocal Explainer]], [[Prop 38 Explainer]], [[Prop 38 Ad]], [[Prop 1 Ad]], [[MiArc]], [[Pocket Change]]
- **Voice picks (2026-10-15):** business = Gemini **Kore** (B6); political = Gemini **Charon** (P1). See [[Test Scorecards]].
- **Renderer:** HyperFrames default, Remotion fallback.
- **Music:** ACE-Step 1.5 (local, MIT) primary; voice and music gates before build.
- **Workflow split:** Grok Bot designs workflow/templates/prompts; Hermes "explainer" profile produces on cheap inference; Regis for head-to-head comparisons.
## Key notes
- [[Video Stack - Overview]] — program MOC
- [[Full Stack Spec]] — complete STACK.md (tools, pipeline, commands, rules)
- [[Decisions]] — dated decisions with rationale
- [[Tools and Setup]]
- [[Lessons Learned]]
- [[Test Scorecards]] — scored stack/voice comparisons
## Next actions
- _TBD — confirm with Tony whether Video Stack stays a project or becomes its own area._
## Related
- [[MOC/Index|Master Index]] · [[index|Vault Index]]