[M G] co/Civitai/Qwen-Image-2512-GGUF "Civitai/Qwen-Image-2512-GGUF · Hugging Face"
Sollen wir qwen image auch selber über comfyui laufen lassen? Oder wäre das parallel mit dem videomodell nicht möglich?
 für GGUF + ComfyUI ist aktuell Qwen-Image-2512-GGUF wahrscheinlich die beste praktische Wahl – nicht das ältere Qwen-Image, sondern die neuere **Qwen-Image-2512**-Variante.

Meine Rangliste für deinen Usecase:

| Zweck                                                                     | Empfehlung                    | Warum                                                                                |
| ------------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Beste kostenlose GGUF-Bildgenerierung in ComfyUI                      | Qwen-Image-2512-GGUF      | Sehr stark bei Realismus, Text im Bild, Prompt-Folgen; Apache-2.0; direkt als GGUF verfügbar. Unsloth beschreibt es als GGUF-Quantisierung mit ComfyUI-GGUF-Support. ([Hugging Face][1]) |
| Bildbearbeitung / Referenzbilder / Text in bestehenden Bildern ändern | Qwen-Image-Edit-2511-GGUF | Separates Edit-Modell, GGUF, nutzt ComfyUI-GGUF. ([Hugging Face][2])                                                                                |
| Schnell / weniger VRAM / experimentell                                | Z-Image Turbo GGUF        | Läuft mit ComfyUI-GGUF und ist deutlich leichter/schneller, aber nicht die beste Gesamtqualität. ([Hugging Face][3])                                                                     |
| Alternative zu Qwen, stark aber schwerer                              | FLUX.2-dev-GGUF           | Gute Qualität, aber grösser, Lizenz weniger frei/praktisch als Apache-2.0; Q4_K_M allein ca. 18.7 GB. ([Hugging Face][4])                                                                |
| Aktuell stärkstes Open-Weights-Modell allgemein                       | HiDream-O1-Image-Dev-2604 | Führt bei Artificial Analysis unter Open-Weights, aber der ComfyUI-Weg ist derzeit eher über Custom Node/FP8, nicht sauber als etablierter GGUF-Standard. ([artificialanalysis.ai][5])   |

Kurz gesagt:
Für deine lokale ComfyUI/Hermes-Pipeline würde ich Qwen-Image-2512-GGUF nehmen.

Die passende Auswahl nach VRAM:

|        VRAM | Modell/Quantisierung                                   |
| ----------: | ------------------------------------------------------ |
| 8–12 GB | Qwen-Image-2512 Q4, Low-VRAM, langsam aber möglich |
|   16 GB | Q5 oder Q6 testen                                      |
|   24 GB | Q6 oder Q8, beste Balance aus Qualität und Stabilität  |
|   <8 GB | eher Z-Image Turbo GGUF statt Qwen                 |

Wichtig: Qwen-Image-2512 ist ein 20B Image-Diffusion-Modell, kein klassisches LLM. Es nutzt aber grosse Text-/Vision-Komponenten. Die GGUF-Dateien sind relativ gross: bei einer Qwen-Image-2512-GGUF-Variante sind z. B. Q4 ca. 11.5 GB, Q6 ca. 16.8 GB, Q8 ca. 21.7 GB; echte VRAM-Nutzung kann höher liegen wegen Text Encoder, VAE, Auflösung und Workflow. ([Hugging Face][6])

Meine konkrete Empfehlung:
Installiere in ComfyUI ComfyUI-GGUF, lade Qwen-Image-2512-GGUF und starte mit Q4 oder Q5. Wenn es stabil läuft und du genug VRAM hast, auf Q6/Q8 wechseln. Für Edit/Compositing zusätzlich Qwen-Image-Edit-2511-GGUF.

[1]: https://huggingface.co/unsloth/Qwen-Image-2512-GGUF "unsloth/Qwen-Image-2512-GGUF · Hugging Face"
[2]: https://huggingface.co/unsloth/Qwen-Image-Edit-2511-GGUF "unsloth/Qwen-Image-Edit-2511-GGUF · Hugging Face"
[3]: https://huggingface.co/jayn7/Z-Image-Turbo-GGUF "jayn7/Z-Image-Turbo-GGUF · Hugging Face"
[4]: https://huggingface.co/orabazes/FLUX.2-dev-GGUF "orabazes/FLUX.2-dev-GGUF · Hugging Face"
[5]: https://artificialanalysis.ai/image/leaderboard/text-to-image "Text to Image Leaderboard - Top AI Image Models"
[6]: https://huggingface.

