Implement 3 quote-card renderer variants (A/B/C) + SVG mockups
highsaga-mdvp9d2026-04-24Blocks Ep 15–18 (LA MÁS DOLL vs LA PERVERSA 4-ep saga; renumbered 2026-04-06 from prior 17–20 so the saga drops right after Ep 14 and ahead of held Jímenez eps 19–20). Spec at research/quote-card-spec.md. Need (1) three static SVG mockups under research/quote-card-variants/{A-typewriter,B-documentary,C-room-echo}.svg for user review, (2) three renderer factories in scripts/lib/card-renderer.ts (cardQuoteTypewriter / cardQuoteSubtitle / cardQuoteRoomEcho), (3) schema enforcement in scripts/lib/validate-slide-plan-vs-script.ts that rejects speaker/attributed_to/author fields on quote-card entries, (4) scripts/qa-name-visual-alignment.ts pass-through for quote-card, (5) assets/sfx/typewriter-tick.mp3 + assets/broll/paper-grain.png assets. Validator already whitelists quote-card as a meme kind; taxonomy slot exists.
saga-mdvp-quote-card-variants
Implement fight_clip_overlay segment kind + ffmpeg compositor
highsaga-mdvp9d2026-04-24Blocks Ep 15 (saga opener). Spec at research/fight-clip-compositor-spec.md. Need (1) scripts/lib/fight-clip-compositor.ts implementing the ffmpeg filter chain (mute original audio, 25% fire-rain overlay, two-bed crossfade at impact, quote-card overlays at timed marks, 1.5s tail audio fade), (2) fight_clip_overlay added to scripts/validate-script.ts kind enum with schema (source.path, source.mute_original===true mandatory, audio_bed_id resolvable against music registry), (3) scripts/build-racheteo.ts::stageSlideshow reserve-block handling so interior quote cards don't count toward per-minute text-card quota, (4) ingest the source clip https://www.youtube.com/watch?v=2JUPiw8wW44 via yt-dlp (audio stripped) to assets/fight-clips/mas-doll-vs-perversa-2026-04-23.mp4, (5) produce/source assets/broll/fire-rain-overlay.webm (CCO).
saga-mdvp-fight-clip-compositor
Unlock AWS Polly for nagacct (Ep 17 Psicólogo Viejo blocker)
highsaga-mdvp9d2026-04-24Ep 17 (¿Qué dice el chat? — was Ep 20 in the dropped 5-ep plan, Ep 19 after collapse, now Ep 17 after the 2026-04-06 renumber) uses AWS Polly voice Miguel (es-US neural) with SSML prosody for the Psicólogo Viejo character. Currently polly:DescribeVoices and polly:SynthesizeSpeech are denied for the nagacct IAM user. One policy attachment resolves it. After unlock, run a 3-voice audition (Miguel / Pedro / Andrés, 30s each) and lock the voice before rendering Ep 17.
saga-mdvp-polly-iam-unlock
Add speaker diarization to live transcripts (pyannote or speechbrain)
highlive-pipeline9d2026-04-24Whisper transcripts today are flat text with no speaker labels — analyze-chunks.ts correctly refuses to attribute quotes to named people because of this. Adding diarization (e.g., pyannote.audio + the same Opus sidecars we already save) would let us attribute 'Speaker_A / Speaker_B / ...' per word, then cluster-to-name via voice samples of Santiago Matias / participants. This unlocks the speaker_claims field in chunk-analysis.json from 'unknown conf=5' to 'Santiago Matias conf=9'. Cost: free (runs locally) but CPU-heavy; plan a per-chunk job alongside transcribe-tail.
speaker-diarization-live
Incremental episode-creation Lambda (ingest prior scripts + saga doc, emit JSON draft)
highproduction9d2026-04-24User feedback on Ep 15 (item 11, 2026-04-24): each episode today is a single agent pass with no awareness of prior episode canon. Ep 15 invented a '30-min peluca' beat that contradicted the cocina / de-boca framing already established in Eps 11-12. A per-episode rewrite by a large-context agent is also the most expensive per-video spend we have. Build an AWS Lambda that: (1) ingests scripts/racheteo-0{01..N}.script.json plus the current saga's _meta._saga_doc history plus any relevant research/*.md files; (2) exposes createEpisode({ topic, previousEpisodes, saga, feedbackItems? }) returning a full JSON script draft that already respects cast/saga continuity, banned phrases (scripts/lib/style-guide.json bannedPhrases), the text-card count validator (scripts/validate-script.ts saga-count-vs-textcard rule), and the 480s runtime floor; (3) is cheaper per run than the current agent-loop (batched context compression + structured output). Downstream: CLI flag SCRIPT_MODE=lambda in scripts/build-racheteo.ts to pull the draft instead of reading a static .script.json. This also unblocks the Ep16-18 GPT-5.5-vs-Opus-4.7 blind-rewrite comparison (see plan /Users/aes/.cursor/plans/ep15_render_+_defer_gpt_4fe75b86.plan.md) because both variants would call the same lambda with different model= params.
episode-creation-lambda
Who-is narration: switched default to in-session Opus, track cost vs automation trade-off
mediumproduction6d2026-04-27As of 2026-04-27 the documented primary narration path for who-is episodes is `scripts/build-who-is.ts --skip-llm` + in-session authoring by the Cursor agent (Claude Opus 4.7). The gpt-4o-mini path is kept as a legacy CLI fallback. Rationale + cost table below.
**Why we switched:**
- gpt-4o-mini narration consistently hit the banned-loanword validator (Anglicisms like 'show', 'reality show', outlet names like 'Listín Diario'). Every who-is episode shipped so far needed 2-3 hand patches.
- Opus in-session writes validator-clean Dominican Spanish on the first pass. Demonstrated working on Ana Beato racheteo-022 (shipped clean after 2 trivial patches — seg 02 'show'→'programa', seg 03 'Listín Diario'→'la prensa nacional').
- Zero external API cost (the agent session is already paid via Cursor subscription). Zero ANTHROPIC_API_KEY setup. Zero quota risk (we hit OpenAI quota exhaustion twice this session alone).
**Cost comparison (per episode, ~15 narrated segments):**
| Path | In tokens | Out tokens | Rate | Per episode | Quality |
|---|---|---|---|---|---|
| gpt-4o-mini (old default) | ~15k | ~3k | $0.15/$0.60 per M | ~$0.003 | Needed 2-3 patches per episode |
| Opus 4.7 via standalone Anthropic API (not implemented) | ~45k | ~3k | $15/$75 per M | ~$0.90 | Estimated clean-first-pass |
| Opus 4.7 in Cursor agent session (CURRENT DEFAULT) | same | same | $0 marginal | $0 direct | Demonstrated clean |
**Trade-off the agent vs automation:**
- Pro in-session Opus: $0, higher quality, full context of validators/prior episodes.
- Con in-session Opus: requires an interactive agent session. Blocks 'racheteo who-is --slug X --episode N' becoming a pure cron-style CLI (the goal in pending-tasks/who-is-llm-orchestrator-only).
- Middle path: add an --anthropic-api flag that calls the Anthropic API directly when the agent is not in the loop (batch builds). Cost ~$0.05–$0.10/episode assuming Sonnet 4.5; ~$0.50–$1.00/episode on full Opus 4.7. User decision pending.
**Action items (open):**
- Ship this session's eps 20 (Pamela) + 21 (Ana) with in-session Opus narration — PROOF.
- Instrument dashboard 'todo' tab to surface per-episode token counts + authoring-path (opus-insession / gpt-4o-mini / anthropic-api) so we can compare quality deltas at scale.
- Revisit the --anthropic-api flag in 2-3 episodes if we decide to automate the cron-style flow. Today's cost delta ($0 → $0.05 per episode is rounding error) but we'd want to avoid $1/episode without explicit user sign-off.
who-is-narration-opus-default-2026-04-27
Collapse who-is model interactions into one orchestrator-only LLM call
mediumproduction6d2026-04-27Goal: shrink the agent's footprint per who-is episode down to ONE scripted pipeline invocation + a bounded LLM call for orchestration, with deterministic scripts doing the rest. Right now a single who-is episode requires the agent to walk through ~15 steps (migrate-to-brain, hydrate-from-bundle, ingest-news, ingest-tiktok-profile, sentiment-tiktok, curate-tiktok-pool, build-who-is + per-segment LLM fills, build-racheteo-v2 remote, time-build local, QC review, iterate on validator failures). Most of those are already scripts; the remaining agent-in-the-loop portion is (1) reading recipe warnings and deciding whether to proceed, (2) interpreting validator errors, (3) regenerating the script after a slide-plan failure, (4) inspecting the MP4. Track metrics as we build the 4 runs this session (Pamela ep20 local+SFN, Ana ep21 local+SFN) and use the data to prioritize which step becomes a single CLI next. Tracking notes live under this task; amend the body as each run lands.
Baseline metrics (fill in as we build ep20 + ep21):
- Pamela ep20 (local v1): agent interactions=___ llm_prompts=___ prompt_tokens=___ completion_tokens=___ wall_min=___ human_intervention_points=___
- Pamela ep20 (sfn v2): agent interactions=___ llm_prompts=___ prompt_tokens=___ completion_tokens=___ wall_min=___ human_intervention_points=___
- Ana ep21 (local v1): agent interactions=___ llm_prompts=___ prompt_tokens=___ completion_tokens=___ wall_min=___ human_intervention_points=___
- Ana ep21 (sfn v2): agent interactions=___ llm_prompts=___ prompt_tokens=___ completion_tokens=___ wall_min=___ human_intervention_points=___
Counted as one "agent interaction" = one user-visible step the agent performed (tool call group + reasoning). Counted as one "human intervention point" = anywhere the agent had to stop and ask the user, OR anywhere the plan diverged from the happy path.
Target end-state: `racheteo who-is --slug <slug> --episode <N>` is ONE CLI that runs the whole pipeline end-to-end, with a single LLM call used only for narration text generation (bounded: ~15k in / ~3k out tokens per episode based on build-who-is today). Any decision normally made by the agent (validator retry, slide-plan regen, cast co-mention) becomes a deterministic script branch. Dashboard tracks per-run metrics via output/build-metrics/ledger.jsonl; add a column for agent_interactions so this task's baseline can be sourced from that feed.
who-is-llm-orchestrator-only
Add Jay One aliases to racheteo-015..018 _cast[].speech_aliases
mediumsaga-mdvp9d2026-04-24Yeiguan is the Whisper-ASR phonetic rendering of Jay One (user-confirmed Apr 24). Saga collapsed 5→4 on 2026-04-24 and renumbered 17–20→15–18 on 2026-04-06 — the 4 scripts to patch are scripts/racheteo-0{15,16,17,18}.script.json. Each must include Jay One in _cast[] with speech_aliases: ['jey one','yeiguan','jey juan','jwan','yei juan','j-one','j one','jone','alante jey one']. Consumed by scripts/lib/cast-index.ts (line 56) and downstream by summarize-tail-window.ts / mine-video-ideas.ts / dashboard faces autocomplete.
saga-mdvp-jayone-aliases
Build super_chat_card segment kind + overlay renderer (Ep 17/18)
mediumsaga-mdvp9d2026-04-24Ep 17 (audience reaction) and Ep 18 ("el chat sigue pidiéndola" cierre) both use a super-chat-styled card that mimics YouTube's super-chat UI (handle + dollar-chip + message). Per saga-wide attribution rule, the handle appears only as part of the chrome art, never as a speaker lower-third. If fight-window chat backfill fails, cards render from synthetic seed text with a visible "reconstrucción" watermark in the lower-right. Needs a new scripts/lib/super-chat-overlay.ts (text → animated super-chat image) and super_chat_card / psicologo_breakdown kinds recognized by validate-script.ts.
saga-mdvp-super-chat-card
Wire music-bed-registry into build pipeline + produce stand-in audio
mediumsaga-mdvp9d2026-04-24research/music-bed-registry.md is the new single source of truth for song/bed references. Need (1) scripts/validate-script.ts to reject any music cue that doesn't resolve to a registry ID, (2) CCO stand-in MP3s under assets/broll/music-stand-ins/ for fight-clip-bed-shorts-2026-04-23 (A + B), blue-yung-kai, and passo-bem-solto-atlxs since none are cleared, (3) song-card PNGs at assets/song-cards/<id>.png for BLUE + PASSO BEM SOLTO (only needed when a future episode consumes them).
saga-mdvp-music-bed-registry-wiring
Retry fight-window chat backfill once stream ends
mediumsaga-mdvp9d2026-04-24scripts/backfill-live-chat.ts currently fails with exit code 3 because the stream is still live (yt-dlp VOD path unavailable, pytchat seektime has no effect on live). Once the stream ends, retry the fight-window range (02:30–03:30 UTC Apr 24) to replace Ep 17's synthetic super-chat cards with real messages. Diagnostic report location: s3://racheteo-build-125867534472/planeta-alofoke/live/chat/_backfill-reports/.
saga-mdvp-chat-backfill-retry
Surface per-chunk structured analysis in the dashboard
mediumdashboard9d2026-04-24scripts/analyze-chunks.ts writes attribution-safe JSON to s3://racheteo-build-125867534472/planeta-alofoke/live/chunk-analysis/<videoId>/<chunkTs>.json (topics, speaker_claims, events, participants_discussed, quotes_of_interest, emotional_tone, audience_signal, warnings, summary_one_liner). Today these are S3-only. Add a Live tab in the dashboard that lists chunks with their summary_one_liner, filterable by participant or topic, and a detail view showing quotes + events. Chunk analysis is cheap (~$0.001/chunk) and backfills all ~500 existing chunks on demand.
chunk-analysis-dashboard-ui
Surface rolling live-chat + super chats in the dashboard
mediumdashboard9d2026-04-24scripts/record-live-chat.ts (via launchd com.planetaalofoke.record-live-chat every 10 min) writes normalized ChatMsg snapshots to live/chat/<videoId>/<chunkTs>.json and appends to live/chat/rolling/<YYYY-MM-DD>.jsonl. Add a 'Chat' panel on the Live tab: scrollable feed with timestamp + author + text, with super chats pinned, plus a per-chunk 'dominant sentiment' pulled from audience_signal in the matching chunk-analysis. Useful for noticing moments the chat reacted strongly (rises in message rate, super-chat spikes).
live-chat-dashboard-ui
Install curl_cffi so yt-dlp can capture TikTok comment bodies
mediumingestion9d2026-04-24scripts/ingest-tiktok-profile.ts passes --write-comments to yt-dlp and records comment_count. But the comments[] array comes back empty without curl_cffi installed (yt-dlp uses it to impersonate Chrome for TikTok's anti-bot). Install curl_cffi into whatever Python env yt-dlp uses (pip install curl_cffi), verify with a single post, then run a --comments-only backfill pass over the already-ingested la-mas-doll (98 posts) and la-perversa (53 posts) to fill in comment bodies without redownloading videos.
tiktok-curl-cffi-install
24/7 AI-generated reggaeton live stream on the channel
mediumchannel-ops9d2026-04-24User direction 2026-04-24: stand up a 24/7 live on our YouTube channel that plays an AI-generated reggaeton loop. Format reference: the two YouTube lofi-style always-on streams linked below (AI-generated music with animated still frame + chat overlay). Implementation sketch: (1) music-generation lambda (OpenAI audio OR facebookresearch/MusicGen in a GPU-backed lambda) producing 4-8 min reggaeton tracks from prompt seeds, (2) playlist builder that stitches + crossfades tracks into a continuous WAV/AAC stream, (3) ffmpeg RTMP ingest to YouTube Live with a static video frame + scrolling track title + a small live-chat overlay. Reuse the existing video-compose / slideshow pipeline where possible for the static frame + overlay. Monetization + discoverability upside; channel-ops says low ongoing cost once track cache is warm.
reggaeton-loop-live
PrepAudioRollup: replace 'always re-TTS body' with content-hash cache
lowproduction3d2026-04-30Phase R follow-up (2026-04-30) shipped 'always re-TTS every body segment on every recreate' as the simple deterministic fix for the Apr-28 stale-audio bug (root cause: PrepAudioRollup was concat'ing 3-day-old `segments/<id>.mp3` files because no Prep step regenerated body narration when the script changed). Cost ~30-60s + ~$0.20/recreate.
**Optimization (deferred):** hash `seg.text` → write to `segments/<id>.<sha8>.mp3`, skip Polly when the hash already exists in S3. Saves ~$0.20 + 60s per no-op recreate. Also lets the 'recreate without script edits' button (re-render visuals only) stop paying TTS cost.
**Owner:** TBD.
**Trigger:** when recreate volume > ~10/day OR when the body-TTS pass starts dominating Prep latency. Today (~1-2 recreates/episode during iteration) the savings don't justify the code complexity.
**Implementation sketch:**
- Compute `sha8 = sha256(seg.text).slice(0, 8)` per narrated segment.
- In `runBodyTts()`: HeadObject `segments/<id>.<sha8>.mp3`; if 200, skip Polly invoke.
- Update AudioRollup candidate paths to also look for `segments/<id>.<sha8>.mp3` (with the script's current sha8).
- Add a `--force-tts` flag on the SFN input for explicit re-synth (e.g. when Polly itself changed, or when the user wants a fresh take).
**Why kept manual today:** the always-re-TTS path is provably correct and easy to reason about. The hash cache adds 3 read paths (S3 HEAD, hash compute, candidate lookup) that all need to stay in sync — easy to silently regress back to stale audio.
audio-rollup-tts-hash-cache-2026-04-30
Relax who-is recipe broll-video floor from 1.0 → 0.8/min (warning-level)
lowproduction6d2026-04-27Hit during Pamela ep20 + Ana ep22 builds (2026-04-27). The who-is recipe sets `broll-video.minPerMin = 1.0` as a hard error. In practice, 6 tiktok-showcase segments over a 7-9 min VO lands at 0.82-0.87/min — below the floor by ~0.15/min. Pamela's build only passed because the slide-class gate skips on the first `--stage=all` run (when no slide-plan exists yet); Ana hit it on restart because the pre-crash slide-plan was on disk. Both scripts were manually patched to `minPerMin: 0.8, level: warning` to unblock. Fix: edit `scripts/recipes/who-is.recipe.ts` around line 521 to (a) lower `broll-video.minPerMin` to 0.8, (b) downgrade `level` from 'error' to 'warning', and (c) update the `why` to explain that 6 showcases is the format target and the floor is about ratio, not hard quota. Alternative: bump `numShowcaseSegments` default from 6 → 7 so 7 showcases / 7 min = 1.0/min passes the existing floor. Trade-off: more showcases = longer video, potentially less narrator control. Prefer the floor relaxation unless a future episode proves we need the extra tiktok density.
who-is-recipe-broll-video-floor
MP4 visual review of restraint chunk (second restrainer ID)
lowsaga-mdvp9d2026-04-24Chunk 20260424T024638Z is the restraint beat where La Más Doll is held back by two men. Jay One is locked (she yells his name). Second restrainer is probably Sr Jiménez per v1 speculation but not visually confirmed. Pull MP4 for the chunk, freeze at the peak "súéltame" moments, identify the second man. Ep 15 (saga opener) ships fine without this (narration says "probablemente Sr Jiménez"), but locking it lifts the narration from probable to named.
saga-mdvp-mp4-restrainer-id