# Programmier-Plan

**Stand:** 28. Juli 2026
**Repo:** `git.webklar.com/knso/videogen` · Backend: Appwrite `appwrite.webklar.com/v1`, Projekt `6a5cee34002bb8360c34`, DB `brandloop`
**Grundlage:** `app-aufbau.md` (Screens) · `konzept-bilder-feed.md` (Konzept) · `datenbank-aufbau.md` (Schema)

---

## 1. Ausgangslage – was schon da ist

| | Stand |
|---|---|
| **Appwrite-Schema** | 15 Tabellen, Indizes, 4 Buckets, Team `internal` – angelegt durch `scripts/setup-appwrite.mjs`, idempotent |
| **Prompts** | P1–P18 als 22 Zeilen geseedet (P8 = Kern + 4 Adapter) via `scripts/seed-prompts.mjs`, versioniert |
| **Regeln** | 4 globale Brand-Regeln in `rules` |
| **Prototyp** | `prototyp-app.html`, 21 Screens, Handy-Ansicht, vollständig ohne Backend |
| **App-Code** | **existiert noch nicht** – es gibt einen Expo-QR-Code, aber keine Anwendung |

**Der entscheidende Punkt:** Das Schema im Repo bildet den Stand **vor** dem Bild-/Feed-Konzept ab. Es fehlen alle Tabellen des neuen Produktteils. Wer jetzt anfängt, UI zu bauen, baut gegen Tabellen, die es nicht gibt.

---

## 2. Schema-Delta – was konkret fehlt

Abgeglichen gegen `scripts/setup-appwrite.mjs`.

### 2.1 Neue Tabellen (6)

| Tabelle | Zweck |
|---|---|
| `folders` | Wissens-Scope: `brand_id`, `name`, `theme_md`, `zweck`, `startwert_modus`, `ist_default`, `post_count`, `signal_count` |
| `attribute_scores` | Score je Attribut je Ordner – **Herzstück** |
| `posts` | Slot-Rezept, `slot_summary`, `sichtbarkeit`, `feed_score` |
| `post_images` | Bilderkette, `typ` = motiv \| text_overlay |
| `post_folders` | m:n Post ↔ Ordner, `ist_fremd`, `quelle` |
| `post_metrics` | Views, Likes, Kopien, Social-Reichweite |

### 2.2 Neue Tabelle wegen des Folgen-Konzepts (1)

| Tabelle | Zweck |
|---|---|
| `follows` | `follower_brand_id`, `followed_brand_id`, unique auf beide, Index in beide Richtungen |

### 2.3 Änderungen an bestehenden Tabellen

| Tabelle | Änderung | Art |
|---|---|---|
| `brands` | `+ nische`, `+ plan`, `+ anzeigename`, `+ avatar_file_id`, `+ follower_count`, `+ ist_oeffentlich` | Spalten ergänzen |
| `attributes` | `− score, k_factor, start_value, used_count, wins, losses, last_used_at` → wandern nach `attribute_scores`; Index `idx_topn` entfällt hier | **Umbau** |
| `categories` | `+ folder_id` | Spalte + Index |
| `assets` | `+ nische`, `+ typ_tags[]`, `+ feed_score`, `+ feed_score_updated_at`, `+ ist_teilbar`; `typ`-Enum **um `kulisse`** | Spalten + **Enum-Änderung** |
| `score_events` | `+ folder_id`, `+ post_id`; `event_type`-Enum **um `feed_import`, `post_reichweite`** | Spalten + **Enum-Änderung** |
| `scenes` | `+ folder_id` | Spalte |
| `jobs` | `typ`-Enum **um `slot_analyse`, `ordner_vorschlag`, `werbetext`** | **Enum-Änderung** |
| `prompt_templates` | `key`-Enum **P18 → P22** (`P_KEYS` im Skript: `length: 18` → `22`) | **Enum-Änderung** |

