# Kurato — Golden Path Versand (Live-Test vor dem ersten Echtmailing)

Zweck: Der komplette Weg von der CSV bis zur Abmeldung, einmal mit echten Mails.
Die 424 Unit-Tests laufen gegen WP-Stubs + FakeWpdb — sie belegen die Logik, **nicht**
dass eine Mail ankommt. Dieser Plan schließt genau die Lücke.

Anlass: FU-Bestandskunden-Mailing (Digistore24 + ~1.600 aus Fastbill).
**Erst wenn hier alles grün ist, wird eine echte Kundenadresse angefasst.**

Stand vor dem Test: kurato.cloud, Kurato 1.2.0, **1 Kontakt, 0 Kampagnen je versendet**.

---

## Vorbedingungen (ohne die geht nichts)

| # | Punkt | Status |
|---|---|---|
| V1 | Versand-Transport gewählt und Key hinterlegt (Versand-Seite) | offen |
| V2 | Absender-Domain beim ESP verifiziert (DKIM-Records gesetzt) | offen |
| V3 | DNS-Ampel in Kurato grün für die Absender-Domain (SPF/DKIM/DMARC) | offen |
| V4 | Testmail-Button liefert eine Mail im Posteingang (nicht Spam) | offen |

V4 ist das Tor: schlägt es fehl, ist jeder weitere Schritt sinnlos.

---

## A — Import (CSV → Kontakte)

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| A1 | Test-CSV mit 5 Adressen (eigene, verschiedene Anbieter: Gmail, GMX, Outlook, Web.de) importieren | 5 Kontakte, Spalten korrekt zugeordnet | Umlaute zerfallen, Spalten verrutscht |
| A2 | Dieselbe CSV nochmal importieren | 0 neue Kontakte (Dedup per E-Mail) | Doppelte Kontakte |
| A3 | CSV mit kaputter Zeile (Komma im Namen, fehlende E-Mail) | Zeile übersprungen, Rest importiert, Meldung | Import bricht komplett ab |
| A4 | Selbsterklärung angehakt vs. nicht | mit = `confirmed` + Consent-Log-Eintrag, ohne = `pending` | Status falsch, Consent-Log leer |
| A5 | Umlaut-Test: Name "Jörg Müller-Weiß" | im Profil korrekt lesbar | Zeichensalat (vgl. Bug L-F2) |

## B — Segment + Empfängerauswahl

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| B1 | Segment "Importiert, bestätigt" bauen | Live-Zähler zeigt die richtige Zahl | Zähler weicht von der Kontaktliste ab |
| B2 | Kampagne anlegen, Segment als Ziel wählen | Empfänger-Vorschau = Zähler aus B1 | Zahlen driften auseinander |
| B3 | Einen Kontakt auf `pending` setzen, Vorschau neu laden | Zahl sinkt um 1 (nur confirmed wird versendet) | Unbestätigte werden mitgezählt |

## C — Versand (der eigentliche Test)

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| C1 | Testversand an eigene Adresse | Mail kommt an, Personalisierung ersetzt (kein `{{vorname}}` sichtbar) | Platzhalter roh in der Mail |
| C2 | **Bei allen 4 Anbietern in den Posteingang schauen** | Posteingang, nicht Spam | Spam-Ordner bei ≥1 Anbieter → **Abbruch, DNS/Reputation klären** |
| C3 | Mail als Plain-Text ansehen (Multipart) | lesbarer Text-Teil vorhanden | nur HTML → Spam-Signal |
| C4 | Darstellung: Gmail-App, iOS Mail, Outlook Web | Layout hält, Dark Mode lesbar | zerschossenes Layout |
| C5 | Echte Kampagne an die 5 Testkontakte starten | alle 5 zugestellt, Fortschritt zählt hoch | Queue hängt, `failed` ohne Grund |
| C6 | Während des Versands pausieren, dann fortsetzen | keine Doppel-Zustellung | jemand bekommt die Mail zweimal |

## D — Abmeldung + Rechtssicherheit

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| D1 | Abmeldelink in der echten Mail klicken | Abmeldung greift, Bestätigungsseite | Link tot / 404 |
| D2 | Kontakt danach in einer neuen Kampagne | wird **nicht** mehr zugestellt | Abgemeldeter bekommt Mail → Rechtsverstoß |
| D3 | Consent-Log prüfen | Widerruf protokolliert (`kurato:unsubscribe`) | kein Eintrag → Nachweis fehlt |
| D4 | Preference Center öffnen, Liste einzeln abbestellen | nur diese Liste betroffen | alle Listen weg |
| D5 | Wiederanmelden über das Center | nur zurück auf früher bestätigten Stand | DOI-Bypass möglich |
| D6 | List-Unsubscribe-Header in der Rohmail prüfen | vorhanden (RFC 8058) | fehlt → Gmail straft ab |

## E — Bounce + Suppression

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| E1 | Kontakt mit garantiert toter Adresse (`irgendwas@flirtuniversity.de` o.ä.) anlegen und anmailen | Hard Bounce | — |
| E2 | ESP-Webhook prüfen | Kurato setzt `suppressed_bounce` | Status bleibt `active` |
| E3 | Nächste Kampagne | Adresse wird übersprungen | erneuter Versand → Reputation leidet |
| E4 | Zustell-Protokoll auf der Versand-Seite | Ereignisse sichtbar, Empfänger maskiert | leer, oder Klartext-Adressen im Log |

## F — Tracking (nur wenn eingeschaltet)

| # | Schritt | Erwartet | Fail wenn |
|---|---|---|---|
| F1 | Tracking aus (Default) | kein Pixel, keine umgeschriebenen Links | Pixel trotz Schalter aus |
| F2 | Tracking an + Kontakt-Consent | Öffnung/Klick gezählt | nichts gezählt |
| F3 | Tracking an, Kontakt-Consent **aus** | nichts gezählt | gezählt → Consent-Gate kaputt |
| F4 | Abmelde-/Preference-Link | **nie** umgeschrieben | umgeschrieben → Abmeldung kann brechen |

---

## Abbruchkriterien (hier wird nicht weitergemacht)

1. **C2 rot** — Mail landet bei irgendeinem Anbieter im Spam.
2. **D2 rot** — Abgemeldete bekommen weiter Mails. Rechtsverstoß, kein "fixen wir später".
3. **E3 rot** — Bounces werden erneut angeschrieben. Killt die Absender-Reputation.

## Nach Grün: Aufwärm-Staffel für die echte Liste

Nicht 1.600 auf einmal. Frische zuerst, in Wellen:

| Welle | Umfang | Gate zur nächsten |
|---|---|---|
| 0 | 5 Testkontakte (dieser Plan) | alles grün |
| 1 | ~50 der frischesten Käufer | Bounce < 3 %, keine Beschwerde |
| 2 | ~200 | Bounce < 3 % |
| 3 | ~500 | Bounce < 5 % |
| 4 | Rest | — |

Zwischen den Wellen mindestens 24 h. Bounce-Rate nach jeder Welle im Zustell-Protokoll ablesen.
Bei > 5 % anhalten und die Liste bereinigen, nicht weiterschießen.
