# Abschlussbericht — KADiCon Reservierung: Widget + E-Mails komplett überarbeitet
Auftrag: 20.08.2026 00:45 CEST · Abschluss: 20.08.2026 ~04:00 CEST · Session e92ed44c

## Production Status
**LIVE und real verifiziert.** Backend `backend@0.21.1`, Widget `reservation-widget@0.6.0`, beide über die bestehende Pipeline produktiv deployed. Widget auf https://www.restaurant-durrani.de/#reservierungen im echten Embedding geprüft.

## Branch / Commits / PRs / Merges
| PR | Branch | Head | Merge (squash) | Inhalt |
|---|---|---|---|---|
| #265 | feat/reservation-self-service-emails | e68d1e99 | 694b85c2 · 02:49 CEST | Backend: Self-Service-API, Migration, E-Mail-Umbau, Slot-partySize, Locks |
| #266 | feat/reservation-widget-ux | 7ad4f6dd | 3bd8a3d9 · 03:06 CEST | Widget: UX-Neubau, i18n, Autocomplete, Telefon, Manage-Element |
| #267 | fix/manage-link-directory-index | e929029c | 614f7268 · ~03:40 CEST | Hotfix: Mail-Links auf index.html-Form (CDN ohne Directory-Index) |
Plus 2 Version-Plan-Commits (7ba419f0, in #267) — ohne sie taggt die Release-Pipeline nichts und der Prod-Deploy skippt still (Stolperfalle gefunden + dokumentiert).

## Deployment
Bestehende Pipeline: Merge→main = Staging (ECS-Rolling + S3/CloudFront); Production via release.yml-Dispatch (2×: Run 32320240459 für 0.21.0/0.6.0, Run 32321772782 für 0.21.1). Alle Runs success. ECS-Rolling brauchte je ~3–4 min nach Workflow-Ende (per Endpoint-Probe verifiziert, nicht nur Health).

## Database
Eine additive Migration (automatisch beim Container-Boot, Staging zuerst): `customerFirstname`, `customerLastname`, `managementToken` (varchar 64, partieller Unique-Index). `customerName` bleibt kompatible Quelle für Dashboard/Suche. Kein manueller DB-Eingriff, kein Rollback nötig.

## Production Verification (alles real auf Prod ausgeführt)
- Widget lädt im Durrani-Embedding, deutsch bei deutschem Browser (en bei englischem — konsistent, keine Mischsprache), 0 Console-Fehler, A11y-Baum korrekt (radiogroups, disabled-Zustände).
- Kompletter Gast-Flow: 2 Personen → Fr 21.08. → 17:30 (Slots personenzahl-bewusst geladen) → Kontakt (Autocomplete „aseckzai@gm" → 3 Vorschläge, Übernahme per Klick) → Prüfen-Karte → Reservierung erstellt (auto-approved) → Bestätigungsseite mit Ändern/Stornieren/Anrufen/Route.
- Self-Service: Manage-Link geöffnet, Reservierung angezeigt, Storno hinter expliziter Bestätigung → storniert. Gegenproben live: Doppel-Storno → 409, GET auf Cancel-Route → 404 (Link-Scanner-sicher), Garbage-Token → 404.
- E-Mails real über SES versendet (Gast an aseckzai@gmail.com: bestätigt + storniert; Restaurant an info@restaurant-durrani.de: neu + storniert inkl. Tagesstatistik) — bitte im Postfach gegenprüfen.
- Alle Testreservierungen („Aria KADiCon-Systemtest", explizit als Test kommentiert) wurden storniert; keine aktive Testreservierung verbleibt.

## Gefundener und behobener Prod-Bug
Management-Links nutzten die Verzeichnis-Form `manage/?token=` — S3/CloudFront löst das nicht auf `index.html` auf (NoSuchKey). Im Live-Akzeptanztest gefunden, Hotfix #267 (`index.html`-Form) noch in derselben Nacht produktiv, End-to-End erneut verifiziert (Link aus Create-Response → Manage-Seite → Storno). Betroffen waren nur die Mails der ersten Testreservierung.

## Widget (live)
Personen-Chips 1–7 + 8+-Stepper als erstes Element · deutsche Wochenleiste (vergangene/geschlossene Tage deaktiviert) · Zeit-Chips (nicht verfügbare sichtbar deaktiviert, nicht klickbar) · Fortschritt 1 Termin → 2 Kontaktdaten → 3 Prüfen · mobile Summary-Bar · Vorname optional/Nachname Pflicht · E-Mail-Autocomplete (14 dt. Provider, nie auto-übernehmend, Tastatur + Screenreader) · Telefon nur Ziffern/+, Paste-Normalisierung · Prüfen-Karte mit „Ändern" je Abschnitt · ehrliche Bestätigung (bestätigt vs. Anfrage eingegangen) · Marketing-Satz, tote Social-Icons, englische Reste entfernt · Bundle 2,72 → 2,46 MB · Alt-Bug gefixt: Step-1-Validierung war wirkungslos.

## E-Mails (live)
7 deutsche Templates: Gast bestätigt/Anfrage/geändert/storniert, Restaurant neu/geändert/storniert (+ Reminder restylt). Transaktions-Hierarchie statt Hero-Bild, Diff-Darstellung bei Änderungen, informative Betreiber-Betreffs (`Neue Reservierung · 4 Personen · Do. 20.08. · 17:30 · Name`), Tagesstatistik in Restaurant-TZ (Stand NACH Aktion, Stornierte nie mitgezählt), Plain-Text-Versionen, ICS-Anhang (Bugfix: wurde vorher global verworfen), Management-Link in jeder Gast-Mail. Neu: Gast bekommt auch bei unbestätigter Anfrage eine Mail (vorher: nichts). SES-Fehler können keine API-Requests mehr scheitern lassen. Preview-Renderer: `pnpm ts-node --swc --transpileOnly tools/email-preview/render.ts`.

## Reservation Management (live)
Krypto-Token (256 bit, global eindeutig, nie in Logs/Listen), Regeln: nur offene zukünftige Reservierungen; Ändern mit serverseitigem Kapazitäts-Recheck (eigene Reservierung ausgenommen) + Advisory-Lock + Transaktion; Storno nur per POST nach sichtbarer Bestätigung; saubere deutsche Fehlerseiten (ungültig/abgelaufen/bereits storniert/Slot vergeben). Tenant-Kontext via AsyncLocalStorage.run — der ursprüngliche enterWith-Ansatz wäre ein Cross-Tenant-Leak gewesen; vom eigenen Regressionstest gefangen, bevor er je lief.

## Tests
Backend 89/0 (42 neue; läuft NICHT in CI — bestehende Lücke, lokal als Gate gefahren) · Widget 61/61 (App hatte NULL Tests) · 3 Mutations-Proben (2 eigene + 2 Reviewer) je exakt vorhergesagt rot + sauber revertiert · Lint/Build/Element-Build grün · 2 adversariale Reviews (Worktree-isoliert): #265 MERGE OK 0C/0I/4M, #266 MERGE OK 0C/1I/6M — das Important (ungetesteter Manage-Paste-Pfad) vor Merge geschlossen.

## Monitoring
Nach beiden Prod-Deploys: Health grün, neue Endpoints korrekt, keine 5xx beobachtet, ECS-Circuit-Breaker (auto-rollback) blieb inaktiv. Kein Rollback nötig.

## Offene Punkte (nicht autonom abschließbar)
1. ⚠️ `apps/backend/.env` MIT AWS-Keys committed im Repo (KAR-720-Klasse) — Rotation + Entfernung liegt bei dir.
2. Backend-Tests in CI aktivieren (heute explizit excluded) — kleine Workflow-Änderung, wollte ich nicht ungefragt an eurer CI-Policy vorbei.
3. OpenAPI-Client-Regen (braucht DB-Boot); Widget nutzt solange den handgeschriebenen typisierten Service.
4. Google-Reservierungen erhalten noch keinen Management-Token (Feld wird im Google-Flow nicht gesetzt — Mails degradieren sauber ohne Button); bewusst nicht angefasst, da Google-Fläche tabu war.
5. Produktentscheidung Sprache: Widget folgt der Browser-Sprache (de-Fallback). Soll Durrani IMMER deutsch sein, genügt `language="de"` am Embed.
6. Prozess-Doku: Prod-Releases brauchen zwingend einen Version-Plan (.nx/version-plans/) — sonst deployt release.yml still nichts.
