Pipeline
Liest die DNA und baut daraus das Video
🛣️ Die Straße – Migration von Fork-pro-Kanal auf EINEN parametrischen Workflow-Satz
Status: Richtung entschieden von Leon am 27.07.2026 (HZ-363). Kein Fork pro Kanal mehr – EINE parametrische Kette, durch die jeder Kanal mit seiner DNA/seinem Rezept läuft. Dies ist die einzige Quelle für den Migrationsplan.docs/fabrik_blaupause.mdH4 (Weg A/B) verweist hierher; dort steht nur noch das Modell, nicht mehr die Abwägung. Verlauf/Changelog →docs/bereiche/youtube.md. Tickets → HZ-363 + Folgestufen.
1 · Warum das kein Risiko ist: die Vielfalt ist Werte-Vielfalt, keine Graph-Vielfalt
Leons Sorge beim Entscheid: „kritisch bei so vielen Arten – Stock, KI, Pexels etc.? Aber man kann ja schon noch angeben, welcher Prompt, welche Maße, welche Stills."
Gemessen am echten n8n-Stand am 27.07.2026 (102 Workflows, 64 aktiv; Messung read-only über die Public-API, Node-Namen + Parameter-Hashes je Stufe):
| Stufe | Kanäle | Struktur-Übereinstimmung (Jaccard über Node-Namen) | Nodes |
|---|---|---|---|
| publisher | 8 | 1.00 – byte-gleich | 10 |
| p5 | 9 | 1.00 – byte-gleich | 8 |
| p3 | 15 | Median 1.00 (16 gemeinsame Nodes) | 3–17 |
| p4 | 12 | Median 0.79 – 21 Kern-Nodes von 36 | 21–36 |
| p1 | 15 | Median 0.50 | 12–19 |
| p2 | 15 | Median 0.39 (Union 162) | 31–78 |
Bei den 10 Zwillings-Kanälen (history, psych, cs, wisdom, geo, survival, doomed, worldcup, alvaro_horror, auswandern_rente_de) sind die Node-Namen je Stufe deckungsgleich. Abweichend sind nur Parameter-Werte:
| Stufe | gemeinsame Nodes | parameter-identisch | abweichend |
|---|---|---|---|
| p2 | 43 | 26 | 17 |
| p4 | 21 | 9 | 12 |
| p3 | 16 | 10 | 6 |
| p1 | 9 | 1 | 8 (fast nur Prompt-Texte) |
Und die „vielen Arten" gibt es nicht. Alle Kanäle tragen dieselbe Quellen-Kette – ApiMart-KI-Zweig + Pexels→Pixabay→Wikimedia→Unsplash, optional getty / harvest-gw / openverse. Pro Kanal ist nur eine andere Teilmenge scharf, und welche Quelle einen Beat bedient, entscheidet schon heute der Claude-Node „Stock Query Router" zur Laufzeit (media=ai|stock je Beat). Die Quellen-Liste ist bereits ein Rezept-Feld (recipe.harvest_sources, wf_fork/patch_harvest_sources.py).
Die beiden scheinbaren Sonderfälle sind keine:
- stickman (78 Nodes, 52 „eigene"): dieselbe KI-Kette 8× entrollt –
AI Image 1..8je
Submit→Wait→Status→Done?→Download→R2-Upload – statt in einem Loop.
- reddit: dieselbe ApiMart-Kette unter anderen Node-Namen („ApiMart - Submit Image" statt
„ApiMart Submit").
Was heute je Kanal wirklich abweicht, ist genau die Liste, die Leon behalten will: Webhook-Pfad · R2-Prefix · Trigger-URL · Voice-ID (Algrow-Node) · Router-Prompt = welche Stills · Skript-Prompt + Wortziel = welche Maße · Thumbnail-Prompt · Store-Key · Telegram-Text. Das sind Rezept-Felder, keine Workflow-Varianten.
2 · Das Muster existiert im Haus – n8n ist die letzte geforkte Schicht
- 6 Gateways laufen schon parametrisch geteilt:
pexels-search,video-db,claude-gen,
algrow, algrow-tts, tg-notify – je EIN Webhook-Pfad, von allen Kanälen gerufen.
- 5 Render-Container bedienen 15 Kanäle (
render-psychologyallein 6). Der Kanal kommt als
channel / recipe_key im Body; fehlt channel, gibt es 400 (Constraint #11, HZ-814).
- 0 Sub-Workflow-Nodes (
executeWorkflow) im ganzen System – verkettet wird über HTTP-Webhooks.
Die Straße braucht also kein neues Muster, nur konsequente Anwendung des vorhandenen.
Wartungslast heute: 53 wf_fork/patch_*.py, davon ~20 mit hartkodierten Kanal-Listen/WF-ID-Maps. Jede neue Stellschraube = 1 Patch × N Kanäle × Constraint #7 (deactivate+activate) je Workflow. Jeder neue Kanal = 6 weitere Workflows, die alle künftigen Patches mitschleppen.
3 · Zielbild
Die fünf Regeln, die den Umbau tragen
- Kanal-Identität nur aus dem Pfad. Sie steht beim Aufrufer an genau einer Stelle, erscheint im
Log, und unbekannter Kanal = Abbruch vor der ersten Geldstelle.
- Der Bauplan wird nie mitgeschickt, immer nachgeschlagen. Ein Aufrufer kann kein Rezept fälschen.
- Kein stiller Default. Kein
||/??in irgendeinem Body. Fehlendes Feld →stopAndError+
tg-notify. (Lesson HZ-814: genau so überlebte "channel":"HISTORY" als Default monatelang.)
- Alias-Auflösung zentral. Jeder Kanal hat heute drei Schreibweisen – Registry-Key
geo,
store_channel: emptyearth, youtube.channel_param: GEO. (Bei cs sind store+YT bewusst history = der CS-Umbau, kein Bug.) Der Bauplan liefert alle drei explizit; kein Node bastelt sie sich selbst zusammen.
- Der Aktiv-Schalter ist Konfiguration. Heute lebt „läuft / läuft nicht" im n8n-
active-Flag plus
node-disabled Cron (alle P1-Crons stehen auf 0 16 /2 *). 6 Workflows können keine 15×6 Zustände tragen → enabled + cadence je Kanal/Stufe in registry.json, bevor irgendetwas umgeschaltet wird. Sonst ist am Umschalttag jeder Registry-Kanal „an" = 15× ApiMart parallel.
DNA-getrieben, aber ratefrei (Leons Entscheid + sein eigener Einwand)
Leon: „schon mit DNA direkt – das Problem ist halt, dass die DNAs bei den meisten Channels noch nicht ausgereift sind; alvaro ist am fortgeschrittensten." Gemessen ist das noch schärfer:
| DNA-Reife (gemessen 27.07.) | Kanäle |
|---|---|
| DNA vorhanden | 4 – alvaro_horror, auswandern_rente_de, geo, history |
| keine DNA, Rezept ist 350–600-Byte-Stub | 11 – cs, psych, wisdom, survival, doomed, worldcup, reddit, stickman, aviation, business, maritime |
shots_per_min maschinenlesbar | 0 – die Werte sind Prosa: "unverifiziert", "15-30 (veri…", "stark templ…" |
Auflösung: Die Straße rechnet immer aus dem Bauplan – nirgends ein Literal (H7-Gesetz). Aber sie rät nie. Jedes Bauplan-Feld trägt eine provenance:
dna– aus der DNA gerechnet (heute: alvaro/geo/history/rente, soweit maschinenlesbar)pinned– heutiger Wert bewusst eingefroren, weil die DNA-Stelle fehlt/Prosa ist (sichtbare Lücke)- fehlt ganz → harter Stop, kein Default
Damit fährt ein reifer Kanal sofort DNA-getrieben, die Stub-Kanäle fahren sichtbar eingefroren weiter, und die Migration wartet nicht auf 11 DNA-Läufe. Die DNA-Reife wird messbar statt still zu Müll zu führen.
4 · Reihenfolge (Begründung = die Zahlen aus §1)
| # | Stufe | Warum hier | Risiko |
|---|---|---|---|
| 1 | publisher | 1.00 identisch · alle 8 AUS · Cron-Trigger → Constraint #7 greift nicht | null |
| 2 | p5 | 1.00 identisch · sitzt hinter allen Geldstellen | niedrig |
| 3 | p3 | Median 1.00 · jede Divergenz ist ein Registry-Feld (Container-Host, Prefix, callback, recipe_key) · Render lokal = $0 prüfbar | niedrig |
| 4 | p4 | 0.79 · die Nicht-Kern-Nodes werden Rezept-Schalter (Thumbnail-Schule, eBook) · Upload erst nach upload_target_check.py grün | mittel (YouTube) |
| 5 | p2 ✅ gebaut + belegt 27.07. (qUTlurAFjer8cbiD, 76 Nodes, deaktiviert) | 0.39 · beide Geldstellen (ApiMart + TTS). Kein Merge: der fetteste P2 (Psych/Rente, 74 Nodes) ist der Stamm, dünne Kanäle sind Teilmenge – 26/43 parameter-identisch belegen das. Beleg: echtes P1-Skript durch die Straße = 90 Beats, 20/14/14/14/14/14, identisch zum Fork bei demselben Run, $0 (HZ-406). Quellen-Mix wirkt (HZ-418, Exec 1751): wikimedia 31 · pexels 26 · openverse 17 · pixabay 16 statt 90× pexels. Offen vor dem Umschalten: HZ-430 (5 Fix-Beat-Kanäle ohne Rezept-Prompt → für die liefert der Bauplan gar keinen beat_modell und die Straße bricht sauber ab) | hoch |
| 6 | p1 | mechanisch am leichtesten (nur Prompts), aber P1 ist der Takt. Solange P1 geforkt bleibt, bleiben 15 Aktiv-Schalter als Gaspedal | mittel |
Canary = doomed (aktiv, kein Publikum, kein eBook-Funnel), danach alvaro. history / cs zuletzt – dort hängt der eBook-Funnel.
Blast-Radius-Kontrolle (ein Bug trifft dann ALLE Kanäle)
- Die Straße entsteht als NEUER Workflow mit neuem Pfad, die Forks bleiben aktiv. Umgeschaltet wird
beim Aufrufer: eine URL (wf_fork/patch_*.py-Muster). Der PUT trifft damit nie den Fork.
- Rückweg = dieselbe URL zurück. Fork wird erst nach zwei sauberen Läufen inaktiv gesetzt,
nie gelöscht.
- Kill-Switch:
enabled:falseje Kanal/Stufe → Straße antwortet 423 und bricht ab.
Global: Bauplan-Gateway deaktivieren → alles fällt fail-closed aus (kein Lauf statt falscher Lauf).
- Netz vor Schritt 1:
channel_clone/backups/2026-07-27-hz363/– alle 74 Kanal-Workflows
plus AKTIV_MAP.json (45 aktiv, inkl. Cron-disabled-Flags).
- Verifikation:
channel_clone/road_check.pydiffed den aufgelösten Bauplan gegen die Werte in
den lebenden Fork-Workflows (read-only, Sekunden, $0). upload_target_check.py bleibt Pflicht-Gate vor jedem P4-Schritt. Doppelagenten-Abnahme nach Constraint #12.
4b · Stand: was am 27.07. gebaut und bewiesen wurde
Die Bauplan-Auflösung steht – sie ist die Grundlage aller sechs Stufen:
| Stück | Datei | Wirkung |
|---|---|---|
| Auflösung Kanal+Stufe → alle Werte | dashboard/bauplan.py | eine Quelle, provenance je Feld, kein stiller Default |
| HTTP-Zugang | dashboard/app.py → /hq/api/bauplan, /hq/api/bauplan/alle | 404 unbekannt · 409 Lücke (mit Feldnamen) · 423 Kill-Switch |
| 7. Gateway | wf_fork/create_bauplan_gateway.py → n8n N4YwuXDZKNY7Wj48 (aktiv) | POST /webhook/bauplan, Guard bricht ohne channel/stage ab |
| Stufe 1 | wf_fork/build_publisher_road.py → n8n MXVBcASOgDmYY8MG (deaktiviert) | EIN Publisher für alle Kanäle statt 8 Klonen |
| Der Beweis | channel_clone/road_check.py | diffed Bauplan gegen die lebenden Workflows, read-only, $0 |
| Schalter + 4. Alias | channel_clone/registry.json → road.enabled/road.auto, asset_prefix | Aktiv-Stand als Konfiguration statt als n8n-Flag |
Belege (alle $0, keine Läufe ausgelöst):
- Gateway gegen sechs Fälle:
history/p3→ 200 ·history/publisher→ 423 ·gibtsnicht/p2→ 404 ·
history/p2 → 409 (beats) · fehlendes stage bzw. channel → Guard bricht ab.
- Trockenlauf der Publisher-Straße gegen eine Kopie der Registry (nie am Live-Topf):
history würde mit seinen eigenen Werten fahren (render-history-remotion · channel=HISTORY · store=history), alvaro wird wegen upload_mode=browser korrekt übersprungen. Gegen die echte Registry fährt sie niemanden – alle 8 stehen auf enabled:false.
road_check.pyüber alle 15 Kanäle × 6 Stufen: 162 Werte stimmen mit dem Fork überein,
3 Drift (unten), 25 benannte Lücken.
- Reifegrad nach Stufen: publisher 8/8 · p5 9/9 · p4 12/12 · p3 12/15 wären heute fahrbar;
p2 klemmt an beats (14) und bildquellen (12), p1 an wortziel (15). Die 3 fehlenden p3 sind die Stubs (aviation/business/maritime).
- Der Migrations-Handgriff funktioniert:
road_check.py --freeze doomed --applyhat die
gemessenen Ist-Werte (beats: 48, bildquellen: [ai, pexels, pixabay]) mit voller Herkunft ins Rezept geschrieben → doomed/p2 ist seither vollständig fahrbar.
Was der Bau selbst zutage gefördert hat
- Es sind VIER Schreibweisen pro Kanal, nicht drei. Neben Registry-Key,
store_channelund
youtube.channel_param gibt es den Asset-Prefix, und der weicht bei 5 von 12 Kanälen ab: cs→cs (Store: history), geo→geo (Store: emptyearth), worldcup→wm, reddit→reddit (Store: reddithorror), alvaro_horror→alvaro. Er ist nicht ableitbar und steht jetzt gemessen als asset_prefix in der Registry.
- Render-Container ≠ Upload-Container. Bei
geo,doomed,survivalundworldcupwird auf
render-psychology/render-history-remotion gerendert, aber über render hochgeladen (youtube.render_container). Wer für Upload-Stufen den Render-Container nimmt, schickt /upload und /publish an den falschen Container – dieselbe Fehlerklasse wie HZ-814.
- Zwei Upload-Wege.
alvaro_horrorlädt bewusst über den AdsPower-Browser (HZ-290) und hat
absichtlich kein channel_param. Ein pauschales Pflichtfeld hätte den Kanal blockiert – oder jemanden verleitet, genau das Leck aufzureißen, das dort zugemauert ist.
recipe.voiceist ein Key, keine Stimm-ID. Die Auflösung läuft über
channel_clone/voices.json (4.771er-Pool), nicht über registry.voices – dort steht ein alter Mini-Pool, in dem kein einziger Rezept-Key vorkommt.
- „36 Bilder fix" stimmt so nicht. Gemessen: psych 24 · worldcup 36 · wisdom/doomed/survival 48 ·
geo 72. Der Wert ist überall unsteuerbar, aber nicht überall gleich – wer bei der Migration pauschal 36 einsetzt, ändert vier Kanäle.
- Drei echte Drifts im Ist-Zustand (nicht durch den Umbau entstanden, von
road_checkgefunden):
history/p2: der Workflow spricht mit Johi (HRttR5MB…), Rezept und Registry sagen
oscar2 (3mmJ2Z…).
alvaro/p2: Workflow Viraj (1U02n4…), Rezeptedoardo(YqZLNY…).alvaro/publisher: ruftrender:8080, laut Registry wäre esrender-psychology:8080
(schlafend: Workflow aus, und der Kanal lädt ohnehin über den Browser).
5 · Sonderfälle
Kriterium: gleiche 6 Stufen + gleicher Store + gleicher Render-Vertrag + läuft → mitmigrieren. Läuft nicht → aus dem Stamm neu instanziieren statt umbauen. Anderer Vertrag → eigener Stamm.
- aviation / business / maritime – 31-Node-Stubs, p1–p3 aktiv, kein p4/p5, kein Render-Container,
andere Datenschicht (dataTable statt Store): eigener Stamm gemeinsam mit den Shorts-WFs (S1–S3, dort liegt der Fork schon zweimal), nach dem Langform-Stamm. Bis dahin unangetastet.
- stickman (komplett aus): nicht einrollen – die 52 entrollten Nodes tragen nur einen Index,
keinen Kanal. Aus dem Stamm-P2-Loop neu instanziieren, 78-Node-Fork exportieren + stilllegen.
- reddit (aus; lebendes Geschwister
alvaro): nur Node-Namen weichen ab, und Namen tragen die
$('…')-Expressions → Umbenennen ist reiner Verlust. Stilllegen, Registry/Recipe behalten.
6 · Die drei Fallen, die im Bau bewusst adressiert werden
- Der Aktiv-Stand ist Konfiguration (siehe Regel 5) – deshalb entsteht das
enabled/cadence-Gate
in Stufe 1, wo es null Risiko hat, und nicht erst bei P2.
- Drei Kanal-Schreibweisen (Key /
store_channel/channel_param) – der Store hat
UNIQUE(channel, run_id) und Assets liegen unter <channel>/<runId>: eine Verwechslung überschreibt fremde Zeilen und Assets. Der Bauplan liefert alle drei explizit.
- Observability schrumpft – 74 Execution-Ströme werden 6, und Webhook-Executions stehen nicht in
der Public-API (Constraint #8). Gegenmaßnahme im selben Schritt: Kanal UND Stufe in jedem tg-notify, Store-Write beim Stufen-Eintritt statt erst am Ende. Außerdem in Loops immer .first() statt .item (sonst „Can't determine which item" / falsches Pairing).
7 · Nicht Teil dieser Migration (eigene Tickets)
Render-Container-Konsolidierung (10 Container auf 7,6 GB RAM) · Shorts-Stamm · DNA-Reifung der 11 Stub-Kanäle (extract_dna.py, $0 über claude-proxy) · derive_format.py-Ausbau (Prosa→Zahl für est_length_min/shots_per_min, beats-Formel) · Weg A (ein Builder generiert die Workflows aus Rezept + Modul-Katalog, HZ-203).
Quelle im Repo: docs/pipeline_strasse_migration.md