### 2.4 Neuer Bucket

`generated-images` – öffentlich lesbar, sobald der zugehörige Post veröffentlicht ist.

### 2.5 Neue Functions (4)

`ordner-initialisieren` · `feed-score-berechnen` (Cron) · `feed-import` · `social-reichweite-holen` (Cron)
Dazu die aus dem alten Plan noch offenen: `elo-update`, `favorit-berechnen`, `ads-metriken-holen`, `kategorie-durchschnitt`, `archivierung`, `job-dispatcher`.

### 2.6 Zwei Fallen bei der Migration

**Enum-Änderungen sind kein Anhängen.** Vier Spalten brauchen neue Enum-Werte (`assets.typ`, `score_events.event_type`, `jobs.typ`, `prompt_templates.key`). Appwrite behandelt das als Spaltenänderung, nicht als Ergänzung – je nach Version bedeutet das Löschen und Neuanlegen der Spalte, also **Datenverlust**. Das ist der Grund, warum diese Änderung **jetzt** passieren muss, solange die Tabellen leer sind. In sechs Wochen mit echten Daten ist es ein Migrationsprojekt.

**Die `attributes`-Auftrennung ist ein Umbau, kein Zusatz.** Sieben Spalten verschwinden aus einer Tabelle und tauchen in einer neuen wieder auf, mit einer zusätzlichen Dimension. Solange keine Brand existiert, ist das eine Textänderung im Setup-Skript. Danach ist es eine Datenwanderung mit Ausfallzeit.

> **Deshalb ist „Fundament zuerst" nicht nur die aufgeräumte, sondern die einzig günstige Reihenfolge.**

---

## 3. Etappen

Jede Etappe hat ein Abnahmekriterium – etwas, das man vorführen kann. Ohne das ist nicht entscheidbar, ob sie fertig ist.

### E0 · Schema nachziehen
**Inhalt:** `setup-appwrite.mjs` um die 7 neuen Tabellen, die geänderten Spalten, die Enum-Erweiterungen und den Bucket ergänzen. `attributes` auftrennen. `P_KEYS` auf 22. Skript einmal komplett gegen eine **leere** Datenbank laufen lassen.
**Abnahme:** Skript läuft zweimal hintereinander fehlerfrei durch (idempotent), 22 Tabellen und 5 Buckets stehen in der Appwrite-Konsole.
**Aufwand:** klein. Reine Skriptarbeit, kein UI.

### E1 · Projektgerüst Expo
**Inhalt:** Expo-Projekt anlegen, TypeScript, Navigation (Tabs + Stacks), Appwrite-SDK verdrahten, Theme aus dem Prototyp übernehmen (Farben, Typo, Glas-Effekte – **ohne** die Fake-Tastatur und ohne die Shader-Spielerei per `setInterval`).
**Abnahme:** App startet auf Handy und im Browser, drei Tabs sind klickbar, jeder Tab zeigt einen leeren Screen mit seinem Namen.
**Aufwand:** mittel. Hier entscheidet sich die Ordnerstruktur des Codes – sorgfältig sein.

### E2 · Auth und Mandant
**Inhalt:** Registrieren, Anmelden, Abmelden. Bei der Registrierung: Appwrite-Team anlegen, `brands`-Zeile anlegen, Zeilenrechte setzen. Session-Persistenz.
**Abnahme:** Zwei Konten anlegen; Konto B sieht **keine** Daten von Konto A – geprüft über die API, nicht über das UI.
**Aufwand:** mittel. Die Rechte-Prüfung ist der eigentliche Inhalt, nicht die Formulare.

> Das ist die Etappe, bei der man am ehesten schludert und es am teuersten wird. Die Mandantentrennung ist das Fundament des Rechte-Modells aus `datenbank-aufbau.md` §5.

