243 lines
19 KiB
Markdown
243 lines
19 KiB
Markdown
# 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** | ✅ **22 Tabellen, Indizes, 5 Buckets, Team `internal`** – live und vollständig, `scripts/setup-appwrite.mjs` reproduziert diesen Stand idempotent (Stand 14.08.2026) |
|
||
| **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 |
|
||
|
||
> **Nachtrag 14.08.2026 – E0 ist erledigt.** Der Abschnitt unten beschreibt den Stand vom 28.07. und ist ab hier **Historie, keine Aufgabenliste mehr**. Die Live-Datenbank war bereits vollständig migriert (alle 7 neuen Tabellen, alle 4 Enum-Erweiterungen, `attributes` aufgetrennt, 5. Bucket) – nur `setup-appwrite.mjs` im Repo hing auf dem alten Stand hinterher und hätte beim Ausführen die Score-Spalten in `attributes` wieder angelegt. Das Skript ist inzwischen auf den Live-Stand gezogen und gegen ihn geprüft: **0 angelegt, 251 existierten schon, 0 Warnungen.** Auch die beiden Enum-Entscheidungen aus §7.1 (Nischen-Liste, `typ_tags`) sind getroffen und stehen als `NISCHEN` und `TYP_TAGS` im Skript.
|
||
|
||
---
|
||
|
||
## 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 ✅ **erledigt (14.08.2026)**
|
||
**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.
|
||
**Abnahme erfüllt:** 22 Tabellen und 5 Buckets stehen live; das Skript läuft ohne eine einzige Änderung durch (`0 angelegt, 251 existierten schon, 0 Warnungen`).
|
||
**Datenbestand:** nur Stammdaten – 4 Zeilen `rules`, 22 Zeilen `prompt_templates` (P1–P18, P8 als Familie). Keine Brand-Daten. **Enum-Änderungen sind ab jetzt trotzdem nicht mehr gefahrlos**, weil `prompt_templates.key` gefüllt ist.
|
||
|
||
> **Lehre aus dieser Etappe:** Die Datenbank war der Quelle voraus, nicht umgekehrt. Wer `setup-appwrite.mjs` in diesem Zustand ausgeführt hätte, hätte die sieben Score-Spalten in `attributes` neu angelegt und die Auftrennung halb zurückgedreht – ein idempotentes Skript schützt nur vor doppeltem Anlegen, nicht vor einer veralteten Definition. Deshalb: **nach jeder Schema-Änderung an der Konsole das Skript nachziehen und mit `scripts/check-appwrite.mjs` gegenprüfen.**
|
||
|
||
### E1 · Projektgerüst Expo ✅ **erledigt im Browser (14.08.2026), nativ ungeprüft**
|
||
**Inhalt:** Expo-Projekt in `client/`, TypeScript, Expo Router (Stack + Tabs), Appwrite-SDK verdrahtet, Theme aus dem Prototyp. Fake-Tastatur und `setInterval`-Shader sind bewusst nicht übernommen.
|
||
**Abnahme:** `npx tsc --noEmit` fehlerfrei · `npx expo export --platform web` erzeugt alle drei Routen · im Browser rendern `/`, `/erstellen` und `/profil` mit Tab-Leiste, keine Konsolen-Fehler. **Offen: der Start auf einem echten Gerät** – dafür fehlt ein Testgerät bzw. ein Dev-Build.
|
||
|
||
**Struktur (`client/src/`):**
|
||
|
||
| Pfad | Inhalt |
|
||
|---|---|
|
||
| `app/_layout.tsx` | Wurzel-**Stack**, nicht direkt die Tabs – Willkommen/Anmelden liegen laut app-aufbau.md §3 vor den Tabs, das Onboarding als Modal darüber. Beide brauchen eine Ebene ohne Tab-Leiste. |
|
||
| `app/(tabs)/` | Feed · Erstellen · Profil |
|
||
| `lib/appwrite.ts` + `.web.ts` | plattform-getrennter Client. `react-native-appwrite` läuft nicht im Browser, das Web-SDK `appwrite` kennt kein React Native – beide exportieren dieselben Klassen, der Rest der App importiert nur `@/lib/appwrite`. |
|
||
| `lib/config.ts` | Endpoint, Projekt-ID, DB-ID aus `app.json` → `expo.extra.appwrite`. **Kein Server-Key** – der landet sonst im Bundle. |
|
||
| `theme/tokens.ts` | Farben, Radien, Abstände aus `prototyp-app.html`. Dark-only, weil der Prototyp kein Light-Theme hat. |
|
||
|
||
**Zwei bewusste Abweichungen:** Das ➕ ist vorerst ein normaler Tab statt des Popovers aus §3 – der kommt mit den drei Erstellen-Abläufen in E6. Und der Profil-Screen zeigt provisorisch Endpoint und Projekt-ID, damit belegt ist, dass der Client wirklich lädt; das fliegt in E2 raus.
|
||
|
||
### E2 · Auth und Mandant ✅ **erledigt (14.08.2026)**
|
||
**Inhalt:** Willkommen/Registrieren/Anmelden/Abmelden, Session-Persistenz, bei der Registrierung Team + `brands`-Zeile mit Zeilenrechten.
|
||
**Abnahme erfüllt:** `scripts/test-mandanten.mjs` legt zwei echte Konten an und prüft **über die API**: B listet nur die eigene Zeile · Direktzugriff B→A `404 row_not_found` · Schreibzugriff B→A `401 user_unauthorized` · Gegenprobe, dass der Server-Key beide Zeilen sieht. Räumt sich selbst auf.
|
||
|
||
**Der eigentliche Inhalt war das Rechte-Modell, nicht die Formulare.** Zwei Dinge, die vorher niemandem aufgefallen waren:
|
||
|
||
1. **Kein Client konnte irgendetwas anlegen.** Bei `rowSecurity: true` regeln Zeilenrechte lesen/ändern/löschen – aber eine Zeile, die es noch nicht gibt, hat keine Rechte. Ohne Tabellen-Recht zum Anlegen war jede Client-Schreiboperation blockiert. 16 Tabellen haben jetzt `create("users")`; `score_events`, `video_metrics`, `post_metrics` und `usage_records` bewusst **nicht** – sonst könnte ein Nutzer seine eigenen Elo-Werte und Feed-Signale fälschen (§11, „Manipulation des Feed-Scores").
|
||
2. **`setup-appwrite.mjs` hat Rechte nie abgeglichen.** Bei bestehenden Tabellen lieferte `POST` nur ein 409, die Rechte blieben unangetastet. Das Skript vergleicht jetzt und zieht per `PUT` nach – sonst driftet das Rechte-Modell genauso wie zuvor das Schema.
|
||
|
||
**Nebenfund: SDK-Version passte nicht zum Server.** Appwrite läuft in **1.8.1**, `create-expo-app` hatte SDKs mit Response-Format 1.9.5 gezogen. Gepinnt auf `appwrite@23.0.0` und `react-native-appwrite@0.25.0` (beide Response-Format 1.8.0), **exakt statt Caret** – der Sinn der Pinnung ist ja gerade der Gleichstand mit dem Server.
|
||
|
||
> 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 🟡 **Kern steht (14.08.2026), Verbessern-Strecke offen**
|
||
**Inhalt:** Modell anlegen (Person, Produkt, **Kulisse**), echte Referenzbild-Uploads, Versionierung mit Release-Prinzip, Modelle-Übersicht im Profil.
|
||
**Erledigt:** Anlegen mit Typwahl und Mehrfach-Bildauswahl (`modell/neu`), Upload nach `asset-references` mit Team-Rechten, Version 1 wird angelegt und sofort freigegeben, `assets.released_version_id` zeigt darauf. Übersicht im Profil nach Typ gruppiert, mit Titelbild aus der freigegebenen Version.
|
||
**Belegt:** Vier Modelle (2 Produkte, 2 Kulissen) über `scripts/seed-demo.mjs` als **Client** angelegt – das beweist nebenbei, dass die Tabellen- und Bucket-Rechte aus E2 ausreichen. Alle vier Bilder laden in der App in Originalgröße.
|
||
**Offen:** weitere Versionen anlegen und freigeben (Verbessern-Strecke), `merkmale` als Token-Lock, Einwilligungs-Upload für Personen.
|
||
|
||
### E5 · Ordner ✅ **erledigt (14.08.2026)**
|
||
**Inhalt:** Ordner-Liste, Anlegen mit den zwei Schaltern, Ordner-Detail, aktiver Ordner als App-Zustand. Function `ordner-initialisieren`.
|
||
**Abnahme erfüllt** – nach dem Anlegen über die Oberfläche steht in `attribute_scores`:
|
||
|
||
| Ordner | Modus | Scores | `start_quelle` |
|
||
|---|---|---|---|
|
||
| (brand-weit, `folder_id = null`) | – | 12 | `neutral` |
|
||
| Sommerkampagne | `erben` | 12 | `geerbt` |
|
||
| Cleane Studioshots | `aus_posts` | 0 | – |
|
||
| Herbstlinie (über die App angelegt) | `erben` | 12 | `geerbt` |
|
||
|
||
Zweck und Startwerte sind als Auswahl **mit Erklärung** umgesetzt, nicht als nackter Schalter: wer sie missversteht, baut sich einen Ordner, der nicht tut, was er erwartet.
|
||
|
||
**Beim Erben wird der Wert kopiert, aber nicht die Sicherheit** – `k_factor` geht zurück auf 32, weil im neuen Scope noch nichts belegt ist.
|
||
|
||
**Abweichung:** `ordner-initialisieren` läuft vorerst im Client statt als Appwrite-Function. Vertretbar, weil nur eigene Zeilen kopiert werden und die Zeilenrechte das ohnehin begrenzen; beim Umzug in eine Function ändert sich nur der Ort.
|
||
|
||
### E6 · Post erstellen 🟡 **Rezept-Hälfte steht (14.08.2026), Generierung extern blockiert**
|
||
**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.
|
||
|
||
**Erledigt:** `erstellen/post` mit Ordnerwahl in der Kopfzeile, Format, Kettenlänge und Slot-Auswahl für Kulisse, Produkt und Person – als Bildkacheln, weil bei Kulissen und Produkten das Aussehen die Information ist und ein Name wie „Halle, Metallwand" nichts darüber sagt, ob es passt. Der Post entsteht **sofort** mit `status: entwurf`, bevor irgendetwas generiert wird; ohne diese Zeile gäbe es keinen Ort, an den man nach dem Warten zurückkehrt. Je Kettenbild eine `jobs`-Zeile. Ordner-Detail zeigt die Posts mit aufgelösten Slot-Namen.
|
||
|
||
**Nachweis über die Oberfläche:** Post „Serum auf Waschtisch" → Ordner Herbstlinie, `4:5`, 3 Bilder, `privat`, Slots Halle-Metallwand + Refine-&-Renew-Serum, dazu **3 Jobs `bild_gen/wartend` mit `prompt_template_key = P7`**.
|
||
|
||
**Job-Dispatcher steht** – `scripts/job-dispatcher.mjs`, serverseitig, weil der Anbieter-Schlüssel nicht ins Client-Bundle darf. Er baut aus Slot-Rezept, den Beschreibungen der freigegebenen Asset-Versionen, den Top-Attributen **des gewählten Ordners** und den globalen Regeln einen Prompt, erzeugt das Bild, legt es in `generated-images` mit Team-Rechten ab, schreibt `post_images` und setzt den Post auf `generiert`, sobald die Kette vollständig ist.
|
||
|
||
Drei Anbieter über `BILD_ANBIETER`:
|
||
|
||
| Wert | Ergebnis (geprüft 14.08.2026) |
|
||
|---|---|
|
||
| `stub` | ✅ erzeugt Platzhalterbilder – **die ganze Kette ist damit prüfbar, ohne einen Cent auszugeben** |
|
||
| `ark` | ✗ `ModelNotOpen: account 3003959567 has not activated seedream-5-0` |
|
||
| `openrouter` | ✗ `Insufficient credits. This account never purchased credits` |
|
||
|
||
**Durchgespielt mit `stub`:** Post „Serum auf Waschtisch" → 3 Jobs → 3 Bilder erzeugt, hochgeladen, Post auf `generiert`, Kette im Ordner-Detail sichtbar (3/3 geladen). Bei den echten Anbietern greift der Fehlerpfad: Job auf `fehler` mit der Anbieter-Meldung, Post auf `fehler`.
|
||
|
||
**Damit fehlt nur noch die Freischaltung.** Sobald eines der beiden Konten offen ist, liefert `BILD_ANBIETER=ark node scripts/job-dispatcher.mjs` echte Bilder – ohne Codeänderung.
|
||
|
||
**Offen:** Umzug des Dispatchers in eine Appwrite-Function (läuft jetzt lokal/per Cron), P7 als versionierter Prompt statt der Vorstufe im Skript, P22 als Tagger, Wartezustand mit Realtime, Ergebnis-Screen mit Veröffentlichen.
|
||
|
||
### 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**~~ – ✅ entschieden und live. 20 Nischen, 17 Motiv-Tags, stehen als `NISCHEN` und `TYP_TAGS` in `scripts/setup-appwrite.mjs`.
|
||
2. **Bildmodell** – blockiert nicht E0, aber E6. Der **Anbieter steht** (OpenRouter, siehe `projekt-uebersicht.md` §9); offen ist nur, welches der dortigen Bildmodelle die Multi-Referenz-Komposition trifft. Der Wegwerf-Test dafür sollte parallel zu E1–E5 laufen.
|
||
3. **Mindestalter und Moderationsweg** – blockiert nicht den Code, aber E7 im Livebetrieb.
|