# Pipeline de Videos AgentSquad — guion → publicación

> Guion → VO → Avatar → Composición → Packaging → Publicación → Métricas. Inventario verificado en disco el 2026-07-04 (6 agentes en paralelo, 6/6 áreas OK) · consolidado 2026-07-05: los hallazgos del barrido están implementados y verificados (sección al final); métricas LATAM fuera de alcance por decisión. Canal EN: @AgentsSquad-c6p · LATAM: @agentsquadlatam.

**KPIs:** 9 etapas · 4 skills clave · 29 reglas duras · 12 capacidades consolidadas


---

## Las 9 etapas

### 1. Ideación & Grounding
_nunca ideas en frío — entrevista + corpus real_

**Skills:** `viral-video-creator`

**Componentes:**
- `Step 0 INTERVIEW` — mandatorio: 3-4 preguntas a Roberto; ideas SOLO desde sus respuestas
- `rejected-ideas.md` — gate: leer ANTES de proponer; anexar cada rechazo con fecha+razón
- `trending-research` — descubrimiento broad cross-platform cuando no hay tema

**Reglas duras:**
- Checklist REX de 8 criterios antes de escribir — mínimo 6/8 o se optimiza la idea
- Nunca re-proponer ángulos similares a los rechazados

### 2. Scout & Library
_modelar winners validados del nicho, no intuición_

**Skills:** `viral-video-creator › scout`

**Componentes:**
- `scout.scout "TEMA"` — Apify multi-plataforma (YT/TikTok/IG) → filtros → embeddings → library.db (SQLite + sqlite-vec)
- `scout.library "TEMA"` — búsqueda semántica top-K winners + transcripts → playground HTML
- `Briefing de patterns` — sin CLI: Claude lee library.db directo y diseca hooks/estructura/CTAs

**Reglas duras:**
- scout SIEMPRE con --dry-run primero (no gastar Apify)
- No saltar fases: scout → library → briefing → guion

**Consolidado 05-jul:**
- ✓ Biblioteca de winners viva: 49 videos, embeddings gratuitos Cloudflare Workers AI bge-m3 (multilingüe ES↔EN), ingesta Apify reproducible

### 3. Guion
_REX + frameworks + reglas duras de copy_

**Skills:** `viral-video-creator`

**Componentes:**
- `REX Core + extensión` — 5 principios, 4 hooks (comunican LO MISMO), 13 estructuras narrativas short/long
- `Channel check` — AI4Managers → formato fijo 'Señales de IA' (5 bloques, CTA inmodificable)
- `script-methodology` — anti-cliché blacklist · enemy naming · momento ajá aislado · CTA invisible · 4 textos THUMB
- `scout/gates.py` — word_to_duration + dialogue_confirmation ANTES de TTS (38 tests)

**Reglas duras:**
- POV obligatorio: a qué se opone, qué apoya, en qué cree, qué experiencias lo validan — nunca cerrar en 'no lo sé'
- Español LATAM neutro — sin voseo
- En VO ES escribir 'inteligencia artificial', nunca la sigla
- NUNCA nombrar cantidad de agentes ni tenure específico
- Nunca fabricar stats/montos/credenciales

**Consolidado 05-jul:**
- ✓ LATAM-neutro, POV de Roberto y no-contar-agentes son bloqueantes del checklist 3e del SKILL.md

### 4. Voice-Over (ElevenLabs)
_single-take eficiente + QA word-level_

**Componentes:**
- `gen_efficient.py` — guiones ≤160 palabras: UN take, factura chars reales
- `make_vo.py + lib/vo.py` — 200+ palabras: generate_long (tramos ≤40 + stitching + voqa por tramo)
- `personas/roberto.json` — voz RobertoDefinitivo · speed 1.12 · respell marca → 'Ei Ai for Mánashers'
- `lib/voqa.py` — faster-whisper int8: alineación ≥0.97, sin gibberish, sin palabras >1.2s

**Reglas duras:**
- Receta de voz LOCKEADA (stability .5 / similarity .9 / style .2) — único knob = speed; prohibido atempo
- voqa obligatorio en CADA VO (whisper-base laxo dejó pasar gibberish 2 veces)
- voqa es ES-only; no usar make_vo para VO en inglés

### 5. Avatar & B-rolls
_router de identidad: HeyGen · MuAPI · Higgsfield_

**Skills:** `prompt-video-avatar`