### E3 · Onboarding
**Inhalt:** Die vier Onboarding-Screens mit echten Feldern, inklusive **Nische**. Uploads in den Bucket `uploads`. P2 als erster echter LLM-Aufruf über `jobs`.
**Abnahme:** Nach dem Onboarding stehen in `brands` sechs ausgefüllte Felder und in `attribute_scores` die ersten Attribute auf der brand-weiten Ebene (`folder_id = null`).
**Aufwand:** mittel.

### E4 · Modelle
**Inhalt:** Modell anlegen (Person, Produkt, **Kulisse**), echte Referenzbild-Uploads, Versionierung mit Release-Prinzip, Modelle-Übersicht im Profil.
**Abnahme:** Ein Produkt-Modell mit 4 Referenzbildern anlegen, freigeben, in der Übersicht sehen. `assets.released_version_id` zeigt auf die richtige Version.
**Aufwand:** mittel. Ohne Modelle kann nichts generiert werden – deshalb vor der Generierung.

### E5 · Ordner
**Inhalt:** Ordner-Liste, Anlegen mit den zwei Schaltern, Ordner-Detail, aktiver Ordner als App-Zustand. Function `ordner-initialisieren`.
**Abnahme:** Zwei Ordner anlegen – einen mit `erben`, einen mit `aus_posts`. In `attribute_scores` stehen danach unterschiedliche Startwerte mit korrekt gesetzter `start_quelle`.
**Aufwand:** mittel. Fachlich der anspruchsvollste Teil bis hierher.

### E6 · Post erstellen (erste echte Generierung)
**Inhalt:** Create-Screen mit Ordner, Format, Kette, Slots. `jobs` + `job-dispatcher` + Realtime-Wartezustand. P7 auf das Slot-System umbauen. P22 als Bild-Tagger. Post-Ergebnis mit privat/veröffentlichen.
**Abnahme:** Ein Prompt erzeugt eine dreiteilige Bilderkette, die im Entwürfe-Screen auftaucht, auch wenn man die App zwischendurch schließt.
**Aufwand:** groß. Hier hängt die Auswahl des Bildmodells dran – **offener Punkt 1**.

### E7 · Feed
**Inhalt:** Veröffentlichen mit Zeilen-Permission, Feed-Deck mit den drei Reitern, Post-Detail, `post_metrics` schreiben, Function `feed-score-berechnen` mit Decay und Explorations-Slot.
**Abnahme:** Ein veröffentlichter Post von Konto A erscheint im Feed von Konto B. `prompt_sent` ist über die API von Konto B **nicht** abrufbar.
**Aufwand:** groß.

### E8 · Kopieren
**Inhalt:** P19 (Slot-Analyst), Kopier-Screen mit Chips und Kompatibilitätsprüfung, P21 (Werbetext), Function `feed-import`, `copied_from_post_id`, Kopien-Zähler.
**Abnahme:** Konto B kopiert den Post von Konto A, ersetzt beide Modelle, generiert – und in der Knowledge Base von Konto B stehen danach neue Attribute mit `start_quelle = feed_import` im gewählten Ordner.
**Aufwand:** groß. Das ist der Moment, in dem das Produkt das tut, was es verspricht.

### E9 · Folgen
**Inhalt:** `follows`, Fremdprofil, Folgen-Button, Reiter „Folge ich" chronologisch, Blockieren und Melden.
**Abnahme:** Konto B folgt Konto A und sieht dessen neuen Post im Reiter „Folge ich", vor allem anderen.
**Aufwand:** mittel.

### E10 · Video-Strecke
**Inhalt:** Die bestehende Video-Strecke aus dem Prototyp echt machen: `scenes`, P3/P4/P5/P8, `videos`, Vote, `elo-update`, gezielte Frage.
**Abnahme:** Ein Prompt erzeugt vier Videos von vier KI-Modellen; nach dem Vote haben sich Scores in `attribute_scores` messbar verändert und `score_events` hat die passenden Zeilen.
**Aufwand:** sehr groß, und mit laufenden Kosten ab dem ersten Test.