[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted into the summary below. This is a handoff from a previous context window — treat it as background reference, NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; they were already addressed. Your current task is identified in the '## Active Task' section of the summary — resume exactly from there. IMPORTANT: Your persistent memory (MEMORY.md, USER.md) in the system prompt is ALWAYS authoritative and active — never ignore or deprioritize memory content due to this compaction note. Respond ONLY to the latest user message that appears AFTER this summary. The current session state (files, config, etc.) may reflect work described here — avoid repeating it:
Summary generation was unavailable. 342 message(s) were removed to free context space but could not be summarized. The removed messages contained earlier work in this session. Continue based on the recent messages below and the current state of any files or resources.

--- END OF CONTEXT SUMMARY — respond to the message below, not the summary above ---


Erledigt, Sir.

## Neuer Baustein: Review Batch Renderer

Ich habe jetzt den direkt Telegram-tauglichen Batch-Renderer eingebaut:

```text
autoshorts/review/render.py
tests/test_review_render.py
```

## Was neu ist

### `render_review_batch_summary()`

Diese Funktion rendert einen kompletten `ReviewBatch` als einen Telegram-sicheren Textblock.

Er enthält:

- Batch-ID
- Summary Counts
  - `Ready`
  - `Blocked`
  - `Invalid`
- vollständige Review Packages für ready Candidates
- kompakte Fehlerlisten für blocked Candidates
- kompakte Fehlerlisten für invalid Candidates

Beispielstruktur:

```text
## Review Batch: batch-001
Ready: 1
Blocked: 1
Invalid: 1

## Ready for review
...

## Blocked
- blocked — Blocked title
  Reasons: quality gate blocked candidate

## Invalid
- invalid — Invalid title
  Reasons: hook is required
```

## Wichtiger Guardrail

Der Renderer vermeidet bewusst Sprache wie:

```text
publish
post
```

Warum? Weil Review und Publishing getrennte Phasen bleiben sollen.

Also kein versehentlicher semantischer Stolperdraht à la “approved-looking text = upload”. Das wäre genau die Sorte Automations-Dummheit, für die später Meetings erfunden werden.

## TDD eingehalten

Ablauf:

1. Tests für Batch Renderer geschrieben.
2. RED verifiziert:

```text
ModuleNotFoundError: No module named 'autoshorts.review.render'
```

3. Minimal implementiert.
4. Tests grün.
5. README aktualisiert.
6. Commit erstellt.

## Tests

Aktueller Stand:

```text
41 passed in 0.08s
```

## Neuer Commit

Branch:

```text
feat/core-approval-quality-gates
```

Commit:

```text
b7b5270 feat: add review batch renderer
```

Aktueller Verlauf:

```text
b7b5270 feat: add review batch renderer
2155728 feat: add review workflow buckets
deea24f feat: add candidate factory
ae6a4f6 feat: add content pillar registry
49d6daf feat: add idea candidates and testbatches
55b17fa feat: add telegram review package
3555ec2 feat: add approval parser and quality gate
7642749 Initial commit
```

## Pipeline jetzt

Wir haben jetzt die erste durchgängige interne Review-Strecke:

```text
Content Pillar
→ CandidateDraft
→ Candidate Factory
→ VideoCandidate
→ TestBatch
→ Review Workflow
   → ready_for_review
   → blocked
   → invalid
→ Review Batch Renderer
→ Telegram-safe Review Text
→ Approval Parser
```

## Nächster sinnvoller Schritt

Als Nächstes würde ich bauen:

```text
autoshorts/cli/demo_review_batch.py
```

oder besser als testbarer Core:

```text
autoshorts/demo/sample_batch.py
```

Ziel:

- sample AI-Life-Systems Batch erzeugen
- 1 ready Candidate
- 1 blocked Candidate
- 1 invalid Candidate
- ReviewBatch bauen
- Telegram-Text rendern

Dann können wir mit einem einzigen Command einen echten Review-Output lokal erzeugen und später direkt an Telegram hängen.


[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted into the summary below. This is a handoff from a previous context window — treat it as background reference, NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; they were already addressed. Your current task is identified in the '## Active Task' section of the summary — resume exactly from there. IMPORTANT: Your persistent memory (MEMORY.md, USER.md) in the system prompt is ALWAYS authoritative and active — never ignore or deprioritize memory content due to this compaction note. Respond ONLY to the latest user message that appears AFTER this summary. The current session state (files, config, etc.) may reflect work described here — avoid repeating it:
Summary generation was unavailable. 49 message(s) were removed to free context space but could not be summarized. The removed messages contained earlier work in this session. Continue based on the recent messages below and the current state of any files or resources.

--- END OF CONTEXT SUMMARY — respond to the message below, not the summary above ---


[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted into the summary below. This is a handoff from a previous context window — treat it as background reference, NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; they were already addressed. Your current task is identified in the '## Active Task' section of the summary — resume exactly from there. IMPORTANT: Your persistent memory (MEMORY.md, USER.md) in the system prompt is ALWAYS authoritative and active — never ignore or deprioritize memory content due to this compaction note. Respond ONLY to the latest user message that appears AFTER this summary. The current session state (files, config, etc.) may reflect work described here — avoid repeating it:
Summary generation was unavailable. 16 message(s) were removed to free context space but could not be summarized. The removed messages contained earlier work in this session. Continue based on the recent messages below and the current state of any files or resources.

--- END OF CONTEXT SUMMARY — respond to the message below, not the summary above ---

Erledigt, Sir.

Ich habe aus den 5 Vorschlägen jetzt einen **konkreten, testbaren TrueTrace Concept Testbatch** gebaut — nicht nur Text im Chat, sondern echte Pipeline-Artefakte.

## Neu gebaut

```text
autoshorts/demo/truetrace_concepts.py
tests/test_truetrace_concept_batch.py
docs/truetrace_concept_testbatch_001.md
```

Commit:

```text
2d6af9f feat: add TrueTrace concept testbatch
```

## Was enthalten ist

Der Testbatch enthält exakt diese 5 Kandidaten:

```text
1. Why Most AI Automations Break After Three Days
2. This Prompt Looks Smart — But the Workflow Still Breaks
3. The Hidden Reason Your AI Notes Become Useless
4. Claim: AI Agents Will Replace Teams
5. One Rule Before You Automate Anything With AI
```

Batch-ID:

```text
truetrace-concept-testbatch-001
```

## Jeder Kandidat enthält jetzt strukturiert

- Candidate ID
- Titel
- Plattformen:
  - YouTube
  - TikTok
- Dauer
- Hook
- Script Outline
- Visual Concept
- Caption
- CTA
- Hashtags
- YouTube Tags
- Hypothesis
- Brand Policy Notes

Also nicht mehr “Ideen-Geschwurbel”, sondern prüfbare Kandidaten. Eine kleine, aber entscheidende zivilisatorische Verbesserung.

## Candidate IDs

```text
truetrace-ai-automation-quiet-rot-38s
truetrace-prompt-vs-system-workflow-breaks-35s
truetrace-ai-notes-equal-importance-34s
truetrace-ai-agents-tasks-not-teams-40s
truetrace-review-before-automation-32s
```

## Validierung

Die Tests prüfen:

- genau 5 Kandidaten
- stabile Reihenfolge
- Zielplattformen nur YouTube/TikTok
- Brand Policy Notes vorhanden
- alle Kandidaten bestehen TrueTrace Brand Fit
- alle Kandidaten bestehen Series Fit
- keine öffentlichen No-Go-Inhalte:
  - kein JARVIS
  - kein Hermes
  - kein User
  - keine Zusammenarbeit
  - keine Politik
- Summary enthält YouTube-/TikTok-Metadaten
- Summary enthält keine Action-Sprache wie:
  - `publish`
  - `upload`
  - `post`

## Neuer Renderer

Neue Funktion:

```python
render_truetrace_concept_summary()
```

Sie rendert alle 5 Konzepte als reviewbaren Text mit:

- Hook
- Outline
- Visuals
- Hypothesis
- YouTube Description
- TikTok Caption
- Tags

Keine Side Effects. Kein Upload. Kein Platform Call. Nur Review-Text.

## Beispielauszug

```text
## TrueTrace Concept Testbatch
Batch: truetrace-concept-testbatch-001
Candidates: 5
Brand: True Trace Shorts — Systems over prompts.

## 1. Why Most AI Automations Break After Three Days
ID: truetrace-ai-automation-quiet-rot-38s
Duration: 38s
Hook: Most AI automations do not fail dramatically. They quietly rot.
...
YouTube:
AI automations rarely explode. They quietly rot when the workflow has no exception path.

Follow for practical AI systems, not automation theatre.

#AI #AISystems #Workflow #Productivity
```

## Dokumentiert

Neue Datei:

```text
docs/truetrace_concept_testbatch_001.md
```

Darin:

- Safety Scope
- alle 5 Candidates
- Hypothesen
- Review Command
- empfohlene Priorisierung

## Empfohlene erste 3 für Preview-Produktion

Ich habe im Dokument festgelegt:

```text
1. Why Most AI Automations Break After Three Days
2. This Prompt Looks Smart — But the Workflow Still Breaks
3. The Hidden Reason Your AI Notes Become Useless
```

Warum:

- konkreter
- visuell gut darstellbar
- weniger generisch
- weniger abhängig vom allgemeinen AI-Hype
- besserer Mechanism-Reveal

## Tests

```text
139 passed in 8.79s
```

## TDD eingehalten

Ablauf:

1. Tests zuerst geschrieben.
2. RED verifiziert:

```text
ModuleNotFoundError: No module named 'autoshorts.demo.truetrace_concepts'
```

3. Minimal implementiert.
4. Focused Tests grün.
5. Full Suite grün.
6. README + Docs aktualisiert.
7. Commit erstellt.

## Aktueller Stand

Jetzt haben wir einen echten ersten Content-Testbatch:

```text
TrueTrace BrandProfile
→ ContentSeries Fit
→ Brand Fit
→ 5 Concrete Candidates
→ Reviewable Concept Summary
→ YouTube/TikTok metadata included
```

## Nächster sinnvoller Schritt

Als Nächstes würde ich aus den besten 3 Konzepten echte **Preview Packages** bauen:

```text
Concept Candidate
→ ShotPlan
→ RenderManifest
→ Local Preview Plan
→ PNG scene cards
→ MP4 timing preview
→ Social Draft
→ Manual Posting Pack
```

Priorität:

```text
1. AI Automations Quietly Rot
2. Prompt vs System
3. AI Notes Become Useless
```

Dann sehen wir nicht nur Textideen, sondern bewegte Review-Previews mit Captions, Timing und Metadaten. Erst dann entscheiden wir, ob daraus finale Videos werden.


[M G] Ok, mach das. Und noch eine frage: wie lösen wir die Subtitles (harmozi style?) und welches TTS model?  Bei build.nvidia.com finde ich nur dieses: https://build.nvidia.com/nvidia/magpie-tts-multilingual/api . Ist das besser als EdgeTTS? Oder besser lokal whisper? Jedoch brauchen wir die gpu ja für das video…


Erledigt, Sir.

## Gebaut: Top-3 TrueTrace Preview Packages

Ich habe jetzt aus den drei priorisierten Konzepten echte lokale Timing-Previews gebaut:

```text
autoshorts/demo/truetrace_preview_packages.py
tests/test_truetrace_preview_packages.py
docs/subtitle_tts_strategy.md
```

Commit:

```text
6efc4dc feat: add TrueTrace top preview packages
```

## Enthaltene Top-3

```text
1. Why Most AI Automations Break After Three Days
2. This Prompt Looks Smart — But the Workflow Still Breaks
3. The Hidden Reason Your AI Notes Become Useless
```

## Generierte Preview-Videos

Die MP4-Timing-Previews liegen hier:

MEDIA:/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/previews/truetrace-top3/truetrace-ai-automation-quiet-rot-38s/preview.mp4

MEDIA:/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/previews/truetrace-top3/truetrace-prompt-vs-system-workflow-breaks-35s/preview.mp4

MEDIA:/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/previews/truetrace-top3/truetrace-ai-notes-equal-importance-34s/preview.mp4

Das sind bewusst noch **Approval-Timing-Previews**, nicht finale Videos. Also: Bewegung, Timing, Szenenlogik, Captions, visuelle Struktur — aber noch nicht finale TTS/Audio/AI-Video-Qualität.

## Was die neue Pipeline macht

Neue Funktion:

```python
build_top_truetrace_preview_packages()
```

Sie erzeugt für jeden Top-3 Candidate:

```text
Candidate
→ ScriptDraft
→ ShotPlan
→ RenderManifest
→ LocalPreviewPlan
→ PNG scene cards
→ FFmpeg MP4 timing preview
→ SocialMediaDraft
→ ContentPreviewApprovalPackage
→ ManualPostingPack
```

Dazu gibt es:

```python
render_top_truetrace_preview_summary()
```

für eine kompakte Telegram-/Review-Zusammenfassung.

## Side Effects sauber getrennt

Die Preview-Erzeugung hat erwartete lokale Render-Side-Effects:

```text
write_png
write_concat
call_ffmpeg
write_mp4
```

Aber die Approval-/Manual-Packs selbst bleiben sauber:

```text
side_effects = ()
external_calls = ()
```

Also kein YouTube, kein TikTok, kein Upload, kein Scheduling, keine API. Noch kein Content-Katapult. Sehr beruhigend.

## Tests

```text
142 passed in 31.72s
```

TDD eingehalten:

1. Test zuerst.
2. RED verifiziert:
   ```text
   ModuleNotFoundError: No module named 'autoshorts.demo.truetrace_preview_packages'
   ```
3. Implementiert.
4. Focused Tests grün.
5. Bug mit relativen Pfaden gefunden.
6. Regressionstest für relative Output-Pfade ergänzt.
7. Full Suite grün.
8. Commit erstellt.

Nebenbei `.gitignore` ergänzt:

```text
data/previews/
```

Damit generierte Preview-Artefakte nicht versehentlich ins Repo wandern. Git ist kein Medienlager, auch wenn es gelegentlich so missbraucht wird.

---

# Zu Subtitles: Hormozi Style?

Ja — aber ich würde es bewusst so formulieren:

```text
Hormozi-inspired, not Hormozi-cloned.
```

Wir sollten den Mechanismus übernehmen, nicht die Identität kopieren.

## Empfehlung

Subtitles als **renderer-owned deterministic captions**, nicht in AI-Video/Image gebacken.

Also:

```text
Script
→ Caption Chunker
→ Timed Subtitle Cues
→ Renderer-owned subtitle layer
→ FFmpeg burn-in
```

Nicht:

```text
Prompt an Bildmodell: “mach fancy captions”
```

Warum? Weil AI-baked Text früher oder später aussieht wie ein betrunkener Drucker mit Ambitionen.

## Style-Regeln

```text
2–5 Wörter pro Caption-Chunk
max. 2 Zeilen
active word highlight
starker schwarzer Stroke / Shadow
weiss als Hauptfarbe
TrueTrace Accent: blau/grün
lower-middle Placement, oberhalb UI Safe Zone
schnelle, aber lesbare Wechsel
wichtige Wörter gelegentlich farbig
```

Beispiel:

```text
MOST AI AUTOMATIONS
QUIETLY ROT
```

Dann active highlight auf:

```text
QUIETLY
ROT
```

## Wichtig

Die Captions müssen **bei uns im Renderer** entstehen, damit wir:

- Text sauber kontrollieren
- Timing ändern können
- Fehler korrigieren können
- keine kaputten AI-Texte im Bild haben
- später Whisper/TTS-Alignment nutzen können

Das ist auch besser für Brand-Konsistenz.

---

# TTS: EdgeTTS vs NVIDIA Magpie vs Whisper

## Kurzfassung

Für Phase 1 würde ich nehmen:

```text
TTS: EdgeTTS English baseline
Subtitles: deterministic renderer-owned captions
Magpie: später A/B testen
Whisper: später Alignment/QA, nicht TTS
GPU: für Video/Image reservieren
```

## Whisper

Whisper ist **nicht** TTS.

Whisper ist Speech-to-Text. Es kann später helfen für:

- TTS-Audio transkribieren
- Timing prüfen
- Subtitle Alignment
- Aussprachen/Fehler erkennen

Aber es erzeugt keine Stimme.

Also:

```text
Whisper = Kontrolle / Alignment
nicht Voice Generation
```

## EdgeTTS

Für jetzt wahrscheinlich die beste Baseline.

Vorteile:

- kein GPU-Verbrauch
- schnell
- kostenlos / kein API-Key
- einfache Integration
- gut genug für erste Tests
- blockiert ComfyUI/Wan/Qwen nicht

Nachteile:

- nicht super einzigartig
- etwas “Standard-Neural-Voice”
- Lizenz/Terms vor echter Skalierung nochmals prüfen

Mögliche englische Testvoices:

```text
en-US-GuyNeural
en-US-ChristopherNeural
en-US-AriaNeural
en-GB-RyanNeural
```

Ich würde für TrueTrace zuerst testen:

```text
en-GB-RyanNeural
```

oder:

```text
en-US-ChristopherNeural
```

Warum? Der Kanal soll präzise, trocken, mechanism-first wirken. Zu enthusiastische “YouTube Guru”-Stimme wäre Gift. Ein sachlicher, klarer Ton passt besser.

## NVIDIA Magpie TTS

Das Modell auf build.nvidia.com:

```text
nvidia/magpie-tts-multilingual
```

würde ich **nicht sofort als Default** nehmen, sondern als A/B-Test.

Vorteile:

- vermutlich modernere Qualität als Standard EdgeTTS
- multilingual
- läuft cloudseitig, also vermutlich kein lokaler GPU-Konflikt

Nachteile:

- API-Abhängigkeit
- Kosten/Rate-Limits/Terms klären
- Stimme/Style noch nicht gehört
- Integration noch nicht gebaut
- eventuell Overkill, bevor Content-Format validiert ist

Wichtig zu Ihrer GPU-Frage:

Wenn Magpie über build.nvidia.com läuft, verbraucht es **nicht unsere lokale Video-GPU**. Aber es schafft eine externe API-Abhängigkeit. Lokal TTS auf derselben GPU wäre dagegen ungünstig, weil wir die GPU für Video/Image brauchen.

## Lokale TTS

Aktuell nicht meine erste Wahl, falls GPU nötig.

Nur sinnvoll, wenn:

- CPU schnell genug
- separate Maschine
- GPU idle
- deutlich bessere Stimme

Sonst blockieren wir Video-Generierung für Voice. Das wäre etwa so, als würde man den Backofen für Toast reservieren, während der Braten roh bleibt.

---

# Dokumentiert

Neue Datei:

```text
docs/subtitle_tts_strategy.md
```

Darin steht jetzt:

```text
Subtitles:
deterministic Hormozi-inspired renderer-owned captions

TTS:
EdgeTTS baseline first

Magpie:
A/B candidate later

Whisper:
alignment/QA later, not TTS

GPU:
reserve for video/image generation
```

---

# Meine konkrete Empfehlung

## Phase 1A — jetzt

Für die aktuellen Previews:

```text
German approval preview cards
kein final TTS nötig
keine finalen Subtitles nötig
```

Ziel: Konzept, Timing, Struktur beurteilen.

## Phase 1B — nächster Schritt

Für das erste finale Testvideo:

```text
English final voiceover mit EdgeTTS
Hormozi-inspired subtitles deterministic burn-in
kein Magpie noch
kein Whisper noch
```

## Phase 1C — danach

A/B-Test:

```text
gleicher 15–20 Sekunden Script-Ausschnitt
1. EdgeTTS
2. NVIDIA Magpie
3. optional lokale TTS, falls CPU/offline brauchbar
```

Bewertung:

- Klarheit
- Energie
- Pronunciation von AI/workflow/system terms
- Artefakte
- Latenz
- Kosten
- Integrationsaufwand

Dann entscheiden wir Voice final. Nicht vorher aus dem Bauch. Der Bauch ist für Tee, nicht für Produktionsarchitektur.

---

## Nächster sinnvoller Schritt

Als Nächstes würde ich bauen:

```text
autoshorts/audio/subtitles.py
tests/test_subtitle_cues.py
```

Ziel:

```text
Script text
→ 2–5 word caption chunks
→ timed subtitle cues
→ active-word/emphasis metadata
→ safe-zone style config
```

Danach:

```text
autoshorts/audio/tts_plan.py
tests/test_tts_plan.py
```

Ziel:

```text
TTSProviderPlan(provider="edge_tts", voice="en-GB-RyanNeural")
```

Noch ohne echte API/Audio-Erzeugung — erst Plan, dann Adapter. Langweilig, kontrolliert, gut.


[M G] Du hast mir 3x das selbe video gesendet. Mach jetzt weiter


Explanation:
**Planning inspection tasks**

I need to take action and inspect the repository. It seems relevant skills could include software development and possibly test-driven development (TDD), as well as checking automation for social shorts. I want to load the necessary skills to handle this effectively. It’s all about ensuring I have the right tools and knowledge to move forward confidently! Let’s get started on this inspection.


test-driven-development

---
name: test-driven-development
description: "TDD: enforce RED-GREEN-REFACTOR, tests before code."
version: 1.1.0
author: Hermes Agent (adapted from obra/superpowers)
license: MIT
platforms: [linux, macos, windows]
metadata:
  hermes:
    tags: [testing, tdd, development, quality, red-green-refactor]
    related_skills: [systematic-debugging, writing-plans, subagent-driven-development]
---

# Test-Driven Development (TDD)

## Overview

Write the test first. Watch it fail. Write minimal code to pass.

**Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing.

**Violating the letter of the rules is violating the spirit of the rules.**

## When to Use

**Always:**
- New features
- Bug fixes
- Refactoring
- Behavior changes

**Exceptions (ask the user first):**
- Throwaway prototypes
- Generated code
- Configuration files

Thinking "skip TDD just this once"? Stop. That's rationalization.

## The Iron Law

```
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
```

Write code before the test? Delete it. Start over.

**No exceptions:**
- Don't keep it as "reference"
- Don't "adapt" it while writing tests
- Don't look at it
- Delete means delete

Implement fresh from tests. Period.

## Red-Green-Refactor Cycle

### RED — Write Failing Test

Write one minimal test showing what should happen.

**Good test:**
```python
def test_retries_failed_operations_3_times():
    attempts = 0
    def operation():
        nonlocal attempts
        attempts += 1
        if attempts < 3:
            raise Exception('fail')
        return 'success'

    result = retry_operation(operation)

    assert result == 'success'
    assert attempts == 3
```
Clear name, tests real behavior, one thing.

**Bad test:**
```python
def test_retry_works():
    mock = MagicMock()
    mock.side_effect = [Exception(), Exception(), 'success']
    result = retry_operation(mock)
    assert result == 'success'  # What about retry count? Timing?
```
Vague name, tests mock not real code.

**Requirements:**
- One behavior per test
- Clear descriptive name ("and" in name? Split it)
- Real code, not mocks (unless truly unavoidable)
- Name describes behavior, not implementation

### Verify RED — Watch It Fail

**MANDATORY. Never skip.**

```bash
# Use terminal tool to run the specific test
pytest tests/test_feature.py::test_specific_behavior -v
```

Confirm:
- Test fails (not errors from typos)
- Failure message is expected
- Fails because the feature is missing

**Test passes immediately?** You're testing existing behavior. Fix the test.

**Test errors?** Fix the error, re-run until it fails correctly.

### GREEN — Minimal Code

Write the simplest code to pass the test. Nothing more.

**Good:**
```python
def add(a, b):
    return a + b  # Nothing extra
```

**Bad:**
```python
def add(a, b):
    result = a + b
    logging.info(f"Adding {a} + {b} = {result}")  # Extra!
    return result
```

Don't add features, refactor other code, or "improve" beyond the test.

**Cheating is OK in GREEN:**
- Hardcode return values
- Copy-paste
- Duplicate code
- Skip edge cases

We'll fix it in REFACTOR.

### Verify GREEN — Watch It Pass

**MANDATORY.**

```bash
# Run the specific test
pytest tests/test_feature.py::test_specific_behavior -v

# Then run ALL tests to check for regressions
pytest tests/ -q
```

Confirm:
- Test passes
- Other tests still pass
- Output pristine (no errors, warnings)

**Test fails?** Fix the code, not the test.

**Other tests fail?** Fix regressions now.

### REFACTOR — Clean Up

After green only:
- Remove duplication
- Improve names
- Extract helpers
- Simplify expressions

Keep tests green throughout. Don't add behavior.

**If tests fail during refactor:** Undo immediately. Take smaller steps.

### Repeat

Next failing test for next behavior. One cycle at a time.

## Why Order Matters

**"I'll write tests after to verify it works"**

Tests written after code pass immediately. Passing immediately proves nothing:
- Might test the wrong thing
- Might test implementation, not behavior
- Might miss edge cases you forgot
- You never saw it catch the bug

Test-first forces you to see the test fail, proving it actually tests something.

**"I already manually tested all the edge cases"**

Manual testing is ad-hoc. You think you tested everything but:
- No record of what you tested
- Can't re-run when code changes
- Easy to forget cases under pressure
- "It worked when I tried it" ≠ comprehensive

Automated tests are systematic. They run the same way every time.

**"Deleting X hours of work is wasteful"**

Sunk cost fallacy. The time is already gone. Your choice now:
- Delete and rewrite with TDD (high confidence)
- Keep it and add tests after (low confidence, likely bugs)

The "waste" is keeping code you can't trust.

**"TDD is dogmatic, being pragmatic means adapting"**

TDD IS pragmatic:
- Finds bugs before commit (faster than debugging after)
- Prevents regressions (tests catch breaks immediately)
- Documents behavior (tests show how to use code)
- Enables refactoring (change freely, tests catch breaks)

"Pragmatic" shortcuts = debugging in production = slower.

**"Tests after achieve the same goals — it's spirit not ritual"**

No. Tests-after answer "What does this do?" Tests-first answer "What should this do?"

Tests-after are biased by your implementation. You test what you built, not what's required. Tests-first force edge case discovery before implementing.

## Common Rationalizations

| Excuse | Reality |
|--------|---------|
| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
| "I'll test after" | Tests passing immediately prove nothing. |
| "Tests after achieve same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" |
| "Already manually tested" | Ad-hoc ≠ systematic. No record, can't re-run. |
| "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. |
| "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. |
| "Need to explore first" | Fine. Throw away exploration, start with TDD. |
| "Test hard = design unclear" | Listen to the test. Hard to test = hard to use. |
| "TDD will slow me down" | TDD faster than debugging. Pragmatic = test-first. |
| "Manual test faster" | Manual doesn't prove edge cases. You'll re-test every change. |
| "Existing code has no tests" | You're improving it. Add tests for the code you touch. |

## Red Flags — STOP and Start Over

If you catch yourself doing any of these, delete the code and restart with TDD:

- Code before test
- Test after implementation
- Test passes immediately on first run
- Can't explain why test failed
- Tests added "later"
- Rationalizing "just this once"
- "I already manually tested it"
- "Tests after achieve the same purpose"
- "Keep as reference" or "adapt existing code"
- "Already spent X hours, deleting is wasteful"
- "TDD is dogmatic, I'm being pragmatic"
- "This is different because..."

**All of these mean: Delete code. Start over with TDD.**

## Verification Checklist

Before marking work complete:

- [ ] Every new function/method has a test
- [ ] Watched each test fail before implementing
- [ ] Each test failed for expected reason (feature missing, not typo)
- [ ] Wrote minimal code to pass each test
- [ ] All tests pass
- [ ] Output pristine (no errors, warnings)
- [ ] Tests use real code (mocks only if unavoidable)
- [ ] Edge cases and errors covered

Can't check all boxes? You skipped TDD. Start over.

## When Stuck

| Problem | Solution |
|---------|----------|
| Don't know how to test | Write the wished-for API. Write the assertion first. Ask the user. |
| Test too complicated | Design too complicated. Simplify the interface. |
| Must mock everything | Code too coupled. Use dependency injection. |
| Test setup huge | Extract helpers. Still complex? Simplify the design. |

## Hermes Agent Integration

### Running Tests

Use the `terminal` tool to run tests at each step:

```python
# RED — verify failure
terminal("pytest tests/test_feature.py::test_name -v")

# GREEN — verify pass
terminal("pytest tests/test_feature.py::test_name -v")

# Full suite — verify no regressions
terminal("pytest tests/ -q")
```

### CLI Entry Points

For new `python -m package.cli.command` style entry points, keep the TDD loop boring and side-effect free:

1. Write a test that imports `main` from the missing CLI module and calls `main([])`.
2. Capture stdout/stderr with the project test framework (`capsys` in pytest) or inject a `stdout` file-like object if supported.
3. RED should usually be an import failure for the missing CLI module/package, not a vague end-to-end shell failure.
4. Implement `main(argv: Sequence[str] | None = None, *, stdout: TextIO | None = None) -> int` so tests can call it without spawning a subprocess.
5. Add the `if __name__ == "__main__": raise SystemExit(main())` guard only after the callable path is green.
6. Verify both the unit test and the real module invocation, e.g. `python -m package.cli.command`, before committing.

This catches command wiring while avoiding accidental network calls, publishing actions, or other theatrical nonsense masquerading as a CLI test.

### With delegate_task

When dispatching subagents for implementation, enforce TDD in the goal:

```python
delegate_task(
    goal="Implement [feature] using strict TDD",
    context="""
    Follow test-driven-development skill:
    1. Write failing test FIRST
    2. Run test to verify it fails
    3. Write minimal code to pass
    4. Run test to verify it passes
    5. Refactor if needed
    6. Commit

    Project test command: pytest tests/ -q
    Project structure: [describe relevant files]
    """,
    toolsets=['terminal', 'file']
)
```

### With systematic-debugging

Bug found? Write failing test reproducing it. Follow TDD cycle. The test proves the fix and prevents regression.

Never fix bugs without a test.

## Demo / Fixture Features

When adding a deterministic demo fixture for later integration wiring, still use RED-GREEN-REFACTOR. Write the wished-for import/API in the test first, verify the missing module/API failure, then implement the smallest fixture that proves the real workflow path. Assert stable IDs/order, bucket counts/classifications, and safety invariants such as review text not implying publication. See `references/deterministic-demo-fixtures.md` for the concise pattern.

## Side-Effect-Free Integration Boundaries

When building toward an external integration (Telegram, email, publishing APIs, render farms, payment flows, etc.), add a tested preparation boundary before any real I/O:

1. Write the wished-for pure function in the test first, e.g. `prepare_review_delivery(text) -> DeliveryPlan`.
2. RED should be a missing module/API or missing validation failure, not a real network/send failure.
3. The GREEN implementation should return a typed/dataclass plan object with explicit metadata: destination/topic, delivery type, human-approval requirement, and `side_effects=()`.
4. Validate safety constraints at the boundary: non-empty content, platform size limits, no forbidden action language, no implicit approval, no credentials or generated media unless explicitly expected.
5. Add a demo CLI only after the pure boundary is green. The CLI should print metadata first, then payload, and still perform no external I/O.
6. Verify both the unit tests and a real `python -m package.cli.demo_*` invocation before committing.

This pattern lets later sending/rendering/publishing adapters consume a boring, audited object instead of smuggling side effects into formatting code. Boring is how we avoid building a content catapult with a moustache.

### Media / Rendering Pipeline Boundaries

When building toward image/video generation or renderer integration, add deterministic planning artifacts before any GPU/API work:

1. Write tests for the wished-for pure API first, e.g. `create_shot_plan(script)`, `build_render_manifest(plan)`, `build_local_preview_plan(manifest)`, or `build_ffmpeg_concat_plan(plan, frames, output_path)`.
2. RED should be a missing module/API or missing validation behavior — never a failed ComfyUI/Wan/Qwen/API invocation.
3. Keep each stage inspectable and side-effect-free until the explicit adapter layer: `ScriptDraft -> ShotPlan -> RenderManifest -> PreviewPlan/PreviewArtifact -> renderer adapter`.
4. Assert stable IDs/order, scene counts, timing continuity, vertical settings (`1080x1920`, `30fps` when building Shorts/Reels/TikTok), reduced preview settings (`540x960`, `24fps` when building fast local timing previews), and placeholder/runtime paths before generating media.
5. Add guardrail tests that reject static slideshows, baked-in/generated text inside assets, non-renderer-owned captions/labels, unsafe render settings, missing fields, bad timings, non-PNG frame paths, non-MP4 output paths, and mismatched frame counts.
6. Split artifact-writing/rendering into explicit boundaries after the pure plans are green: frame writer returns `side_effects=("write_png",)`, concat planning remains side-effect-free, and the FFmpeg renderer returns `side_effects=("write_concat", "call_ffmpeg", "write_mp4")` plus `external_calls=("ffmpeg",)`.
7. Only after the manifest/preview boundary is green should a later adapter call local GPU tools or external services. Planning first; GPU roulette later.

This prevents wasting render time on unclear direction and keeps expensive media generation behind a small, audited boundary.

### Approval-First Media/Social Pipelines

When a feature crosses from content planning toward media/social delivery, prefer many small RED-GREEN-README-COMMIT slices instead of one large integration jump:

1. typed artifact/model (`SocialMediaDraft`, `RenderManifest`, etc.)
2. renderer/formatter for human review text
3. side-effect-free delivery/preparation boundary
4. demo CLI that prints metadata first, then payload

For each slice, write the missing import/API test first, verify the expected import/API failure, implement only that slice, run the focused test, run the full suite, update docs, and commit. Keep invariant tests explicit: `requires_human_approval=True`, `side_effects=()`, no external calls, no credential access, no file writes unless that adapter is the explicit feature, and no accidental action language such as `publish`/`post` in review/delivery text.

This staged approach keeps approval semantics auditable and prevents a later renderer, Telegram sender, or platform adapter from being smuggled into formatting code.

## Testing Anti-Patterns

- **Testing mock behavior instead of real behavior** — mocks should verify interactions, not replace the system under test
- **Testing implementation details** — test behavior/results, not internal method calls
- **Happy path only** — always test edge cases, errors, and boundaries
- **Brittle tests** — tests should verify behavior, not structure; refactoring shouldn't break them
- **Substring sentinels that collide with generated IDs** — when asserting that amounts or fixture values are excluded from a result, assert the full formatted token (e.g. `999.00`) or a typed field, not a short substring like `999` that can appear inside UUIDs/hash-like IDs.
- **Domain classes named `Test*` trigger pytest collection warnings** — if a production dataclass/model must be named like `TestBatch`, set `__test__ = False` on the class or choose a non-`Test` prefix. Keep the fix in production code when the name is part of the domain language, and verify the warning is gone before committing.
- **Defaulting with `x or default` hides invalid explicit inputs** — when a factory/test helper accepts optional overrides, use `x if x is not None else default` so tests can pass intentionally blank strings (`""`) or zero values and watch validation reject them. Otherwise invalid-input tests may silently receive defaults and give false confidence.

## Final Rule

```
Production code → test exists and failed first
Otherwise → not TDD
```

No exceptions without the user's explicit permission.

To view linked files, call skill_view(name, file_path) where file_path is e.g. 'references/api.md' or 'assets/config.yaml'


social-media-shorts-automation

---
name: social-media-shorts-automation
description: Design, generate, review, publish, and optimize automated short-form social videos across YouTube Shorts, TikTok, Instagram Reels, and related channels.
version: 1.0.0
tags: [shorts, tiktok, youtube-shorts, reels, creator-monetization, content-automation, social-media]
---

# Social-Media Shorts Automation

Use this skill when building or operating systems that generate short-form social videos, especially faceless or semi-automated pipelines for YouTube Shorts, TikTok, Instagram Reels, Facebook Reels, Pinterest video pins, X video, or LinkedIn video.

## Core principle

Treat the system as a **Shorts Growth Engine** / **semi-automatic Retention Studio**, not merely a renderer. A working video generator can still fail if it produces generic, low-retention, AI-slop content. Optimize for audience identity, hook quality, retention mechanics, visual mechanism reveals, platform fit, and analytics feedback. For the user's strongest strategic direction, see `references/retention-studio-strategy.md`.

For this user's AutoShorts workflows, approval previews should be German for concept comprehension, while final social-media videos should remain English unless explicitly changed. Treat preview legibility as a quality gate: readable font sizes, pixel-width-aware line wrapping, no clipped words, and no overcrowded cards.

## Retention-Studio / Mechanism-Reveal Pipeline

For quality-first automated Shorts, especially Hidden Money & AI Systems concepts, prefer the pipeline documented in `references/retention-studio-pipeline-pattern.md`:

```text
strategy → scored ideas → mechanism explainer → shot plan → keyframe/I2V prompt plan → seeded clip jobs → clip review → deterministic final renderer
```

When implementing a new repo or core pipeline, first scaffold the approval-first typed artifact chain in `references/approval-first-retention-studio-core.md`:

```text
ContentPillar → ContentBrief → ScriptDraft → VideoCandidate → TestBatch → ReviewBatch → Telegram ReviewDelivery → ShotPlan
```

Hard rule for this class: AI-video clips provide photorealistic texture and motion only. Captions, numbers, labels, arrows, UI text, price/risk/margin layers, and factual claims are renderer-owned. Final content should keep `static_card_limit = 0`, use visual changes every 1–2 seconds, and reject technically successful clips that do not serve the retention beat.

Before video generation, build an inspectable content-to-review spine: `ContentBrief → ScriptDraft → CandidateDraft → VideoCandidate → TestBatch → ReviewBatch → Telegram ReviewDelivery`. Keep it deterministic, side-effect free, and human-approval-first so hooks, retention beats, visual concepts, policy notes, and review copy can be optimized before spending GPU/API time. See `references/content-to-review-pipeline.md` for the implementation pattern and guardrails.

Hard rule for this class: AI-video clips provide photorealistic texture and motion only. Captions, numbers, labels, arrows, UI text, price/risk/margin layers, and factual claims are renderer-owned. Final content should keep `static_card_limit = 0`, use visual changes every 1–2 seconds, and reject technically successful clips that do not serve the retention beat.

## Recommended workflow

1. **Define channel position**
   - Niche(s): e.g. life optimization, AI life systems, Christian faith/FaithTok.
   - Target audience and emotional problem.
   - Language and tone.
   - Whether the brand is faceless, AI-persona-led, or creator-led.
   - No-go topics and theological/ethical boundaries if faith-related.

2. **Generate test ideas before code-heavy automation**
   - Create 20–30 candidate ideas across 2–4 content pillars.
   - For each: hook, script kernel, visual concept, caption, CTA, risk/quality score.
   - Select a small first batch (e.g. 3 per pillar) for real testing.
   - Before renderers or external AI APIs, add a deterministic content-development core: `ContentPillar → ContentBrief → ScriptDraft → CandidateDraft → VideoCandidate`. See `references/content-development-core.md`.

3. **Use hook-first, retention-led scripting**
   - First 1–2 seconds must contain a clear tension, identity statement, pain point, contrarian line, or immediate self-recognition.
   - Prefer one idea per video over generic “4 tips” unless the list format is the explicit series.
   - Avoid generic advice viewers have already heard unless framed with a new angle, concrete payoff, or strong story.
   - Reward hooks with direct address, specificity, open loops, tension, and a visible payoff; reject slow intros such as “In this video...”.
   - See `references/retention-led-shorts.md` for scoring signals, penalties, and voiceover finalization rules.

4. **Quality gate before rendering**
   Score drafts on:
   - hook strength
   - novelty
   - emotional pull
   - clarity
   - retention risk
   - comment/share potential
   - AI-slop risk
   - platform/policy risk
   Render only drafts above the chosen threshold, e.g. 8/10.

5. **Visual strategy**
   - Avoid obvious AI artifacts, fake UI text, illegible generated lettering, generic charts, and soulless stock visuals.
   - Prefer high-contrast kinetic captions, visible hook text in first seconds, relatable B-roll, screen recordings for AI/tutorial content, phone/screen close-ups, before/after transformations, cards, and timer visuals.
   - Use pattern interrupts every 2–3 seconds for retention.
   - Before rendering, generate a shot plan from the finalized voiceover: first-frame hook, pattern interrupt, core explanation, screen/card visual, CTA, caption beats, and asset type.
   - Before writing a renderer, convert shot plans into deterministic render manifests: machine-readable timeline items, asset placeholders, caption tracks, style constraints, and approval constraints. See `references/render-manifest-pipeline.md`.
   - Treat this as a deterministic intermediate artifact, not optional prose. See `references/retention-render-planning.md` for the five-scene shot-plan pattern and renderer-facing fields.
   - For the first video output, build a manifest-driven preview renderer before any AI-video or polished asset pipeline: first create a side-effect-free local preview plan, then write deterministic PNG scene cards, then build a side-effect-free FFmpeg concat plan, and only then call FFmpeg from an explicit renderer boundary. The boundary should report side effects such as `write_png`, `write_concat`, `call_ffmpeg`, and `write_mp4`; keep generated PNG/MP4 artifacts out of Git. See `references/manifest-preview-renderer.md`.
   - When the user rejects static/slideshow output, add an `asset_plan.v2` layer that maps each scene to motion-led local asset modes such as `cinematic_ai_video`, `screen_recording_simulation`, `mechanism_animation`, or `kinetic_text_overlay` with no baked text. If the user requires a free workflow, do not depend on paid video-credit tools; use local procedural motion and optional free NVIDIA LLM text refinement. See `references/ai-video-asset-planning.md`.
   - For hybrid workflows, add an imported-clip assembly layer: provider handoff prompts create short manual AI-video clips, humans drop them into deterministic `data/imported_ai_clips/<slug>/scene_XX.mp4` paths, and the final renderer combines imported clips with local mechanism animations/captions. See `references/imported-ai-clip-assembly.md`.
   - For local Wan2.2 quality workflows, prefer keyframe/image-to-video over raw text-to-video: generate/select a clean keyframe, run native Wan2.2 I2V variants, review clips bluntly for subject clarity/motion/artifacts, then add all labels/numbers/captions via deterministic renderer. See `references/wan-i2v-retention-studio.md`.
   - When using NVIDIA Build/API Catalog for keyframes, prefer hosted `ai.api.nvidia.com` calls before considering self-hosted NIM deployment; implement a configurable model fallback chain, store the key as a runtime secret, and inspect/clean logos, emblems, license plates, and fake text before I2V. See `references/nvidia-keyframe-generation.md`.

6. **Social-media draft and content-preview approval packages before TTS/rendering polish**
   - Before TTS, platform APIs, or polished video generation, build a typed `SocialMediaDraft` from the approved candidate plus render manifest/preview.
   - Include platform targets, title variants, caption, hashtags, CTA, hook, duration, quality/risk summary, and preview reference.
   - Render this into a Telegram-safe approval message and wrap it in a side-effect-free delivery plan with `requires_human_approval=True` and `side_effects=()`.
   - After a local MP4 timing preview exists, combine the MP4 path plus social draft review into a side-effect-free content preview approval package. This package should carry `approval_language=de`, `final_video_language=en`, explicit approval commands, no external calls, and no side effects.
   - Add local demo CLIs that print metadata first, then review text; draft-only CLIs must not write files, call renderers, call Telegram, or access credentials. MP4 preview CLIs may write local artifacts and call FFmpeg only at explicit renderer boundaries that report side effects.
   - See `references/social-draft-approval-package.md` and `references/content-automation-patterns.md` for concrete artifact sequence, user-specific language rules, and external workflow patterns.

7. **Human approval loop before posting**
   - Send preview to the user through the requested channel, usually Telegram.
   - Include: video or preview reference, title, caption, hashtags, platform recommendation, duration, quality/risk score.
   - Wait for explicit approval such as `Freigabe alle`, `Freigabe YouTube`, `Freigabe TikTok`, `Ablehnen`, or `Ändern: ...`.
   - Do not publish automatically unless the user has explicitly authorized that workflow.

8. **TTS / voice layer after draft approval**
   - Treat TTS as a later layer, not the starting point for a first post draft.
   - Decide voice, final voiceover script, words-per-second, scene timing, caption sync, audio format, provider, and separate voice approval only after the social draft package is reviewable.
   - This avoids spending TTS/rendering effort on unapproved hooks, captions, or platform positioning.

9. **Publish and measure**
   Track at minimum:
   - platform, video id/url, niche, format, hook, duration
   - views at 2h/24h/7d
   - likes, comments, shares, saves, follows
   - retention metrics if available
   Use results to generate the next batch.

## Platform duration/monetization notes

See `references/monetization-and-approval-notes.md` for current working notes captured from official YouTube/TikTok docs and the recommended approval/posting setup. See `references/retention-led-shorts.md` for retention scoring signals, no-faith/adjacent-pillar handling, and voiceover finalization rules. See `references/render-manifest-pipeline.md` for the script → shot plan → render manifest → renderer sequence that prevents generic AI slideshow output. See `references/manifest-preview-renderer.md` for the first deterministic Pillow/FFmpeg preview-renderer pattern. See `references/local-preview-frame-writer.md` for the staged `LocalPreviewPlan → PNG scene cards → FFmpeg concat plan` boundary before MP4 rendering. See `references/social-draft-approval-package.md` for the typed first-post approval package to build before TTS or platform APIs. See `references/content-automation-patterns.md` for public workflow patterns, queue/status/cost tracking, versioned approval, synthetic-content disclosure, originality gates, and the user's German-preview/English-final rule. See `references/ai-video-asset-planning.md` for motion-led final-render asset planning with Kling/Hailuo/Runway-style clips and screen simulations. See `references/imported-ai-clip-assembly.md` for hybrid assembly where manually generated provider clips are imported into a deterministic local renderer. See `references/nvidia-keyframe-generation.md` for NVIDIA-hosted keyframe generation, model fallback, secret handling, and pre-I2V logo/plate cleanup. Re-check official docs before making business-critical decisions because platform programs change often.

## Project/repo hygiene for automated video bots

- Keep source code in Git; keep generated videos, assets, `.env`, OAuth tokens, API keys, and downloaded secrets out of Git.
- Use `.gitignore` aggressively for outputs, media caches, local credentials, and temp files.
- Store platform credentials as runtime secrets; never print raw tokens.
- Build the repo as modular components: idea generation, scripting, rendering, review, publishing, analytics.
- For approval-first Retention Studio implementations, use a typed side-effect-free core before real I/O: `ContentBrief`, `ScriptDraft`, `VideoCandidate`, review batches, Telegram delivery plans, then shot plans. See `references/approval-first-retention-studio-core.md`.

## Sensitive-content positioning

When AI/productivity automation is the primary brand, be cautious about mixing in religious/FaithTok, health, finance, or other high-trust themes. If the user flags the combination as sensitive, pause that pillar rather than forcing it into the same testbatch. Replace it with a safer adjacent pillar (for example `attention_design`) and document the paused pillar in config so it can be revisited only with explicit approval and a separate brand strategy.

## Pitfalls

- Do not over-optimize renderer quality before testing whether the content format earns retention.
- Do not let German approval previews become unreadable technical cards: use pixel-width-aware wrapping, readable fonts, limited line counts, and visual checks for clipped/overcrowded text.
- Do not confuse approval language with final audience language. For this user, approval previews are German by default; final social videos remain English unless explicitly changed.
- Do not copy public automation workflows wholesale: keep their queue/status/cost tracking and template structure, but remove blind auto-publishing until a human approval/version lock exists.
- Do not mass-generate repetitive shorts without originality/inauthentic-content gates; this can harm monetization/account health and create AI slop.
- Do not jump directly from a script idea to ComfyUI/Wan/Qwen/Image/video generation. First build and inspect the content-to-review spine so weak hooks, generic scripts, policy issues, and static visual concepts are killed before render time.
- Do not create a parallel candidate path for content experiments; convert content artifacts into the existing candidate factory input so IDs, hashtags, duration classes, quality gates, and pillar/platform validation stay centralized.
- Do not use `x or default` for optional content/script overrides in factories; explicit blank strings and zero durations must pass through to validation so tests can catch them.
- Do not assume TikTok monetization rules match YouTube: TikTok Creator Rewards emphasizes 1min+ eligible videos, while shorter videos may still be better for growth.
- Do not publish religious or health-adjacent content with overconfident claims; preserve humility, nuance, and platform safety.
- Do not rely on platform payout as the only monetization path; plan indirect monetization such as newsletter, templates, affiliates, digital products, or services.


Explanation:
**Considering subtitle implementation**

I need to continue implementing subtitles and TTS (text-to-speech). Additionally, there's a duplicate videos issue that I might need to address; it could be the same media. I should inspect the files to confirm what's going on, so I can take appropriate action. I want to ensure everything runs smoothly, so tackling these areas seems like a priority! I’ll break it down step by step for clarity.


Explanation:
**Addressing duplicate video issue**

I’m thinking the user mentioned a duplicate video was sent. I should definitely apologize for that and look into fixing it. Perhaps I can send the correct version with labels. Also, the phrase "mach weiter" suggests it’s time to proceed with the next steps for subtitles or TTS planning. I need to inspect the current files to make sure everything is in order. Let's get this sorted!


     1|"""Build top-3 TrueTrace preview approval packages with local MP4 previews."""
     2|
     3|from __future__ import annotations
     4|
     5|from dataclasses import dataclass
     6|from pathlib import Path
     7|
     8|from autoshorts.content.script_draft import ScriptDraft
     9|from autoshorts.demo.truetrace_concepts import build_truetrace_concept_batch
    10|from autoshorts.ideas.candidate import VideoCandidate
    11|from autoshorts.preview.package import ContentPreviewApprovalPackage, build_content_preview_approval_package
    12|from autoshorts.rendering.ffmpeg_plan import build_ffmpeg_concat_plan
    13|from autoshorts.rendering.ffmpeg_renderer import render_ffmpeg_preview_video
    14|from autoshorts.rendering.local_frame_writer import write_local_preview_frames
    15|from autoshorts.rendering.local_preview import build_local_preview_plan
    16|from autoshorts.rendering.manifest import build_render_manifest
    17|from autoshorts.rendering.preview import build_html_preview
    18|from autoshorts.rendering.shot_plan import create_shot_plan
    19|from autoshorts.social.draft import SocialMediaDraft, build_social_media_draft
    20|from autoshorts.social.manual_posting import ManualPostingPack, build_manual_posting_pack
    21|from autoshorts.social.review import render_social_media_draft_review
    22|
    23|TRUE_TRACE_TOP3_CANDIDATE_IDS = (
    24|    "truetrace-ai-automation-quiet-rot-38s",
    25|    "truetrace-prompt-vs-system-workflow-breaks-35s",
    26|    "truetrace-ai-notes-equal-importance-34s",
    27|)
    28|
    29|
    30|@dataclass(frozen=True)
    31|class TrueTracePreviewArtifact:
    32|    candidate: VideoCandidate
    33|    script: ScriptDraft
    34|    draft: SocialMediaDraft
    35|    package: ContentPreviewApprovalPackage
    36|    manual_posting_pack: ManualPostingPack
    37|    frame_paths: tuple[str, ...]
    38|
    39|
    40|@dataclass(frozen=True)
    41|class TrueTracePreviewBatchResult:
    42|    batch_id: str
    43|    artifacts: tuple[TrueTracePreviewArtifact, ...]
    44|    approval_language: str
    45|    final_video_language: str = "en"
    46|    side_effects: tuple[str, ...] = ("write_png", "write_concat", "call_ffmpeg", "write_mp4")
    47|    external_calls: tuple[str, ...] = ("ffmpeg",)
    48|
    49|
    50|def _slug_path(candidate_id: str) -> str:
    51|    return candidate_id.replace("/", "-")
    52|
    53|
    54|def _retention_beats(candidate: VideoCandidate) -> tuple[str, ...]:
    55|    if candidate.candidate_id == "truetrace-ai-automation-quiet-rot-38s":
    56|        return (
    57|            "Day one looks green: the demo output feels useful.",
    58|            "By day three the input has changed and the system has no exception path.",
    59|            "Show normal, weird, and uncertain inputs splitting into review and stop conditions.",
    60|            "Rule: no exception path means fragile automation.",
    61|        )
    62|    if candidate.candidate_id == "truetrace-prompt-vs-system-workflow-breaks-35s":
    63|        return (
    64|            "A glowing prompt card looks clever, then warning labels appear around it.",
    65|            "Reveal the missing pieces: input boundary, useful output standard, review step, action boundary.",
    66|            "Transform the prompt into a repeatable workflow diagram.",
    67|            "Rule: prompt is not system.",
    68|        )
    69|    if candidate.candidate_id == "truetrace-ai-notes-equal-importance-34s":
    70|        return (
    71|            "A clean notes page floods with equally weighted bullets.",
    72|            "Separate facts, decisions, risks, open questions, and next actions into visible columns.",
    73|            "Show why pretty summaries fail when they do not guide action.",
    74|            "Rule: ask for decision structure, not notes.",
    75|        )
    76|    return (
    77|        candidate.hook,
    78|        candidate.script_outline,
    79|        candidate.visual_concept,
    80|    )
    81|
    82|
    83|def _script_from_candidate(candidate: VideoCandidate) -> ScriptDraft:
    84|    return ScriptDraft(
    85|        brief_id=candidate.candidate_id,
    86|        title=candidate.title,
    87|        hook=candidate.hook,
    88|        script_outline=candidate.script_outline,
    89|        retention_beats=_retention_beats(candidate),
    90|        visual_concept=candidate.visual_concept,
    91|        caption=candidate.caption,
    92|        cta=candidate.cta,
    93|        estimated_duration_seconds=candidate.duration_seconds,
    94|    )
    95|
    96|
    97|def _top_candidates() -> tuple[VideoCandidate, ...]:
    98|    by_id = {candidate.candidate_id: candidate for candidate in build_truetrace_concept_batch().candidates}
    99|    return tuple(by_id[candidate_id] for candidate_id in TRUE_TRACE_TOP3_CANDIDATE_IDS)
   100|
   101|
   102|def _build_artifact(candidate: VideoCandidate, *, output_dir: Path, approval_language: str) -> TrueTracePreviewArtifact:
   103|    script = _script_from_candidate(candidate)
   104|    shot_plan = create_shot_plan(script)
   105|    manifest = build_render_manifest(shot_plan)
   106|    html_preview = build_html_preview(manifest)
   107|    preview_plan = build_local_preview_plan(manifest)
   108|
   109|    candidate_dir = output_dir / _slug_path(candidate.candidate_id)
   110|    frame_result = write_local_preview_frames(
   111|        preview_plan,
   112|        output_dir=candidate_dir / "frames",
   113|        approval_language=approval_language,
   114|    )
   115|    concat_plan = build_ffmpeg_concat_plan(preview_plan, frame_result, output_path=candidate_dir / "preview.mp4")
   116|    render_result = render_ffmpeg_preview_video(concat_plan)
   117|
   118|    draft = build_social_media_draft(candidate, html_preview)
   119|    social_review_text = render_social_media_draft_review(draft)
   120|    package = build_content_preview_approval_package(
   121|        draft=draft,
   122|        preview_video_path=render_result.output_path,
   123|        social_review_text=social_review_text,
   124|        approval_language=approval_language,
   125|        final_video_language="en",
   126|    )
   127|    manual_pack = build_manual_posting_pack(draft, video_path=render_result.output_path)
   128|    return TrueTracePreviewArtifact(
   129|        candidate=candidate,
   130|        script=script,
   131|        draft=draft,
   132|        package=package,
   133|        manual_posting_pack=manual_pack,
   134|        frame_paths=frame_result.frame_paths,
   135|    )
   136|
   137|
   138|def build_top_truetrace_preview_packages(
   139|    *,
   140|    output_dir: str | Path = "data/previews/truetrace-top3",
   141|    approval_language: str = "de",
   142|) -> TrueTracePreviewBatchResult:
   143|    """Render local timing previews and side-effect-free approval/manual packs for the top 3 concepts."""
   144|
   145|    out = Path(output_dir).expanduser().resolve()
   146|    artifacts = tuple(
   147|        _build_artifact(candidate, output_dir=out, approval_language=approval_language) for candidate in _top_candidates()
   148|    )
   149|    return TrueTracePreviewBatchResult(
   150|        batch_id="truetrace-top3-preview-packages-001",
   151|        artifacts=artifacts,
   152|        approval_language=approval_language,
   153|    )
   154|
   155|
   156|def render_top_truetrace_preview_summary(result: TrueTracePreviewBatchResult) -> str:
   157|    """Render a compact Telegram-safe summary of top-3 preview packages."""
   158|
   159|    lines = [
   160|        "## TrueTrace Top-3 Preview Packages",
   161|        f"Batch: {result.batch_id}",
   162|        f"Packages: {len(result.artifacts)}",
   163|        f"Approval language: {result.approval_language}",
   164|        f"Final video language: {result.final_video_language}",
   165|        f"Side effects: {', '.join(result.side_effects)}",
   166|        f"External calls: {', '.join(result.external_calls)}",
   167|        "",
   168|    ]
   169|    for index, artifact in enumerate(result.artifacts, start=1):
   170|        commands = ", ".join(artifact.package.approval_commands)
   171|        lines.extend(
   172|            [
   173|                f"## {index}. {artifact.candidate.title}",
   174|                f"Candidate: {artifact.candidate.candidate_id}",
   175|                f"Duration: {artifact.candidate.duration_seconds}s",
   176|                f"Preview video: {artifact.package.preview_video_path}",
   177|                f"Hook: {artifact.candidate.hook}",
   178|                f"Manual upload pack prepared: {'yes' if artifact.manual_posting_pack.requires_human_manual_upload else 'no'}",
   179|                f"Approval commands: {commands}",
   180|                "",
   181|            ]
   182|        )
   183|    text = "\n".join(lines).strip()
   184|    if len(text) > 4096:
   185|        raise ValueError("top TrueTrace preview summary exceeds Telegram message limit")
   186|    return text
   187|


     1|from pathlib import Path
     2|import shutil
     3|
     4|import pytest
     5|
     6|from autoshorts.demo.truetrace_preview_packages import (
     7|    TRUE_TRACE_TOP3_CANDIDATE_IDS,
     8|    build_top_truetrace_preview_packages,
     9|    render_top_truetrace_preview_summary,
    10|)
    11|
    12|
    13|pytestmark = pytest.mark.skipif(shutil.which("ffmpeg") is None, reason="ffmpeg is required for MP4 preview rendering")
    14|
    15|
    16|def test_build_top_truetrace_preview_packages_renders_three_mp4_approval_packages(tmp_path):
    17|    result = build_top_truetrace_preview_packages(output_dir=tmp_path, approval_language="de")
    18|
    19|    assert result.batch_id == "truetrace-top3-preview-packages-001"
    20|    assert [artifact.candidate.candidate_id for artifact in result.artifacts] == list(TRUE_TRACE_TOP3_CANDIDATE_IDS)
    21|    assert len(result.artifacts) == 3
    22|    assert result.side_effects == ("write_png", "write_concat", "call_ffmpeg", "write_mp4")
    23|    assert result.external_calls == ("ffmpeg",)
    24|
    25|    for artifact in result.artifacts:
    26|        assert artifact.package.requires_human_approval is True
    27|        assert artifact.package.approval_language == "de"
    28|        assert artifact.package.final_video_language == "en"
    29|        assert artifact.package.side_effects == ()
    30|        assert artifact.package.external_calls == ()
    31|        assert Path(artifact.package.preview_video_path).exists()
    32|        assert artifact.package.preview_video_path.endswith("preview.mp4")
    33|        assert artifact.manual_posting_pack.requires_human_manual_upload is True
    34|        assert artifact.manual_posting_pack.side_effects == ()
    35|        assert artifact.manual_posting_pack.external_calls == ()
    36|        assert artifact.manual_posting_pack.video_path == artifact.package.preview_video_path
    37|        assert tuple(post.platform for post in artifact.manual_posting_pack.platform_posts) == ("youtube", "tiktok")
    38|
    39|
    40|def test_build_top_truetrace_preview_packages_accepts_relative_output_dir(tmp_path, monkeypatch):
    41|    monkeypatch.chdir(tmp_path)
    42|
    43|    result = build_top_truetrace_preview_packages(output_dir="relative-previews", approval_language="de")
    44|
    45|    for artifact in result.artifacts:
    46|        assert Path(artifact.package.preview_video_path).is_absolute()
    47|        assert Path(artifact.package.preview_video_path).exists()
    48|
    49|
    50|def test_render_top_truetrace_preview_summary_is_telegram_safe_and_references_three_videos(tmp_path):
    51|    result = build_top_truetrace_preview_packages(output_dir=tmp_path, approval_language="de")
    52|
    53|    text = render_top_truetrace_preview_summary(result)
    54|
    55|    assert "## TrueTrace Top-3 Preview Packages" in text
    56|    assert "Packages: 3" in text
    57|    assert "Approval language: de" in text
    58|    assert "Final video language: en" in text
    59|    assert text.count("Preview video:") == 3
    60|    assert "Freigabe alle" in text
    61|    assert "Manual upload pack prepared: yes" in text
    62|    assert "publish" not in text.casefold()
    63|    assert "post" not in text.casefold()
    64|    assert len(text) < 4096
    65|


Explanation:
**Planning for implementation**

I need to implement the audio and subtitles tests for the tts_plan in the script. Maybe I should also verify if the videos differ, possibly checking their hashes? If the three videos are the same, we might have a media delivery issue, so producing a distinct labeled contact sheet could help. The user has asked to move on, so I should continue with the next build and run a git status to check the repository.

terminal

git status --short && git log --oneline -5


Output:
6efc4dc feat: add TrueTrace top preview packages
2d6af9f feat: add TrueTrace concept testbatch
b6c4fcc feat: add TrueTrace brand profile validation
3f39d10 feat: add manual posting packs and branding plan
e57e328 feat: add public-safe content series guardrails


Output:
032ed16e2433619aa48f587f51fc3a156117bec4cbf42881246be640cd57d012  data/previews/truetrace-top3/truetrace-ai-automation-quiet-rot-38s/preview.mp4
55f27f60ba439c97b2fc36eb34acda28fb2bf3d5bd99137ad7c019ab6d519c1e  data/previews/truetrace-top3/truetrace-ai-notes-equal-importance-34s/preview.mp4
47023552c9fb7b282248beaf3258b1f790248741d892868ee80bad4da6abaa6b  data/previews/truetrace-top3/truetrace-prompt-vs-system-workflow-breaks-35s/preview.mp4


Explanation:
**Evaluating status output**

I’m noticing that the status output seems to lack some information — it only logs because perhaps the command doesn’t have a clear separator. That’s something to consider! I think I should write tests to check this out more thoroughly. I also need to inspect the data models and possibly improve the style as well. It looks like there’s a few things that could be tweaked to make it clearer and more effective!


File unchanged since last read. The content from the earlier read_file result in this conversation is still current — refer to that instead of re-reading.


     1|"""Side-effect-free render manifest generation from shot plans."""
     2|
     3|from __future__ import annotations
     4|
     5|from dataclasses import dataclass
     6|
     7|from autoshorts.ideas.candidate import ValidationResult
     8|from autoshorts.rendering.shot_plan import ShotPlan, validate_shot_plan
     9|
    10|
    11|@dataclass(frozen=True)
    12|class RenderSettings:
    13|    width: int = 1080
    14|    height: int = 1920
    15|    fps: int = 30
    16|    no_generated_text_inside_assets: bool = True
    17|    captions_renderer_owned: bool = True
    18|    static_card_limit: int = 0
    19|
    20|
    21|@dataclass(frozen=True)
    22|class RenderLayer:
    23|    text: str
    24|    renderer_owned: bool = True
    25|
    26|
    27|@dataclass(frozen=True)
    28|class RenderTimelineItem:
    29|    scene_purpose: str
    30|    start_seconds: int
    31|    end_seconds: int
    32|    asset_mode: str
    33|    motion_instruction: str
    34|    placeholder_asset_path: str
    35|    caption_layer: RenderLayer
    36|    label_layer: RenderLayer
    37|
    38|
    39|@dataclass(frozen=True)
    40|class RenderManifest:
    41|    script_id: str
    42|    title: str
    43|    settings: RenderSettings
    44|    timeline: tuple[RenderTimelineItem, ...]
    45|
    46|
    47|def _is_blank(value: str) -> bool:
    48|    return not (value or "").strip()
    49|
    50|
    51|def _placeholder_path(script_id: str, index: int, purpose: str) -> str:
    52|    return f"placeholders/{script_id}/{index:02d}-{purpose}.png"
    53|
    54|
    55|def build_render_manifest(plan: ShotPlan) -> RenderManifest:
    56|    """Turn a validated shot plan into a machine-readable render manifest."""
    57|
    58|    validation = validate_shot_plan(plan)
    59|    if not validation.is_valid:
    60|        raise ValueError("; ".join(validation.errors))
    61|
    62|    timeline = tuple(
    63|        RenderTimelineItem(
    64|            scene_purpose=scene.purpose,
    65|            start_seconds=scene.start_seconds,
    66|            end_seconds=scene.end_seconds,
    67|            asset_mode=scene.asset_mode,
    68|            motion_instruction=scene.visual_action,
    69|            placeholder_asset_path=_placeholder_path(plan.script_id, index, scene.purpose),
    70|            caption_layer=RenderLayer(text=scene.caption_beat, renderer_owned=True),
    71|            label_layer=RenderLayer(text=scene.purpose.replace("_", " "), renderer_owned=True),
    72|        )
    73|        for index, scene in enumerate(plan.scenes, start=1)
    74|    )
    75|    return RenderManifest(
    76|        script_id=plan.script_id,
    77|        title=plan.title,
    78|        settings=RenderSettings(),
    79|        timeline=timeline,
    80|    )
    81|
    82|
    83|def validate_render_manifest(manifest: RenderManifest) -> ValidationResult:
    84|    errors: list[str] = []
    85|    if _is_blank(manifest.script_id):
    86|        errors.append("script_id is required")
    87|    if _is_blank(manifest.title):
    88|        errors.append("title is required")
    89|
    90|    settings = manifest.settings
    91|    if (settings.width, settings.height) != (1080, 1920):
    92|        errors.append("render size must be vertical 1080x1920")
    93|    if settings.fps != 30:
    94|        errors.append("fps must be 30")
    95|    if not settings.no_generated_text_inside_assets:
    96|        errors.append("generated text inside assets is not allowed")
    97|    if not settings.captions_renderer_owned:
    98|        errors.append("captions must be renderer-owned")
    99|    if settings.static_card_limit != 0:
   100|        errors.append("static_card_limit must be 0")
   101|
   102|    if not manifest.timeline:
   103|        errors.append("at least one timeline item is required")
   104|
   105|    previous_end: int | None = None
   106|    for index, item in enumerate(manifest.timeline, start=1):
   107|        if _is_blank(item.scene_purpose):
   108|            errors.append(f"timeline item {index} scene_purpose is required")
   109|        if item.end_seconds <= item.start_seconds:
   110|            errors.append(f"timeline item {index} end_seconds must be greater than start_seconds")
   111|        if previous_end is not None and item.start_seconds < previous_end:
   112|            errors.append(f"timeline item {index} start_seconds overlaps previous item")
   113|        previous_end = item.end_seconds
   114|
   115|        for field_name, value in {
   116|            "asset_mode": item.asset_mode,
   117|            "motion_instruction": item.motion_instruction,
   118|            "placeholder_asset_path": item.placeholder_asset_path,
   119|            "caption_layer text": item.caption_layer.text,
   120|            "label_layer text": item.label_layer.text,
   121|        }.items():
   122|            if _is_blank(value):
   123|                errors.append(f"timeline item {index} {field_name} is required")
   124|
   125|        if item.asset_mode == "static_slideshow":
   126|            errors.append("static slideshow asset mode is not allowed")
   127|        if "baked-in" in item.motion_instruction.casefold():
   128|            errors.append("baked-in text in render assets is not allowed")
   129|        if not item.caption_layer.renderer_owned:
   130|            errors.append(f"timeline item {index} caption layer must be renderer-owned")
   131|        if not item.label_layer.renderer_owned:
   132|            errors.append(f"timeline item {index} label layer must be renderer-owned")
   133|
   134|    return ValidationResult(is_valid=not errors, errors=tuple(errors))
   135|


     1|# AutoShortsBot
     2|
     3|AutoShortsBot is a semi-automatic short-form content engine for high-quality YouTube Shorts, TikTok, and Instagram Reels.
     4|
     5|The project is deliberately approval-first: it can draft, score, render previews, and prepare publishing metadata, but it must not publish anything without explicit human approval.
     6|
     7|## Current scope
     8|
     9|Implemented foundation:
    10|
    11|- `autoshorts.cli.demo_local_preview_video` — local CLI entry point that renders deterministic PNG frames and an MP4 timing preview.
    12|- `autoshorts.cli.demo_render_preview` — local CLI entry point that prints side-effect-free render-preview metadata plus HTML.
    13|- `autoshorts.cli.demo_review_batch` — local CLI entry point that prints the deterministic review demo.
    14|- `autoshorts.cli.demo_social_draft` — local CLI entry point that prints a side-effect-free social media draft review delivery.
    15|- `autoshorts.cli.demo_telegram_delivery` — local CLI entry point that prints side-effect-free Telegram delivery metadata plus review text.
    16|- `autoshorts.config.pillars` — content pillar registry, default AI/life-systems positioning, and pillar fit validation.
    17|- `autoshorts.content.brief` — strategy brief model for retention-led content development before scripting.
    18|- `autoshorts.content.demo_pipeline` — deterministic content-to-Telegram-review demo pipeline.
    19|- `autoshorts.content.script_draft` — structured script drafts with retention beats, visual concept, caption, CTA, and AI-slop validation.
    20|- `autoshorts.content.to_candidate` — converts content briefs and script drafts into candidate drafts and validated video candidates.
    21|- `autoshorts.demo.sample_batch` — deterministic AI-life-systems review demo with ready, blocked, and invalid candidates.
    22|- `autoshorts.demo.truetrace_concepts` — five concrete TrueTraceShorts concept candidates with metadata for strategy review.
    23|- `autoshorts.demo.truetrace_preview_packages` — renders top-3 TrueTrace local MP4 timing previews plus approval/manual upload packages.
    24|- `autoshorts.ideas.candidate` — structured video candidate model and validation.
    25|- `autoshorts.ideas.factory` — draft-to-candidate factory with stable ids, default hashtags, and duration classes.
    26|- `autoshorts.ideas.testbatch` — testbatch validation and renderable-candidate filtering.
    27|- `autoshorts.preview.package` — combines MP4 timing preview plus social draft review into an approval package.
    28|- `autoshorts.review.approvals` — deterministic Telegram approval command parser.
    29|- `autoshorts.strategy.brand` — TrueTraceShorts brand profile, default descriptions/bios/tags, and brand-fit validation.
    30|- `autoshorts.strategy.score` — follower-conversion strategy scoring for hook, curiosity, identity, emotion, trust, follow reason, originality, and variety.
    31|- `autoshorts.strategy.series` — public-safe series registry and hard no-go validation before public content advances.
    32|- `autoshorts.rendering.ffmpeg_plan` — side-effect-free FFmpeg concat planning for local preview frames.
    33|- `autoshorts.rendering.ffmpeg_renderer` — explicit FFmpeg boundary that writes concat files and MP4 timing previews.
    34|- `autoshorts.rendering.local_frame_writer` — deterministic Pillow PNG scene-card writer for local preview plans.
    35|- `autoshorts.rendering.local_preview` — side-effect-free local preview frame planning from render manifests.
    36|- `autoshorts.rendering.manifest` — side-effect-free vertical render manifest generation from shot plans.
    37|- `autoshorts.rendering.preview` — side-effect-free HTML timeline previews for render manifests.
    38|- `autoshorts.rendering.shot_plan` — retention-led shot planner before render manifests or video generation.
    39|- `autoshorts.review.package` — Telegram-friendly review package renderer for human approval.
    40|- `autoshorts.social.delivery` — side-effect-free delivery plan for social media draft review text.
    41|- `autoshorts.social.draft` — approval-first social media draft model from candidate plus render preview.
    42|- `autoshorts.social.manual_posting` — side-effect-free manual upload copy pack for phase-1 YouTube/TikTok metadata.
    43|- `autoshorts.social.review` — Telegram-safe approval text renderer for social media drafts.
    44|- `autoshorts.review.render` — renders complete review batches into Telegram-safe text.
    45|- `autoshorts.review.workflow` — splits candidates into ready, blocked, and invalid review buckets.
    46|- `autoshorts.telegram.review_sender` — validates Telegram review text and prepares side-effect-free delivery plans.
    47|- `autoshorts.scripting.quality` — conservative pre-render quality gate.
    48|- Tests for content briefs, content demo pipeline, content-to-candidate conversion, content pillars, candidate factory, idea candidates, script drafts, shot plans, testbatches, sample review CLI, sample Telegram delivery CLI, sample review demos, review workflow, review-batch rendering, approval parsing, review formatting, Telegram delivery preparation, and script quality gating.
    49|
    50|## Brand profile
    51|
    52|`autoshorts.strategy.brand.TRUETRACE_SHORTS_BRAND` captures the phase-1 public brand contract for `True Trace Shorts`:
    53|
    54|- channel name and handles;
    55|- tagline: `Systems over prompts.`;
    56|- public YouTube description and TikTok bio;
    57|- default hashtags and YouTube tags;
    58|- tone and visual rules;
    59|- forbidden public topics;
    60|- generic AI-tips patterns;
    61|- mechanism keywords for brand fit.
    62|
    63|`validate_candidate_brand_fit()` rejects candidates that violate no-go topics, generic AI-tips positioning, or the mechanisms-over-hype promise. `apply_brand_metadata()` appends default hashtags and brand policy notes without changing the candidate content or performing side effects.
    64|
    65|`autoshorts.demo.truetrace_concepts.build_truetrace_concept_batch()` provides the current five-candidate TrueTrace concept testbatch for strategy review:
    66|
    67|- `Why Most AI Automations Break After Three Days`
    68|- `This Prompt Looks Smart — But the Workflow Still Breaks`
    69|- `The Hidden Reason Your AI Notes Become Useless`
    70|- `Claim: AI Agents Will Replace Teams`
    71|- `One Rule Before You Automate Anything With AI`
    72|
    73|`render_truetrace_concept_summary()` renders those candidates with hooks, outlines, visuals, hypotheses, YouTube descriptions, TikTok captions, and tags. It performs no platform action and avoids action language.
    74|
    75|`autoshorts.demo.truetrace_preview_packages.build_top_truetrace_preview_packages()` turns the top three concepts into local German approval timing previews and side-effect-free approval/manual-upload packages. It writes PNG scene cards and MP4 timing previews under the selected output directory, then prepares `ContentPreviewApprovalPackage` and `ManualPostingPack` artifacts for each candidate.
    76|
    77|Subtitle/TTS strategy is documented in `docs/subtitle_tts_strategy.md`: use deterministic Hormozi-inspired renderer-owned captions; start TTS with EdgeTTS English baseline; A/B test NVIDIA Magpie later; use Whisper for alignment/QA, not voice generation.
    78|
    79|## Strategy scoring
    80|
    81|`autoshorts.strategy.score.ContentStrategyScore` evaluates whether a candidate can move beyond generic AI tips and plausibly convert viewers into followers. It scores:
    82|
    83|- hook;
    84|- curiosity;
    85|- identity;
    86|- emotion;
    87|- trust;
    88|- follow reason;
    89|- originality;
    90|- variety.
    91|
    92|`build_candidate_strategy_review()` returns a `CandidateStrategyReview` with `render_allowed`, reject reasons, and improvement suggestions. It blocks generic AI-slop language, weak future-value promises, forbidden public topics, and recent format repetition before render capacity is wasted.
    93|
    94|`autoshorts.strategy.series.DEFAULT_CONTENT_SERIES` currently exposes only public-safe series:
    95|
    96|- `Prompt vs System`
    97|- `AI System Autopsy`
    98|- `One Rule, One Example`
    99|- `AI Hype Court`
   100|
   101|`validate_candidate_series_fit()` rejects public content about JARVIS/Hermes, the user, their collaboration, private/behind-the-scenes material, politics, legal loopholes, private-data misuse, rule-bypassing, exploitation, or anything that could enable lawbreaking.
   102|
   103|## Content development
   104|
   105|`autoshorts.content.brief.create_content_brief()` turns a pillar, topic, audience problem, and payoff into a deterministic `ContentBrief`. The brief carries positioning, tone, visual rules, no-go topics, target platforms, and target duration before any script or renderer exists.
   106|
   107|`validate_content_brief()` rejects missing strategy fields, unsupported platforms, and durations outside `10..180` seconds.
   108|
   109|`autoshorts.content.script_draft.create_script_draft()` turns a `ContentBrief` into a structured `ScriptDraft` with:
   110|
   111|- exact hook;
   112|- script outline;
   113|- 3–5 retention beats;
   114|- visual concept;
   115|- caption and CTA;
   116|- estimated duration.
   117|
   118|`validate_script_draft()` rejects missing core fields, invalid beat counts, invalid durations, and generic AI-slop phrases such as `in this video` or `unlock your full potential`.
   119|
   120|`autoshorts.content.to_candidate.convert_script_draft_to_candidate_draft()` turns a validated `ContentBrief` + `ScriptDraft` pair into the existing `CandidateDraft` type. It preserves platform strategy, hook, script outline, visual concept, caption, CTA, no-go policy notes, and an initial conservative quality score.
   121|
   122|`convert_script_draft_to_candidate()` then uses the existing candidate factory to create a validated `VideoCandidate`, including stable candidate id generation, default hashtags, duration classification, and pillar/platform validation.
   123|
   124|`autoshorts.content.demo_pipeline.build_ai_life_systems_demo_delivery()` runs the deterministic demo path end-to-end:
   125|
   126|```text
   127|ContentBrief
   128|→ ScriptDraft
   129|→ VideoCandidate
   130|→ TestBatch
   131|→ ReviewBatch
   132|→ Telegram ReviewDelivery
   133|```
   134|
   135|The result object exposes every intermediate artifact for inspection and still has no send/publish side effects.
   136|
   137|## Render planning
   138|
   139|`autoshorts.rendering.shot_plan.create_shot_plan()` turns a validated `ScriptDraft` into a deterministic five-scene retention plan:
   140|
   141|```text
   142|first_frame_hook
   143|→ pattern_interrupt
   144|→ core_explanation
   145|→ screen_or_card_visual
   146|→ cta
   147|```
   148|
   149|Each `ShotScene` carries timing, voiceover beat, visual action, asset mode, caption beat, retention goal, and whether labels/captions remain renderer-owned.
   150|
   151|`validate_shot_plan()` rejects static slideshow asset modes, baked-in fake UI text, non-renderer-owned labels/captions, bad timing, missing fields, and plans whose first scene is not the first-frame hook.
   152|
   153|`autoshorts.rendering.manifest.build_render_manifest()` turns a validated `ShotPlan` into a machine-readable `RenderManifest` with:
   154|
   155|- vertical `1080x1920` render settings;
   156|- `30` fps;
   157|- one timeline item per shot scene;
   158|- motion instructions inherited from the shot plan;
   159|- deterministic placeholder asset paths;
   160|- renderer-owned caption and label layers;
   161|- `no_generated_text_inside_assets=True`;
   162|- `static_card_limit=0`.
   163|
   164|`validate_render_manifest()` rejects unsafe render settings, generated text inside assets, non-renderer-owned captions/labels, static slideshow timeline items, missing timeline fields, and bad timeline timing. The manifest is still a preparation artifact only; it renders nothing and calls no external API.
   165|
   166|`autoshorts.rendering.preview.build_html_preview()` turns a validated `RenderManifest` into a side-effect-free `RenderPreview` HTML document. It exposes script metadata, vertical format, fps, every timeline item, placeholder asset paths, motion instructions, and renderer-owned caption/label text for local inspection before any media generation.
   167|
   168|`validate_render_preview()` rejects blank previews, wrong preview types, missing HTML documents, side effects, and external renderer calls. Preview generation still does not write files, render videos, call ComfyUI, or touch Telegram.
   169|
   170|Run the same deterministic preview locally with:
   171|
   172|```bash
   173|python -m autoshorts.cli.demo_render_preview
   174|```
   175|
   176|It prints preview metadata first, then the HTML document. Redirect stdout to a local `.html` file if you want to inspect it in a browser; the CLI itself still writes nothing.
   177|
   178|`autoshorts.rendering.local_preview.build_local_preview_plan()` turns a validated `RenderManifest` into a deterministic `LocalPreviewPlan` for later frame/video generation. The default plan uses reduced local preview settings `540x960` at `24fps`, creates one `LocalPreviewFrame` per manifest timeline item, converts scene timing to milliseconds, and assigns deterministic placeholder image paths under `data/previews/<script_id>/`.
   179|
   180|`validate_local_preview_plan()` rejects non-vertical preview sizes, non-positive fps, blank output paths, empty frame lists, missing frame fields, bad frame durations, non-zero first-frame start time, overlapping frames, side effects, and external tool calls. This is still a plan only: it writes no PNGs, calls no FFmpeg, and renders no MP4.
   181|
   182|`autoshorts.rendering.local_frame_writer.write_local_preview_frames()` is the first explicit artifact-writing boundary. It consumes a validated `LocalPreviewPlan` and writes deterministic PNG scene cards using Pillow. Each card uses manifest-derived frame data: scene purpose, caption, label, timing, asset mode, and motion instruction. It returns `LocalFrameWriteResult` with `side_effects=("write_png",)` and `external_calls=()`.
   183|
   184|Approval-preview cards default to German copy (`approval_language="de"`) so the concept is easier to review before final English production. Text wrapping is pixel-width based rather than fixed character-count based, which keeps long German lines inside the vertical card bounds.
   185|
   186|Generated preview frames should stay under runtime preview directories such as `data/previews/<script_id>/` or temporary test directories. PNG media is ignored by Git.
   187|
   188|`autoshorts.rendering.ffmpeg_plan.build_ffmpeg_concat_plan()` turns a `LocalPreviewPlan` plus `LocalFrameWriteResult` into a side-effect-free `FFmpegConcatPlan`. It records ordered PNG paths, per-frame durations, output `.mp4` path, derived `.concat.txt` path, preview dimensions, fps, and FFmpeg concat demuxer file content.
   189|
   190|`validate_ffmpeg_concat_plan()` rejects mismatched or missing values, non-`.mp4` output paths, non-`.concat.txt` concat paths, non-positive dimensions/fps, missing entries, non-PNG frame paths, non-positive durations, side effects, and external tool calls. This layer still does not write the concat file and does not call FFmpeg.
   191|
   192|`autoshorts.rendering.ffmpeg_renderer.render_ffmpeg_preview_video()` is the explicit local video-rendering boundary. It consumes a validated `FFmpegConcatPlan`, writes the `.concat.txt` file, calls `ffmpeg`, and writes the `.mp4` timing preview. It returns `FFmpegRenderResult` with `side_effects=("write_concat", "call_ffmpeg", "write_mp4")` and `external_calls=("ffmpeg",)`.
   193|
   194|The renderer exports `yuv420p` MP4 with `+faststart`. Tests skip if `ffmpeg`/`ffprobe` are unavailable and otherwise smoke-probe width, height, frame rate, and approximate duration.
   195|
   196|Run the deterministic local MP4 timing preview with:
   197|
   198|```bash
   199|python -m autoshorts.cli.demo_local_preview_video --out-dir data/previews/demo-local-preview
   200|```
   201|
   202|The command creates German approval-preview PNG scene cards under `frames/`, writes `preview.concat.txt`, renders `preview.mp4`, and prints script/output metadata. It performs no Telegram calls, no platform calls, and no AI/API calls. Final social-media videos can still be produced in English later; this preview layer is German for concept review.
   203|
   204|## Content preview package
   205|
   206|`autoshorts.preview.package.build_content_preview_approval_package()` combines an existing MP4 timing preview with the social media draft review text into a side-effect-free `ContentPreviewApprovalPackage`. It records approval language `de`, final video language `en`, approval commands, preview video path, and requires human approval.
   207|
   208|`render_content_preview_approval_text()` creates a Telegram-safe text block that references the MP4 preview and embeds the social media draft review. The package layer does not send Telegram messages, publish to platforms, call renderers, or access credentials.
   209|
   210|Research notes for external content-automation patterns are tracked in `docs/research/content_automation_research.md`. Growth strategy notes are tracked in `docs/research/content_growth_strategy_research.md`. TrueTraceShorts branding recommendations are tracked in `docs/branding_truetrace_strategy.md`. The operating business plan for a JARVIS-run, human-approved content machine is tracked in `docs/businessplan_jarvis_content_machine.md`. Together they emphasize series/queues, editable previews, versioned approval, originality gates, synthetic-content disclosure metadata, follower-conversion psychology, variety inside a stable promise, manual phase-1 uploads with prepared metadata, and avoiding blind auto-posting.
   211|
   212|## Social media draft
   213|
   214|`autoshorts.social.draft.build_social_media_draft()` turns a validated `VideoCandidate` plus `RenderPreview` into an approval-first `SocialMediaDraft` containing:
   215|
   216|- candidate id;
   217|- target platforms and human-readable platform labels;
   218|- title variants;
   219|- caption, hashtags, CTA, and hook;
   220|- planned duration;
   221|- quality/risk summary;
   222|- preview reference and preview type;
   223|- `requires_human_approval=True`;
   224|- `side_effects=()`.
   225|
   226|`validate_social_media_draft()` rejects unsupported platforms, blank required fields, missing titles/hashtags, invalid durations, missing human approval, and any side effects. This is the first platform-facing post draft, but still performs no upload, no Telegram call, no rendering, and no credential access.
   227|
   228|`autoshorts.social.review.render_social_media_draft_review()` turns a valid `SocialMediaDraft` into a Telegram-safe approval message with candidate id, platforms, duration, hook, title variants, caption, hashtags, CTA, preview reference, quality/risk summary, explicit human-approval status, and approval commands:
   229|
   230|- `Freigabe YouTube`
   231|- `Freigabe TikTok`
   232|- `Freigabe Instagram`
   233|- `Freigabe alle`
   234|- `Ablehnen`
   235|- `Ändern: <requested change>`
   236|
   237|The renderer avoids publish/post language and keeps approval semantics separate from any future platform action.
   238|
   239|`autoshorts.social.delivery.prepare_social_draft_delivery()` wraps social draft review text in a side-effect-free `SocialDraftDelivery` object with delivery type `social_draft_review`, topic metadata, `requires_human_approval=True`, and `side_effects=()`. It validates non-empty text, Telegram's 4096-character limit, and the same no publish/post language boundary.
   240|
   241|

Use offset=241 to continue reading (showing 1-240 of 405 lines)


from autoshorts.audio.subtitles import (
    SubtitleCue,
    SubtitleStyle,
    build_hormozi_subtitle_track,
    validate_subtitle_track,
)

def test_build_hormozi_subtitle_track_chunks_text_into_renderer_owned_safe_cues():
    track = build_hormozi_subtitle_track(
        "Most AI automations do not fail dramatically. They quietly rot when the workflow has no exception path.",
        total_duration_seconds=8,
    )

    assert track.renderer_owned is True
    assert track.style == SubtitleStyle.hormozi_inspired()
    assert track.side_effects == ()
    assert track.external_calls == ()
    assert track.cues[0].start_ms == 0
    assert track.cues[-1].end_ms == 8000
    assert all(2 <= len(cue.text.split()) <= 5 for cue in track.cues)
    assert all(cue.active_word in cue.text.upper().split() for cue in track.cues)
    assert all(cue.max_lines == 2 for cue in track.cues)
    assert all(cue.renderer_owned for cue in track.cues)

def test_build_hormozi_subtitle_track_uses_continuous_timing_and_emphasis_metadata():
    track = build_hormozi_subtitle_track(
        "Prompt is not system. Workflow boundaries create reliable automation.",
        total_duration_seconds=6,
    )

    previous_end = 0
    for cue in track.cues:
        assert cue.start_ms == previous_end
        assert cue.end_ms > cue.start_ms
        assert cue.emphasis_color == track.style.accent_color
        previous_end = cue.end_ms
    assert previous_end == 6000

def test_validate_subtitle_track_rejects_non_renderer_owned_and_overcrowded_cues():
    cue = SubtitleCue(
        text="THIS CUE HAS FAR TOO MANY WORDS",
        start_ms=0,
        end_ms=1000,
        active_word="TOO",
        renderer_owned=False,
    )
    track = build_hormozi_subtitle_track("short valid text", total_duration_seconds=2).with_cues((cue,))

    validation = validate_subtitle_track(track)

    assert validation.is_valid is False
    assert "subtitle cue 1 must be renderer-owned" in validation.errors
    assert "subtitle cue 1 must contain 2..5 words" in validation.errors

def test_build_hormozi_subtitle_track_rejects_blank_text_and_bad_duration():
    for text, duration in [("", 3), ("valid text", 0)]:
        try:
            build_hormozi_subtitle_track(text, total_duration_seconds=duration)
        except ValueError as exc:
            assert str(exc)
        else:
            raise AssertionError("expected ValueError")


LSP diagnostics introduced by this edit:
<diagnostics file="/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/tests/test_subtitle_cues.py">
ERROR [1:6] Import "autoshorts.audio.subtitles" could not be resolved [reportMissingImports] (Pyright)
</diagnostics>