**Componentes:**
- `HeyGen (CLI + MCP)` — ÚNICO que preserva rostro real — Avatar V + motion prompt open-palm, pose final
- `MuAPI` — b-roll no-persona: Hunyuan $0.15 motion sutil · gemini-omni para escena con figura humana
- `Higgsfield (CLI)` — cámara cinemática/VFX + Soul ID Camila (receta 2 etapas)
- `avatar_qa.py` — grid cara+manos tras CADA render HeyGen, ANTES de componer

**Reglas duras:**
- Rostro de persona = HeyGen SIEMPRE; MuAPI/Higgsfield reinterpretan la cara
- calculate_dynamic_cost ANTES de cada submit MuAPI
- Batch generativo = 1 a la vez: submit → review → adopt/redirect → next
- No doble persona: b-rolls de un talking-head son NO-persona

**Consolidado 05-jul:**
- ✓ avatar_qa.py post-render es regla global del SKILL router (nunca componer sobre material defectuoso)
- ✓ Router cubre gemini-omni para escenas no-persona con figura humana (9:16 nativo, manos limpias)

### 6. Composición & Captions
_la cadena de build del short final_

**Componentes:**
- `lib/compose.py` — cadena C1: avatar 1080×1920 → b-roll cards (banner via insightface) → captions → hookcard → outro
- `compose_simple.py` — sistema dual AI4M: caja sans + serif PP Editorial dinámico, push-in hook, scrim
- `3 SPECs (broll-style/)` — caption-system AI4M · premium-editorial (Playfair + oro) · dynamic-overlays (alpha MOV)
- `Validación` — frames reales libass (nunca Playwright) + screenshots del video FINAL antes de 'listo'

**Reglas duras:**
- Safe-zones Shorts: x<936, y<1632; captions nunca pisan la cara
- Wrap balanceado SIN truncar palabras (verificación 0 drops)
- Audio: loudnorm 2-pass -14 LUFS POR SEGMENTO · multi-clip con volume=XdB medido · +faststart siempre
- Text-behind solo con palabras inferibles por los extremos
- Lip-sync: UN comando FFmpeg con split+enable — prohibido concat y -ss antes de -i
- Entrega al usuario vía Cloudflare Stream (bypassa cache)

**Consolidado 05-jul:**
- ✓ Captions dual-style con SSOT: canon calibrado 1075/760/20 en la SPEC y en compose_simple.py; snapshots de entregas congelados
- ✓ Dos estilos de captions vivos y documentados: C1 (cron shorts AgentSquad) y dual-style AI4M — roles aclarados en lib/captions.py
- ✓ Entrega estándar scriptada: lib/cfstream.py sube a Cloudflare Stream con CLI — verificado E2E

### 7. Packaging
_thumbnail + título — la JuntaYT decide (invocación automática)_

**Skills:** `youtube-channel-advisor`

**Componentes:**
- `pikzels-thumbnail` — persona Roberto entrenada + --reference + --score (CTR subscores) + faceswap
- `nano-banana-2-edit` — edición con 2 imágenes de referencia vía MuAPI (prompt-library)
- `JuntaYT` — Galloway · vidIQ · Eloisa Wolf · Kallaway · Aprilynne Alter — todo título pasa por acá. Protocolo 05-jul: se invoca AUTOMÁTICA y se ejecuta su mejor recomendación (sin aprobación manual)
- `_validate_title` — gate determinista horneado en voc-shorts: keyword + no-verbatim-hook + sin clickbait

**Reglas duras:**
- Título: Ask-YT (pregunta real) + keyword + complementa el hook + límite chars + sin clickbait ni stats inventadas
- Thumbnail NUNCA exhibe la duda — vende fuerza; el claim honesto va en el guion
- Short de persona: frame con CONTACTO VISUAL + card del cartel (hookcard) sobre el pecho
- Nunca grid contable de agentes — cluster difuminado

**Consolidado 05-jul:**
- ✓ Gate de título ≤60 chars horneado en código (_validate_title, reglas JuntaYT) — 80 tests
- ✓ Pikzels --score es gate obligatorio de thumbnails (≥7 pasa; <7 itera u override registrado de la JuntaYT)

### 8. Publicación
_OAuth por canal, upload idempotente_

**Componentes:**
- `lib/publish.py` — gate status=composed → upload resumable → video_id persistido ANTES del pin-comment (idempotencia)
- `Tokens por canal` — AGENTSSQUAD (doble S, EN) · AI4MANAGERS · THALX (LATAM @thalx-ai)
- `Canales` — EN: UC1aFO4qVYm_l8hZBNCHMaSQ · LATAM: UChKr79TW5giBsYNGL9t0SCw (7 Shorts ES movidos)