---

## 4. Reihenfolge und Abhängigkeiten

```
E0 Schema
 └─ E1 Gerüst
     └─ E2 Auth ──────────────┐
         ├─ E3 Onboarding     │
         ├─ E4 Modelle ───────┤
         └─ E5 Ordner ────────┤
                              └─ E6 Post erstellen
                                    ├─ E7 Feed
                                    │    ├─ E8 Kopieren
                                    │    └─ E9 Folgen
                                    └─ E10 Video
```

**E3, E4 und E5 sind untereinander unabhängig** – sie können in beliebiger Reihenfolge oder parallel entstehen. Alles ab E6 hängt an allen dreien.

**E10 (Video) steht bewusst hinten,** obwohl es das Kernprodukt ist: Es ist die teuerste Strecke in der Umsetzung *und* im Betrieb, und der Bild-Strang erzeugt vorher die Daten, mit denen sich der Video-Teil überhaupt sinnvoll testen lässt.

---

## 5. Was im ersten Durchgang bewusst wegbleibt

| Weggelassen | Warum |
|---|---|
| Desktop-Ansicht | Handy zuerst; der Handoff im Repo ist laut dir noch nicht gültig |
| Stripe / Abrechnung | Preismodell ist offen (offener Punkt 3) – ohne Entscheidung nicht baubar |
| Meta-/TikTok-Ads-Anbindung | App-Reviews dauern; Antrag trotzdem **jetzt** stellen, unabhängig vom Code |
| Social-Reichweite (echte Instagram-Zahlen) | eigene App-Review; bis dahin läuft der Feed-Score nur auf In-App-Signalen |
| P12–P18, P20 | nachrüstbar, ohne sie funktioniert der Loop |
| Verbessern-Strecke | vorhanden im Prototyp, aber nicht auf dem kritischen Pfad |
| Developer-Modus | intern, kein Nutzerwert – erst wenn es echte Prompts zu debuggen gibt |

---

## 6. Risiken

| Risiko | Wirkung | Gegenmaßnahme |
|---|---|---|
| **Enum-Migration nach dem Livegang** | Datenverlust in vier Spalten | E0 vor allem anderen, solange die DB leer ist |
| **Multi-Referenz-Komposition** (Person + Produkt + Kulisse konsistent in einem Bild) | Der Kern des Bild-Modus funktioniert nicht wie gedacht | Vor E6 mit zwei, drei Kandidatenmodellen einen Wegwerf-Test fahren – **bevor** UI dafür gebaut wird |
| **Generierungskosten im Test** | schleichende Kosten ohne Gegenwert | `jobs.cost_usd` ab E6 mitschreiben und ein hartes Tageslimit einbauen |
| **Leerer Feed** | E7 ist nicht bewertbar, E8 nicht testbar | Vor E7 mit dem eigenen Konto 15–20 Posts erzeugen |
| **Prototyp als Vorlage überschätzt** | Screens werden 1:1 übernommen inklusive der Attrappen | `app-aufbau.md` §6 ist die verbindliche Liste, nicht die HTML-Datei |
| **Öffentlich/privat nur im UI durchgesetzt** | fremde Knowledge Bases werden über die API auslesbar | Abnahme von E7 ausdrücklich **über die API** prüfen, nicht über den Bildschirm |

---

## 7. Was vor E0 zu entscheiden ist

1. **Nischen-Liste** und **`typ_tags`-Liste** – beide sind Enums und wandern in E0 ins Schema. Später zu ändern heißt: dieselbe Migrationsfalle wie oben.
2. **Bildmodell** – blockiert nicht E0, aber E6. Der Test dafür sollte parallel zu E1–E5 laufen.
3. **Mindestalter und Moderationsweg** – blockiert nicht den Code, aber E7 im Livebetrieb.