**Reglas duras:**
- Shorts ES van al canal LATAM; AgentSquad EN solo inglés (evita canibalización)
- NO confundir @thalx-ai con @thalx (junk, sin token)

**Consolidado 05-jul:**
- ✓ Publicación multi-canal: el canal viaja en spec.json (agentsquad/ai4m/latam), default EN
- ✓ Thumbnail y playlist se setean en el mismo publish (con retry, idempotencia preservada)

### 9. Métricas (loop mensual)
_Analytics API → Telegram + YouTube Brain_

**Componentes:**
- `monthly.py` — cron día 1: views, AVD, retención con flags 🟢🟡🔴, snapshot mes-a-mes → Telegram
- `analytics_to_brain.py` — puente → YouTube Brain (Agrici): scorecard source-cited contra bandas oficiales
- `ai4m-lofi metrics_check` — seguimiento discovery del livestream cada 4 días

**Reglas duras:**
- CTR e impresiones son Studio-only — la API no las expone; PROHIBIDO fabricar el número

**Consolidado 05-jul:**
- ✓ Métricas LATAM fuera de alcance (decisión 05-jul) — código listo por si se retoma
- ✓ Crontab limpio: sin jobs vencidos (ab_check retirado)

---

## Data flow del pipeline (diagrama)

_De la idea al video publicado en YouTube — 9 etapas, QA gates en cada transición · consolidado 2026-07-05 (12 capacidades implementadas)_

**Etapas del flujo:** 1. Ideación & Research → 2. Guion → 3. Assets → 4. Ensamblaje → 5. Distribución

**Nodos:**

| Etapa | Nodo | Detalle |
|---|---|---|
| Ideación & Research | `Roberto` | INTERVIEW Step 0 · tema real |
| Ideación & Research | `Scout` | Apify multi-plataforma |
| Ideación & Research | `library.db` | bge-m3 · CF Workers AI · 49 winners |
| Guion | `Guion REX` | 13 estructuras + gates.py · LATAM neutro |
| Assets | `VO ElevenLabs` | gen_efficient.py · voqa >=0.97 |
| Assets | `Avatar router` | HeyGen / MuAPI / Higgsfield · avatar_qa.py |
| Assets | `B-rolls AI` | Sora 2 / Seedance |
| Ensamblaje | `Composición` | compose.py · FFmpeg · -14 LUFS |
| Ensamblaje | `Packaging` | Pikzels + JuntaYT · titulo <=60c |
| Distribución | `publish.py` | multi-canal · spec.json · idempotente |
| Distribución | `YouTube` | @AgentsSquad-c6p |
| Distribución | `Métricas` | monthly.py + YT Brain · CTR Studio |

**Flujos (qué artefacto viaja entre nodos):**

- `Roberto` → `Scout` — **tema grounded** _(experiencia real)_
- `Scout` → `library.db` — **winners filtrados** _(views/edad)_
- `library.db` → `Guion REX` — **briefing patterns** _(top-5 semántico)_
- `Guion REX` → `VO ElevenLabs` — **guion validado** _(gates.py OK)_
- `Guion REX` → `B-rolls AI` — **prompts b-roll** _(1-by-1, cost gate)_
- `VO ElevenLabs` → `Avatar router` — **VO aprobado** _(QA >=0.97)_
- `Avatar router` → `Composición` — **clips avatar QA** _(sin deformidades)_
- `B-rolls AI` → `Composición` — **b-rolls fullscreen** _(premium style)_
- `Composición` → `Packaging` — **master MP4** _(+faststart)_
- `Packaging` → `publish.py` — **thumb + título** _(gate JuntaYT)_
- `publish.py` → `YouTube` — **video_id** _(por canal)_
- `YouTube` → `Métricas` — **CTR / AVD** _(loop mensual)_

**QA gates (bloquean avance):**
- VO: similitud faster-whisper >= 0.97 antes de avanzar
- Avatar: avatar_qa.py en CADA render (deformidades)
- Título: JuntaYT automática — se ejecuta su mejor recomendación, <= 60c
- Video final: verificado con screenshots antes de entregar

**Reglas duras de contenido:**
- Español LATAM neutro, sin voseo; 'inteligencia artificial' en VO
- NUNCA nombrar cantidad de agentes ni tenure
- Safe-zones vertical: texto en x<936 / y<1632
- Audio: loudnorm -14 LUFS, normalización por clip

**Loop de aprendizaje:**
- Métricas mensuales alimentan la siguiente ideación
- CTR solo de YouTube Studio (API no lo expone)
- YouTube Brain guarda aprendizajes por video

**Consolidado 2026-07-05:**
- Embeddings gratuitos: Cloudflare Workers AI bge-m3 (multilingüe ES/EN)
- Publicación multi-canal: agentsquad · ai4m · latam, con thumbnail y playlist
- Entrega estándar scriptada: lib/cfstream.py → Cloudflare Stream (E2E)
- Métricas LATAM fuera de alcance por decisión (código listo si se retoma)

---

## Stack complementario · Thalx Remotion (AWS Lambda)

Vía de render programático React paralela al pipeline FFmpeg. Estado: REPARADO 2026-07-04 · VERSIONADO Y PUSHEADO — fiber 8→9 (React 19 OK), drei muerto desinstalado, PixelOffice aislado en su propio site (`thalx-pixeloffice`). Verificado con renders E2E reales en Lambda: MotionGraphic (site principal) + PixelOffice 3D (site R3F), ambos descargados y frame-checked. Fix en git: commits `1167e33` (split de bundles + fiber 9, 6 archivos) y `f3cac31` (lambda_render.mjs versionado) en `aguirrerjg/thalx-web-app` master.

**Infra:** Lambda us-east-1 `remotion-render-4-0-434…` (3008MB reales · 600s reales — el nombre miente) · 2 sites S3: `thalx-remotion` (11 comps sin R3F) y `thalx-pixeloffice` (R3F aislado) · invocadores `lambda_render.mjs` / `render_pixel_office.mjs` · deploy reproducible `remotion/deploy.mjs` (check · sites · function).

**Regla permanente:** composiciones R3F/Three van a `index-pixeloffice.ts`, NUNCA a `Root.tsx` — el import estático contamina el bundle entero (incidente 2026-04-16, reparado 2026-07-04).

### Arquitectura en detalle (del diagrama archify)

_Video programático React (Remotion 4.0.434) · split de bundles para aislar R3F · reparado 2026-07-04_

**Componentes:**

| Componente | Rol |
|---|---|
| `Dev / Claude Code` | repo thalx-web-app |
| `deploy.mjs` | deploySite / deployFunction · IaC versionado |
| `lambda_render.mjs` | renderMediaOnLambda · retry + presign |
| `render_pixel_office.mjs` | 3D · formats |
| `site thalx-remotion` | 11 comps sin R3F |
| `site thalx-pixeloffice` | PixelOffice · fiber 9.6.1 · R3F aislado |
| `Lambda render` | 4-0-434 · 3008MB · 600s · us-east-1 |
| `S3 renders` | remotionlambda-useast1-… · presignUrl |

**Flujos:**

- `Dev / Claude Code` → `deploy.mjs` — _bundle + upload_
- `Dev / Claude Code` → `lambda_render.mjs` — _render job JSON_
- `Dev / Claude Code` → `render_pixel_office.mjs` — _render 3D_
- `deploy.mjs` → `site thalx-remotion` — _src/index.ts_
- `deploy.mjs` → `site thalx-pixeloffice` — _index-pixeloffice.ts_
- `lambda_render.mjs` → `Lambda render` — _renderMediaOnLambda_
- `render_pixel_office.mjs` → `Lambda render` — _serveUrl pixeloffice_
- `Lambda render` → `site thalx-remotion` — _carga bundle_
- `Lambda render` → `site thalx-pixeloffice` — _carga bundle 3D_
- `Lambda render` → `S3 renders` — _MP4 h264_

**Boundaries:** region: «AWS us-east-1» (sitemain, sitepx, lambda, s3out) · security-group: «R3F en cuarentena» (sitepx)

**Por qué DOS sites (incidente 2026-04-16):**
- El import estático de @react-three/fiber 8 (reconciler React 18) con React 19 crasheaba TODO el bundle en la inicialización
- Fix 2026-07-04: fiber 9.6.1 + PixelOffice movido a su propio entry (index-pixeloffice.ts) y site aparte
- Regla permanente: composiciones R3F/Three NUNCA van a src/Root.tsx

**Contratos de versión:**
- @remotion/lambda DEBE coincidir con la función desplegada (remotion-render-4-0-434-…)
- El nombre miente: dice mem2048mb/240sec pero real es RAM 3008MB / timeout 600s / disk 2048MB
- Tras cambios: node deploy.mjs check | sites | function (IaC versionado)

**Estado y verificación:**
- E2E verde 2026-07-04: MotionGraphic (site principal) y PixelOffice 3D renderizados en Lambda y frame-checked
- Versionado: commits 1167e33 (split + fiber 9) y f3cac31 (lambda_render.mjs) en GitHub
- IaC: deploy de sites/función versionado en remotion/deploy.mjs (check | sites | function)

---

## Capacidades implementadas · consolidado 2026-07-05

- **✓** **Packaging con JuntaYT automática.** La Junta (Galloway · vidIQ · Eloisa Wolf · Kallaway · Aprilynne) se invoca sola ante cualquier título y se ejecuta su mejor recomendación — así se retituló en vivo el video #3: “What Happens When AI Agents Run Your Office?” (44c) con descripción purgada de cantidades.
- **✓** **Captions AI4M con fuente única de verdad.** El canon calibrado (**CAP_Y 1075 · serif 760 · Box maxc 20**) vive en la SPEC y está horneado en `compose_simple.py`; los videos ya entregados quedaron como snapshots congelados con header.
- **✓** **Publicación multi-canal idempotente.** `publish.py` lee canal/thumbnail/playlist del `spec.json` de cada short (agentsquad · ai4m · latam), setea thumbnail y playlist con retry DESPUÉS de persistir video_id, y un re-run nunca re-sube. 80 tests.
- **✓** **Entrega estándar scriptada.** `lib/cfstream.py` sube a Cloudflare Stream (multipart + poll readyToStream + download habilitado + delete; CLI incluido) — verificado E2E real.
- **✓** **Gate de título ≤60 chars en código.** `_validate_title` hornea las reglas JuntaYT (Ask-YT + keyword + sin clickbait + no duplicar el hook) — 80/80 tests.
- **✓** **Biblioteca de winners viva y gratuita.** 49 videos con embeddings de Cloudflare Workers AI **bge-m3** (1024d, free tier, multilingüe): un query en español encuentra winners en inglés. Cliente robusto ante transcripts >8k tokens; fallback `EMBEDDINGS_PROVIDER=openai` disponible.
- **✓** **Dos sistemas de captions vivos y documentados.** C1 (caja blanca + keyword rojo — cron de shorts AgentSquad vía `lib/compose.py`) y dual-style AI4M (SPEC) — roles aclarados en `lib/captions.py`; migrar el cron al dual-style es decisión aparte.
- **✓** **Reglas duras horneadas en los skills, no en memoria.** `avatar_qa.py` post-render obligatorio, router gemini-omni, POV de Roberto (5 puntos), LATAM neutro, no-count de agentes, y Pikzels --score como gate con override registrado de la JuntaYT.
- **✓** **Memorias de canales al día.** 3 canales OAuth funcionales — AI4Managers, AgentSquad EN y AgentSquad LATAM (@agentsquadlatam, ex @thalx-ai) — con tokens verificados en ~/.env.
- **✓** **Stack Thalx Remotion operativo.** Bundle R3F aislado en site propio (fiber 9.6.1), renders E2E verdes en Lambda, todo versionado en GitHub (`1167e33` · `f3cac31`).
- **✓** **Deploy Remotion reproducible (IaC).** `remotion/deploy.mjs` (check · sites · function) valida la versión local contra la función desplegada y lee los parámetros REALES de AWS (3008MB · 600s), nunca el nombre.
- **✓** **Config real documentada, sin verdades dobles.** FFmpeg es el backend productivo de video (Remotion = vía paralela); los quirks del nombre de la Lambda (mem2048→3008MB · 240sec→600s) están en docs/architecture.
- **⛔ Nota de alcance — fuera por decisión (05-jul):** métricas mensuales del canal LATAM. El código queda listo por si se retoma — canal `latam` registrado en monthly.py y `gen-thalx-token.py` ya trae el scope de Analytics.

---

_Fuentes verificadas: `~/.claude/skills/viral-video-creator` · `~/.claude/skills/prompt-video-avatar` · `~/.claude/skills/youtube-channel-advisor` · `~/bin/agentsquad-shorts` · `~/bin/agentsquad-metrics` · `~/bin/voc-shorts` · `~/bin/pikzels-thumbnail` · memoria persistente. Inventario 2026-07-04 (6 agentes, 6/6 áreas) · consolidado 2026-07-05._