docs: dev-Doku für den Live-Betrieb neu sortiert — Archiv + Betrieb + Roadmap

Das System ist seit 20.07.2026 live; die dev-Doku spiegelt jetzt den
Betriebszustand statt der Release-Vorbereitung:

- dev/betrieb/: Deploy-Runbook (neu: §10 Betrieb nach Go-Live mit
  SFTP-Deploy-Checkliste inkl. queue:restart-Pflicht und den
  Betriebs-Erkenntnissen der Go-Live-Woche), Legacy-API-Parität, Umami.
- dev/roadmap.md: offene Nachlauf-Punkte (Go-Live-Mails, Merge nach main,
  Stripe-Test-Artefakte, Key-Rotation, Alt-System-Abschaltung) und
  Ideen-Backlog.
- dev/archive/: abgeschlossene Phasen — Migration-2026-Planung,
  Cutover-Runbook (mit Erledigt-Vermerk), Release-Plan/QA-Checkliste,
  Optimierungsphasen, Frontend-Konzepte + Responsive-Audits vom 16.07.,
  Altsystem-Reste.
- dev/README.md als Wegweiser; gitignore-Pfad der SQL-Dumps mitgezogen
  (dev/archive/migration-2026/sql/ bleibt untracked, ebenso dev/migration/
  und dev/api/).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-21 07:52:38 +00:00
parent 58c773b153
commit 6a4c4d8837
360 changed files with 923 additions and 1 deletions

View file

@ -0,0 +1,50 @@
# Boost-Platzierung — ENTSCHIEDEN + UMGESETZT (13.07.2026)
Stand: 13.07.2026. Kontext: Boost-Add-on ist seit 10.07. Ende-zu-Ende live.
Die zwei offenen Punkte von Montag sind entschieden und umgesetzt.
## Entscheidung (User, 13.07.2026)
1. **Harte Slot-Grenzen:** max. **5** gleichzeitige Boosts auf der Startseite,
max. **3** je Branchenseite (Kategorieseite) — die Grenzen entsprechen genau
den sichtbaren Plätzen (Startseite: Lead + 4 Seitenleiste; Branchenseite:
Lead + 2 Top-Slots). Verkauft wird nur, was sichtbar ist.
2. **Zwei Boost-Stufen mit Preisunterscheidung:**
- **Startseiten-Boost** (voller Preis, 12/20/35 Credits für 7/14/30 Tage):
erscheint wie bisher auf Startseite **und** Branchenseite.
- **Kategorie-Boost** (50 % vom Boost-Preis, aufgerundet: **6/10/18
Credits**): erscheint **nur** auf der Branchenseite der Rubrik.
3. **Hero-Slider:** vorerst **nicht** umgesetzt — durch die Slot-Grenze
(5 = sichtbare Plätze) entsteht kein Sichtbarkeits-Überhang. Bleibt als
spätere Ausbaustufe notiert (dann könnte die Grenze steigen), ebenso die
Zeitraum-Buchung wie im Anzeigengeschäft.
## Umsetzung (13.07.2026, Suite: 1222 grün)
- `boosts.placement` (`home`/`category`, Enum `BoostPlacement`; Bestandsboosts
= `home`), Preise/Slots in `config/credits.php` (`boost_category`,
`boost_slots`).
- `BoostService::slotAvailability()` prüft je Ausgabe (Portal × Sprache, beim
Kategorie-Boost zusätzlich je Rubrik); Kauf am vollen Inventar wirft
`BoostNotAllowedException::soldOut()` mit dem frühesten freien Termin.
**Verlängerungen** brauchen keinen freien Slot (die PM hält ihren);
die Stufen verlängern unabhängig voneinander.
- Startseite (`HomePageData`) zeigt nur `home`-Boosts; Branchenseite
(`CategoryPageData`) zeigt Kategorie-Boosts **mit Vorrang** vor
Startseiten-Boosts (die 3 Kategorie-Slots sind die verkaufte Garantie,
Startseiten-Boosts füllen nur übrige Plätze).
- Kategorie-Boost invalidiert nur die Branchenseiten-Caches, nicht die
Startseite.
- Kunden-UI (PM-Seite): zwei Boost-Angebote im Add-on-Panel; solange buchbar,
zeigt jede Stufe „Noch X von Y Plätzen frei." (knappste Ausgabe); bei vollem
Inventar „Derzeit ausgebucht — der nächste Platz wird am TT.MM.JJJJ frei."
statt der Kauf-Buttons (serverseitig gegated).
- Preisseite (`/preise`, beide Staffeln + Leistungsbeschreibungs-Note zu
Slot-Begrenzung, de/en), Buchungsseiten-Preisliste ergänzt.
- Tests: `BoostServiceTest` (Slots/Preise/Verlängerung),
`Web/BoostPlacementTest` (Platzierung/Vorrang/Cache),
`PressReleaseShowAddonsTest` (UI/Ausgebucht), `CreditPricingServiceTest`.
**Hinweis [EXTERN]:** Die aktualisierte Boost-Note der Preise-Seite
(Leistungsbeschreibung, „was Sie bekommen und was nicht") sollte mit in die
anwaltliche Freigabe des Rechtstexte-Pakets.

View file

@ -0,0 +1,182 @@
# Entwicklungsplan: Release-Fertigstellung Pressekonto
> Konsolidiert aus vier Analysen vom **03.07.2026** (dev-Doku-Abgleich, Web-Frontend-Bestandsaufnahme, Backend-Baustellen, Deploy-Readiness).
> Reihenfolge nach **Risiko/Nutzen**: erst Korrektheit & kaputte Pfade, dann Release-Entscheidungen (Billing), dann Frontend-Fertigstellung, dann Aufräumen, dann Deploy/QA.
> Ergänzt `dev/optimize/entwicklungsplan.md` (Phasen 0–5 dort abgeschlossen; dessen Phase 6 ist hier als R4 aufgenommen).
---
## Stand & Wiedereinstieg (zuletzt: 09.07.2026)
**Phase R0 komplett; R1 bis auf [EXTERN] fertig; R2.1 erledigt.** Erledigt: R0.1–R0.5 (Variant-Routen raus, gdpr_consent_at in allen drei Registrierungspfaden, Newsletter-Binding config-aware + fail-fast, Seeder produktionssicher inkl. PlanSeeder + Dev-User-Guard, Fehlerseiten 403/404/419/500/503 self-contained) · R1.1 (Gate safe-by-default `true`, Legacy-/Grandfathered-Pfade per Test festgeschrieben, Scope-Logik war korrekt) · R1.3 (Quota-Reset als finales Tarif-Modell-Verhalten dokumentiert).
Offen in R1 (alle [EXTERN]): R1.2 Stripe-Live-Sync, R1.4 Preisfreigabe, R1.5 Refund-Entscheidung.
R2.1 Kontaktformular komplett (Backend + Web-Form, Brand-Empfaenger via CONTACT_EMAIL_*-Keys [EXTERN: echte Adressen]). Bonus-Fix: legal-request-Honeypot nickt Bots wieder still ab (size:0-Regel verriet die Falle).
**03.07. nachmittags (User-Vorgaben):** Preise/Limits der Buchungsseite sind die abgestimmte Wahrheit; Pipeline verifiziert: admin/payments/plans pflegt Plan-DB (inkl. Vorteile zeilenweise) -> erscheint auf Buchungsseite, oeffentlicher Preise-Seite, pricing-teaser UND (neu) Hub. **Vorteilstexte selbst sind noch nicht final [EXTERN: User passt sie im Admin-UI an — schlaegt automatisch ueberall durch].**
R2.2+R2.3 erledigt: Hub-Landing komplett ueberarbeitet (datengetriebenes Tarif-Grid aus Plan::active() via neuem `HubPageData`, echte Plattform-Kennzahlen statt erfundener, Fake-Kundenliste raus, alle toten Links beseitigt, unhaltbare Claims entschaerft, Footer-Rechtslinks auf /de/*-Seiten der Hub-Domain). Hub-Rechtsseiten sind erreichbar, brauchen aber weiterhin finale Texte (R3.1).
**Naechste Schritte:** R2.4 (Sitemap/robots/meta) ohne User-Input machbar; R3 Inhalte + R1.2 Stripe-Live [EXTERN].
**03.07. abends — Hub-Inhalts-Audit + Rechtstexte (Entscheidung E2: B2B-only!):** Alle Hub-/Web-Inhalte gegen den echten Funktionsstand geprueft und korrigiert (Startseiten-Claims, FAQ, Hilfe, Veroeffentlichen, API-Doku inkl. OpenAPI); kontakt/ueber-uns zurueckgebaut; Rechtstexte aus docs/legal eingebaut (Impressum echt, AGB v0.6, /richtlinien, /melden); Widerrufs-UI raus (E2), Submit-Modal auf Archiv-Hinweis (T2neu); Preise nach Leistungsbeschreibung v0.2. Details in R3.0–R3.2.
**03.07. spaet — Rechtstexte komplett + T1neu + Mail-CI (TAGESABSCHLUSS, Suite: 935 gruen):** Datenschutzerklaerung v0.3 host-aware eingebaut (Hub-/Spoke-Bausteine, Magic-Link 15 Min., Stripe Payments Europe); Cookie-Seite zurueckgebaut (301 -> /datenschutz, Cookies stehen in Abschnitt 4, kein Banner). T1neu umgesetzt: Unternehmer-Pflicht-Checkbox im Buchungs-Modal, protokolliert in `booking_confirmations` (Timestamp/IP/AGB-Version aus `config/legal.php`), CheckoutController erzwingt zeitnahe Bestaetigung serverseitig. System-Mails auf zentrales pressekonto-CI-Layout umgestellt (components/emails/layout, inkl. Framework-Auth-Mails via toMailUsing, EmailBrandingTest).
**06.07. — Zentrale Rechts-E-Mail (User-Entscheidung):** `info@pressekonto.com` ist die Haupt-E-Mail fuer alle rechtlichen Anliegen; zentral in `config/legal.php` (`LEGAL_CONTACT_EMAIL`), alle fuenf Rechtsseiten (Impressum, AGB, Datenschutz, /melden, /richtlinien) nutzen sie domainuebergreifend. Support-/Redaktionskontakt bleibt je Brand (CONTACT_EMAIL_*), REPORT_EMAIL_* ist nur noch optionales separates DSA-Postfach. Live-Domain bestaetigt .com (Deployment-Checkliste), nicht .de.
**06.07. — Umami eingebaut + Klaerungsliste KOMPLETT beantwortet:** Umami-Snippets (dev/release/umami.md, sys.adametz.media) je Brand in `config/domains.php` (`brand.umami_website_id`), gerendert via `x-web.umami` im web-master-Layout — per `UMAMI_ENABLED=false` deaktiviert bis zum Go-Live (User-Vorgabe; Checklisten-Punkt in WS-7 ergaenzt). User bestaetigt: Server-Fristen (Logs 14 Tage, Backups 4 Wochen) stimmen, Stripe-Entitaet (Stripe Payments Europe, Dublin) passt. **Inhaltliche Freigaben erteilt:** AGB par. 1 (3) Portalliste via Hub-#familie ok · AGB par. 11 (3) Kuendigungsseiten-Ergaenzung ok · Depublikations-Preis neutral ohne Betrag ok (bis T12) · Medienprivileg-Zitierung bleibt vorsichtig (par. 57 Abs. 1 MStV) bis anwaltliche Praezisierung · /melden-Aenderungsweg ueber Magic-Links fuer Launch ok. Damit sind die Rechtstexte inhaltlich final; offen nur noch [EXTERN] (anwaltliche Freigabe, Postfach info@pressekonto.com).
**06.07. — R3.1-Restbacklog KOMPLETT umgesetzt (Suite: 956 gruen):** (a) E3-Versionierung: `legal_documents`-Tabelle (type/version/effective_from, Fassungen unloeschbar, zukuenftige erst ab Stichtag; `LegalDocument::current()`, Seeder mit den 5 aktuellen Fassungen) + `legal_document_id` an `booking_confirmations` (T1neu referenziert jetzt die konkrete AGB-Fassung; config/legal.php nur noch Fallback). (b) `rejection_categories`-DB: /richtlinien rendert aktive Kategorien live (Seeder K1–K6; deaktivieren statt loeschen). (c) T13: Melde-Zeile sichtbar unter jedem Artikel. (d) T10: Formulare /melden/rechtswidrig (DSA Art. 16 inkl. Wahrheitsversicherung) + /melden/kontaktdaten (DSGVO) mit URL-Feld und automatischem PM-Match; Vorgangsnummer M-{id}; unverzuegliche Eingangsbestaetigung + internes Postfach-Mail (auch im Per-PM-Flow); Admin-Queue zeigt Vorgangsnummer/URL/Name. (e) T12: Depublikations-Checkout (Panel auf der Kunden-PM-Seite mit T1neu-Checkbox + Doppelausweis, Stripe-Ad-hoc-Charge, Webhook depubliziert → Status archived + `depublished_at`, oeffentliche URL zeigt Tombstone mit 410). **Preis entschieden (O6, User 06.07.): 35 EUR netto** — Default 3500 in config/billing.php, per BILLING_DEPUBLICATION_PRICE_CENTS ueberschreibbar (0 deaktiviert). Go-Live: beide Seeder in WS-7-Checkliste.
**06.07. — Spoke-Frontend-Audit umgesetzt (Fake raus, Newsletter zurueckgebaut, Boersendaten echt, EN-Lokalisierung):** Ergebnis des businessportal24-Frontend-Audits vollstaendig abgearbeitet: (a) **Fake-Inhalte entfernt** — Fake-Anzeigen (`focus-ad`, `feed-ad`, Inline-Ad + Studie-Box in release-detail) geloescht; alle Mock-/Fallback-Daten raus (focus-hero, feed, most-read, active-newsrooms, industry-spotlight, industry-index rendern jetzt nur noch mit echten Daten oder gar nicht); Legacy-Mock-Komponenten geloescht (`featured-releases`, `press-releases-grid`, `highlight-card`, `press-release-card`); tote Links/Buttons ersetzt oder entfernt (Newsroom-Link, Richtlinien-Link, Print statt Permalink, „Newsroom folgen" raus, RSS-Erwaehnung raus, ⌘K funktional). (b) **Newsletter zurueckgebaut** — `newsletter-strip` aus allen Spoke-Seiten + Newsletter-Box aus release-detail entfernt (Feature nachgelagert, s. unten; Backend/DOI-Hinweis in R6.1 bleibt). (c) **Boersendaten echt** — neuer `MarketDataService` (Yahoo-Finance-Quotes, `config/market.php`, Cache) + Artisan-Command `market:refresh` alle 15 Min. im Scheduler; `live-ticker` konsumiert nur noch echte Daten und blendet sich ohne Daten aus. (d) **EN-Lokalisierung** — alle UI-Texte der Kern-Seiten (Header/Footer, Home-Sektionen, release-detail, kategorie, suche, newsrooms, newsroom, Pagination) in `lang/de/web.php` + `lang/en/web.php` ausgelagert; `Carbon::setLocale('de')`-Hardcodes auf `app()->getLocale()` umgestellt. **Langform-Seiten (veroeffentlichen, faq, hilfe, api) bleiben vorerst deutsch** — hreflang="en" dort entfernt; EN-Uebersetzung nachgelagert.
**06.07. — Zwei Legacy-Befunde des Users gefixt:** (1) **Buchungs-Gate-Bug:** `grandfatheredNow` verlangte ein gesetztes `grandfathered_until` — das ist aber das explizite Legacy-Vertragsende und im Import-Regelfall NULL; dadurch hatte praktisch kein Bestandskunde eine aktive Zahlungsoption („Buchung erforderlich"). Jetzt deckelt `grandfathered_until` nur, wenn gesetzt; sonst zaehlt die laufende, vom MAN-Lauf fortgerollte Periode (`current_period_end`). Lokal: 15/22 Vereinbarungen wieder aktiv, Limits/unbegrenztes Kontingent greifen. (2) **Legacy-API-Uebernahme (User-Entscheidung, ersetzt „OUT" aus der Scope-Doku):** `legacy:import-api-keys` uebernimmt die api_keys der Altportale als Sanctum-Tokens mit identischem Secret (nur berechtigte Nutzer; Dry-Run lokal: 406 Keys); die Auth-Middleware uebersetzt `X-ApiKey`/`X-Api-Key` fuer gueltige Keys in Bearer. **Härtung 14.07.:** Query-/Body-Parameter `api_key` werden nicht mehr akzeptiert, damit Secrets nicht in Logs/Referrern landen. Unbekannte Header-Keys erhalten weiter 410+Hinweis. Die dokumentierten /service/…-Pfade sind Aliasse auf die v1-Controller (nicht uebernommene Alt-Endpoints antworten 410 mit Hinweis). Go-Live-Kette: Import laeuft NACH dem Grandfathering (WS-7 ergaenzt). Ausserdem R6.1-Vorpruefung: /thumbnails-Redirect nachgeruestet (fehlte trotz „ANGEPASST"-Vermerk), QA-Checkliste `dev/release/qa-checkliste.md` erstellt.
**06.07. — Bestandskunden end-to-end verifiziert + Adress-Luecke geschlossen (Suite: 993 gruen):** Nach dem Buchungs-Gate-Fix zeigte sich die naechste Luecke der Kette: Der MAN-Rechnungslauf ueberspringt Vereinbarungen ohne Rechnungsadresse — und der Grandfather-Import nahm Adressen nie mit, weil die Altportale sie nur als Freitext-Blob fuehren. Neu: `legacy:import-billing-addresses` (LegacyAddressParser strukturiert die Blobs best-effort, raet nie; Report der manuell nachzupflegenden Faelle; nie ueberschreibend; CY in config/countries.php ergaenzt). Lokal: 11/21 Bestandskunden mit Adresse, MAN-Lauf rechnet ab (Zypern-Rechnungen exakt wie Legacy-Brutto, kein Preissprung), 9 manuell zu klaeren (7 leere Alt-Adressen). API-Key-Import echt gelaufen (406 Tokens), connektar-Key end-to-end gegen /service und /api/v1 verifiziert (200). **Git-Hygiene vorgezogen:** Users Frontend-Umbau committet (eigener Commit), Produktiv-SQL-Dumps enttrackt + .gitignore (User-Entscheidung: ohne History-Bereinigung, Repo bleibt privat; Dumps liegen weiter lokal fuer R6.3).
**07.07. — QA-Abschnitt A abgenommen + Billing/USt-Haerten + Tarifwechsel/Downgrades:** QA-Checkliste Abschnitt A (Login & Konto) komplett manuell abgenommen; dabei 3 2FA-Befunde gefixt: 2FA-Einrichtung verlangt jetzt Code-Bestaetigung (vorher keine Login-Challenge), Recovery-Codes nur noch einmalig sichtbar, falscher Challenge-Code crasht nicht mehr (fehlende Route `two-factor.login`); E-Mail-Wechsel jetzt als Pending-Flow mit Bestaetigungslink an die neue Adresse. **Stripe-USt vervollstaendigt:** `automatic_tax` in der Checkout-Session aktiviert (USt fehlte vorher in der Session), STR-Rechnungsspiegelung liest USt aus `total_taxes` (Stripe-API liefert kein `tax`-Feld), Profil-Rechnungsadresse als Stripe-Rechnungsempfaenger (Wallet-Daten ueberschreiben sie nicht mehr); Rechnungsseite listet neue STR/MAN-Rechnungen (STR verlinkt ins Stripe-Portal), Archiv-Filter entfernt, Altsystem-Archiv nur noch fuer Bestandskunden. **Tarifwechsel im Buchungs-Modal:** Abo-Swap mit anteiliger Verrechnung; Downgrades ohne Gutschrift erst zum Periodenende, Kontingent-Sperre beim Wechsel. **UX-Runde:** Volltext-Suchindex (+ Fix doppelte Alpine-Instanz), PM-Vorschau im echten Portal-Design mit Portal-Switch, Ablehnungsbegruendung direkt im Editor, Einreichen-Modal-Panelstruktur + fixierter Kontingent-Badge, Add-ons-Modal statt Toast bei fehlendem Guthaben, Depublikations-Sektion ans PM-Seitenende + Direktlinks je Portal, `single_purchases`-ENUM um `depublication` erweitert, abgebrochener Depublikations-Checkout blockiert nicht mehr.
**08.07. — Agentur-Attribution + Depublikations-Kauf + eVatR-USt-ID-Pruefung:** Agentur-Attribution neu geloest — Uebermittler kommt aus neuem Profilfeld `users.agency_name` statt eigener Agentur-Firma; PM-Detailseite zeigt Urheber/Uebermittler-Zeile; `legacy:migrate-agency-names` uebertraegt die Alt-Agenturnamen; `companies:retire-agency-type` schafft den Firmen-Typ „Agentur" ab; Firmen-Typ-Auswahl aus der Profil-UI entfernt (Plan abgehakt, Abnahme 08.07. dokumentiert). **Depublikation end-to-end:** Kauf wird als eingeloest markiert, erzeugt Rechnung + Bestaetigungs-Mail; Buchungsseiten-Verlauf zeigt den Kauftyp, Erfolgsmeldungen gruen. **USt-ID-Pruefung (neu):** vor jeder Buchung per eVatR (EU-VIES) gegengeprueft, Stripe-Tax-IDs abgeglichen; eigene USt-IdNr. aus `BILLING_OWN_VAT_ID`. Deploy-Runbook §3a ergaenzt: FULLTEXT-Migrations-Wartungsfenster fuer `press_releases` (bei importiertem Bestand faellt jede Spaltenaenderung auf ALGORITHM=COPY = Stunden; Ablauf Indizes droppen -> migrieren -> nacheinander neu bauen = ~6 Min).
**09.07. — KI-Kennzeichnung (Art. 50 KI-VO) + PM-Editor-UX + Tages-Fixes (Suite: 1142 gruen):** KI-Kennzeichnung als **Pflicht-Deklaration fuer Text und Bilder** umgesetzt (Enums `AiTextDeclaration`/`AiImageDeclaration`, Model `PressReleaseAiDeclaration` + Migrations, Concern `InteractsWithAiDeclaration`, `AiDeclarationRequiredException`, oeffentliches Label `portal/ai-public-label`, Test `PressReleaseAiDeclarationTest`) — EU-KI-VO Art. 50 Transparenzpflicht. PM-Editor-UX: Kategorie in die Schreibflaeche, Bild-Upload als Modal-Flow. **Tages-Fixes:** OpenAPI-Route `/docs/api/v1` auf `public/api/v1.yml` umgebogen (der `docs/`-Ordner ist ein nicht mitgekommener Mount; Route lief sonst 500) + 404-Guard; `depublish`-Credits `25 -> 35` (Entscheidung O6) in config bestaetigt und die zwei stehen gebliebenen Preis-Tests nachgezogen. Suite wieder komplett gruen (1142).
**09.07. abends — R5.3 Monitoring + Buchungsdaten + kompletter WIP in 7 Commits committet (Suite: 1149 gruen):** (a) **R5.3 Monitoring** umgesetzt (Details in R5.3): `OperationalAlert` (immer `Log::critical` + Best-Effort-Mail an `MONITORING_ALERT_EMAIL`), alle 6 Scheduler-Tasks mit `->onFailure()`, Queue-`failing`-Listener, `LOG_STACK=daily` als Prod-Soll. (b) **Buchungs-/Verlaengerungsdatum** des Abos auf der Buchungsseite (`SubscriptionPeriodResolver` holt `start_date` + `current_period_end` gecacht aus Stripe, da Cashier die Periode nicht lokal spiegelt). (c) Den kompletten bis dahin uncommitteten Arbeitsbaum in **7 fokussierte Commits** zerlegt (Branch jetzt **~187 vor `main`**): OpenAPI-Route auf `public/api/v1.yml` (500-Fix) · Depublish-Credit 35 · Buchungs-/Verlaengerungsdatum · R5.3 Monitoring · Release-Doku · Backend-WIP (Kontakt-Queue, Page-Cache, Validierungs-Feedback, Company-Portal-Restriction, i18n-Auslagerung) · Web-Frontend-Umbau (separat, User-Bereich; Kontakt-Modal durch `contact-launcher` ersetzt). **Bewusst uncommittet belassen:** `sidebar.blade.php` (User-Regel, aus allen Commits ausgeschlossen), der geloeschte `docs/`-Mount + `config/pressekonto.code-workspace` (Mount nicht mitgekommen — die einzige code-relevante Datei `docs/api/v1.yml` liegt jetzt als `public/api/v1.yml` vor), zwei Dev-Screenshots in `dev/frontend/booking/`.
**10.07. — PM-Tracking (User-Einschub, `docs/weiteres/Pm Tracken.md`):** Ereignis-Log `press_release_events` (type view/share, channel, ohne Personenbezug) + Model/Enums/Factory. Aufrufe: Legacy-`hits`-Zaehler bleibt die angezeigte Wahrheit (bewusst einfacher Page-Request-Zaehler, User-Vorgabe), zusaetzlich View-Event je Detailaufruf; oeffentliche Detailseite zeigt Aufrufe wieder in der Meta-Zeile (wie im Altsystem, beide Hero-Varianten, nicht in der Vorschau). Shares: `POST …/press-release/{slug}/share` (throttle 30/min, nur archiv-sichtbare PMs) loggt LinkedIn/X/E-Mail/Link-kopiert-Klicks der Teilen-Buttons (fetch keepalive + CSRF-Meta); Kundenpanel-PM-Seite zeigt neue Kachel „Geteilt" mit Gesamtzahl + Kanal-Aufschluesselung. Tests: `Web/PressReleaseTrackingTest` (7). Hinweis: Branch war zu Sessionbeginn bereits vollstaendig gepusht; die „drei user_filter_presets-Migrations" (R6.5 optional) existieren nicht mehr — es gibt nur noch eine konsolidierte, Punkt gegenstandslos.
**10.07. nachmittags — R2.5 + QA-Nachpruefung + R4.3 + KI-Queue-Fix + API-KI-Deklaration (Suite: 1165 gruen):** R2.5 erledigt (legal-request-Texte aus freigegebenem Meldeverfahren, s. R2.5) · QA-Checkliste B–G vertieft (s. R6.1) · R4.3 erledigt (Karten-Helper am Model, s. R4.3). **Befund KI-Pruefung:** Auto-Routing (Rot → Ablehnung, Gelb/Gruen → Auto-Publish) war laengst gebaut, lief lokal aber nie — `composer run dev` startete den Worker ohne `--queue=default,classification`; gefixt, Backlog abgearbeitet (Pipeline live verifiziert; Prod-Runbook war korrekt). **Befund + Fix API (Entscheidung User 10.07.):** v1-API hatte keine KI-Deklarationsfelder → Submit lief seit 09.07. immer auf 422 (haette alle 406 Legacy-API-Kunden blockiert; Suite uebersah es, weil die Factory deklariert). Jetzt: `ai_text_declaration` Pflicht im v1-Submit (Call gilt dokumentiert als verbindliche Bestaetigung + Archiv-Kenntnisnahme AGB § 13), `ai_declaration` Pflicht je Bild-Upload; Legacy-/service-Pfade defaulten auf none/original (Mailing R6.4 weist darauf hin, dass die weitere Nutzung diese Erklaerung darstellt); OpenAPI + /api-Seite aktualisiert. Tests: Submit-/Image-/Legacy-API-Tests erweitert. **Beobachtung fuer QA/Launch:** Test-Kauderwelsch („asdas dsada…") wurde Gruen klassifiziert und auto-publiziert — vor Go-Live Prompt/Schwelle pruefen bzw. `CLASSIFICATION_GREEN_DELAY` als Sicherheitsfenster setzen [USER-ENTSCHEIDUNG].
**10.07. abends — API-Legacy-Paritaet (neues Arbeitsdokument `dev/release/api-legacy-paritaet.md`):** User-Vorgabe: /service-API muss beim Cutover verhaltensgleich zum Altsystem laufen. Alt-Vertrag vollstaendig aus dem Symfony-Code (`_businessportal24.com/`) extrahiert; Prod-Aktivitaets-Logs (User, `dev/api/` — enttrackt via .gitignore, Kundendaten!) ausgewertet: **ein echter Client (connektar), 5 Endpoints, Sofort-Publish beim POST belegt; 2 der 5 Endpoints (Company-/Kontakt-Create) laufen heute auf 410.** Entscheidungen: Sofort-Publish nachbilden + Admin-Schalter fuer KI-Pruefung der Legacy-Einlieferungen; Legacy-API und v1 bleiben getrennt; Ablehnungs-Rueckkanal spaeter in v1. S1+S2 noch am 10.07. umgesetzt: Legacy-Compat-Controller fuer die 5 connektar-Endpoints (inkl. Company-/Kontakt-Create, die vorher 410 liefen), Anlegen = Einreichen mit Sofort-Publish via Permission `api:immediate-publication` (WS-7: connektar zuweisen!), Admin-Schalter "KI-Pruefung Legacy-API" (neue app_settings-Tabelle, Toggle auf /admin/press-releases) + KI-Spalte/Pruef-Button in der Admin-PM-Liste; API-Einlieferer-Mails gehen an Pressekontakt/Firmen-Mail statt an den Einlieferer (User-Entscheidung). Tests: LegacyServiceParityTest. **Offen: S4 Testdaten (User-Vorgaben ausstehend), S5 Staging-Smoke, S7 Mailing.**
**13.07. — Boost-Platzierung entschieden + umgesetzt (Suite: 1222 gruen):** User-Entscheidung: harte Slot-Grenzen (5 Startseite, 3 je Branchenseite = exakt die sichtbaren Plaetze; Hero-Slider damit vorerst unnoetig, bleibt Ausbaustufe) + zweite Boost-Stufe **Kategorie-Boost** (nur Branchenseite, 50 % aufgerundet: 6/10/18 Credits; Startseiten-Boost unveraendert 12/20/35 inkl. Branchenseite). Umsetzung: `boosts.placement` + `BoostPlacement`-Enum, `credits.boost_category`/`boost_slots`, `BoostService::slotAvailability()` (je Portal x Sprache, Kategorie zusaetzlich je Rubrik; Verlaengerung haelt ihren Slot; ausgebucht -> Exception mit fruehestem freien Termin), Startseite zeigt nur Home-Boosts, Branchenseite Kategorie-Boosts mit Vorrang, Kategorie-Boost invalidiert nur Kategorie-Caches, Add-on-Panel mit zwei Angeboten + „Derzeit ausgebucht — frei ab"-Anzeige, Preise-/Buchungsseite + Lang-Keys (de/en) inkl. Slot-Hinweis in der Leistungsbeschreibungs-Note (**[EXTERN]: in anwaltliche Freigabe aufnehmen**). Details in `dev/release/boost-platzierung-offen.md`.
**14.07. — Optimierungs-Audit vollständig priorisiert umgesetzt:** Alle kritischen/hohen Befunde sowie M1–M8 und M11–M13 aus `dev/optimize/optimize-14-07-2026.md` sind code-seitig geschlossen: granulare Admin-Autorisierung inkl. Livewire-Methoden-Guards und admin-only Legacy-KI-Schalter; Quota-/Publish-Transaktionen mit Row-Locks; Slug-Unique-Retry; API-/Legacy-HTML-Sanitizing; serverseitige Attachment-MIME-Prüfung; Newsletter-Ownership/Confirmation; Legacy-Keys nur noch im Header; Deferred-View-Tracking; Suche-/Newsroom-Caches. Neue PM-Medien liegen privat und werden abhängig von öffentlicher Sichtbarkeit bzw. Policy ausgeliefert. **Deploy-Pflicht:** `php artisan db:seed --class=RolesAndPermissionsSeeder` und einmalig `php artisan press-releases:privatize-media`. **[EXTERN/DRINGEND]:** OpenAI-, Google- und Mail-Secrets beim Anbieter rotieren; Produktiv-Env mit `APP_ENV=production`, `APP_DEBUG=false`, `SESSION_SECURE_COOKIE=true`.
**Wiedereinstieg / naechste Schritte:**
1. ~~Klaerungsliste Rechtstexte~~ **erledigt 06.07.**; ~~R3.1-Restbacklog~~ **erledigt 06.07.** (siehe oben).
2. **[EXTERN]:** Postfach `info@pressekonto.com` anlegen (Haupt-Rechts-Mail, 06.07.); CONTACT_EMAIL_*-Support-Postfaecher je Brand; REPORT_EMAIL_* optional · R1.2 Stripe-Live · R1.4 Preisfreigabe · R5.4 Favicons · anwaltliche Freigabe AGB v0.6/Datenschutz v0.3. (~~Depublikations-Preis~~ entschieden: 35 EUR netto.)
3. **Phase-2-Rest (bewusst nach Launch):** T11-Ausbau DSA-Queue (Basis existiert) · englische Rechtstexte (O-C, nach Sprachvarianten-Entscheidung) · oeffentliche Versionshistorie der Rechtstexte (O-D) · Ablehnungs-Mails mit Kategorie-Link (rejection_categories im Reject-Flow verdrahten).
4. **Dann:** R6.1 manuelle QA-Durchgaenge (User, per qa-checkliste.md), R6.3 Staging-Rehearsal [EXTERN], R6.4 Go-Live-Kette, R6.5 Git-Hygiene-Rest: Branch (~187 Commits vor main, Stand 09.07.) pushen + mergen + Release-Tag, sobald freigegeben (~~vorher WIP zerlegen~~ **erledigt 09.07., 7 Commits**); optional die drei user_filter_presets-Migrations zusammenfuehren. (~~SQL-Dump-Entscheidung~~ erledigt 06.07.: enttrackt + ignoriert.)
---
## ▶ Wo es morgen weitergeht (Stand 09.07.2026 abends)
**Ausgangslage:** Arbeitsbaum ist aufgeraeumt — der gesamte WIP ist in 7 Commits auf `fix/zeitzone-europe-berlin` (~187 vor `main`), Suite gruen (1149). Uncommittet bleiben nur bewusst: `sidebar.blade.php` (User), der `docs/`-Mount + `config/pressekonto.code-workspace`, zwei Dev-Screenshots.
**Als Naechstes in meiner Hand (kein externer Input noetig), Empfehlung in dieser Reihenfolge:**
1. ~~**QA-Checkliste B–G vertiefen**~~ **erledigt 10.07.:** alle Beleg-Tests verifiziert (Suite 1156 gruen), B–G um die Features 07.–10.07. ergaenzt (KI-Kennzeichnung, Tracking, Kontakt-Queue, Tarifwechsel, eVatR, Monitoring u. a.), Frontend-Umbau-Vorbehalt in F aufgehoben. Neuer (unkritischer) Befund: Page-Data-Cache ohne Publish-Invalidierung, max. 5 Min. Verzug auf Home/Kategorie — dokumentiert, kein Blocker. Jetzt sind die manuellen Browser-/Staging-Durchgaenge (User) dran.
2. **R4.3 Dedup** (optional, „nur wenn Zeit") — Release-Card-Varianten (feed-item, focus-hero, focus-side-item, feed-top-item) vereinheitlichen, Inline-SVGs (13 Web-Komponenten) → Icon-Set; Industrie-Icon-Map hat der Frontend-Umbau bereits erledigt. Kein Blocker.
3. ~~**R6.5 optional:** die drei `user_filter_presets`-Migrations zusammenfuehren~~ **gegenstandslos (10.07.):** es existiert nur noch eine konsolidierte Migration.
**Wartet auf Dich / extern (nicht durch mich abschliessbar):** R6.5 Branch merge + Release-Tag (Dein Go; Push ist erfolgt) · R1.2 Stripe-Live · R1.4 Preisfreigabe · R1.5 Refund-Entscheidung · (~~R2.5 legal-request-Texte~~ erledigt 10.07., Wortlaut aus freigegebenem Meldeverfahren) · R5.4 Favicons · R6.3 Staging-Rehearsal · R6.4 Go-Live-Kette · Postfach `info@pressekonto.com` · anwaltliche Freigabe.
~~**Thema für Montag (13.07., Entscheidung User):** Boost-Platzierung~~ **entschieden + umgesetzt 13.07.** (Slot-Grenzen 5/3, Kategorie-Boost zu 50 %, Slider vertagt — s. Stand-Block + `dev/release/boost-platzierung-offen.md`).
**Bewusst nachgelagert (nicht Teil dieses Release):** Sentry/APM · User-Panel-Vollflow · Newsletter-DOI · EN-Rechtstexte · T11-DSA-Queue-Ausbau.
---
## Ausgangslage (Stand 03.07.2026)
- **Testsuite komplett grün:** 838 passed, 1 skipped (3.678 Assertions, ~70 s).
- **Branch:** `fix/zeitzone-europe-berlin`, **73 Commits vor `main`**, 62 davon ungepusht. `main` hat nichts Neues → Merge/Push ist Teil des Release (R6.5).
- **Keine Debug-Reste** (`dd`/`dump`/Test-Routen) im Backend; Mailables vollständig mit Views; Scheduler (5 Tasks) angeschlossen.
- **Wichtig:** `dev/migration 2026/12-NAECHSTE-SCHRITTE.md` ist teilweise **veraltet** — Stripe/Billing (dort „Phase 8 komplett offen") ist laut `dev/frontend/hub-flux/PROGRESS.md` seit 12.06. umgesetzt (Checkout, Webhooks, Tarif-Admin, Grandfathering). Restaufwand-Schätzungen von dort nicht übernehmen.
- **Nachgelagert (User-Entscheidung 03.07.):** kompletter getesteter Flow fürs User-Panel — bewusst NICHT Teil dieses Plans.
---
## Arbeitsregeln (für jeden Schritt)
- **Test-First bei Korrektheits-/Verhaltensänderungen** (R0–R2): erst Pest-Test, dann ändern.
- Tests gezielt laufen: `php artisan test --compact --filter=<Name>` (wir sind bereits im Sail-Container).
- Nach PHP-Änderungen: Pint (`--dirty`).
- **Web-Frontend** (`resources/views/web/**`, `components/web/**`) gehört dem User → separat committen, **vor jedem Löschen bestätigen lassen**.
- `resources/views/components/layouts/app/sidebar.blade.php` gehört dem User und bleibt aus allen Commits ausgeschlossen (`git reset -q -- …` vor jedem `git add`).
- Jeder Schritt = ein eigener, fokussierter Commit.
- Punkte mit **[EXTERN]** brauchen Input vom User/Auftraggeber (Texte, Entscheidungen, Zugänge) — früh anstoßen, parallel weiterarbeiten.
---
## Phase R0 — Korrektheit & kaputte Pfade (klein, sofort, Test-First)
- [x] **R0.1 Variant-Routen reparieren** — `routes/web.php:211-227`: `/variant-1` + `/variant-2` verweisen für die Businessportal-Domain auf `web.businessportal24-variant-float-glow` / `-glass-gradient` — **diese Views existieren nicht** → 500. Entscheidung: Routen entfernen (Design-Experimente sind abgeschlossen) oder auf `web.businessportal24` zurückfallen. Regressionstest.
- [x] **R0.2 `gdpr_consent_at` im Registrierungsflow befüllen** — Feld existiert (Model-Cast + Migration), wird aber **nirgends geschrieben** (verifiziert 03.07.). Bei Registrierung (Fortify `CreateNewUser`), Magic-Link-/Social-Provisioning (`CustomerAccountProvisioner`) setzen. DSGVO-relevant. Test-First. *(= rev-1 aus migration-Doku)*
- [x] **R0.3 Newsletter-Sync-Binding ehrlich machen** — `AppServiceProvider.php:47` bindet `NewsletterSyncClient` **unconditional** an `NullNewsletterSyncClient`; `NEWSLETter_SYNC_ENABLED=true` bewirkt nichts, das Admin-UI (`admin/newsletter/sync`) suggeriert aber eine funktionierende Sync. Fürs Release: No-Op bleibt (echte Sync ist nachgelagert), aber Binding config-aware machen und im Admin-UI den Placeholder-Status sichtbar ausweisen. Test.
- [x] **R0.4 Seeder produktionssicher machen** —
- `PlanSeeder` (Starter/Business/Pro/Agency) wird von `DatabaseSeeder` **nicht** aufgerufen → in den Produktions-Seed-Pfad aufnehmen (eigener `ProductionSeeder` oder Aufnahme + Guard).
- `PaymentOptionSeeder` schreibt Platzhalter (`prod_placeholder_monthly`, `price_placeholder_monthly`, …) → echte IDs via `billing:sync-stripe-plans` bzw. Seed anpassen (R1.2-Abhängigkeit).
- Test-User (`admin@pressekonto.test`, `test@example.com`, Passwort `password`) in `DatabaseSeeder` nur noch in `local`/`testing` anlegen.
- [x] **R0.5 Fehlerseiten erstellen** — `resources/views/errors/` existiert nicht → 404/500/503 (mind. 404 + 503 theme-/domain-aware fürs Web, neutrale Variante fürs Portal). Render-Tests.
---
## Phase R1 — Backend-Release-Entscheidungen (Billing/Gates)
- [x] **R1.1 Buchungs-/Tarif-Gate prüfen & bereinigen (User-Vorgabe 03.07.)** — Das Gate muss vollständig geprüft und bereinigt werden; **Legacy-Buchungen müssen funktionieren** (Bestandskunden mit Alt-Abo/Grandfathering dürfen veröffentlichen). Ist-Stand beim Testen: ohne gebuchtes Paket keine Veröffentlichung möglich (Gate lokal scharf via `.env`: `BILLING_ENFORCE_BOOKING=true`) — Soll-Verhalten bestätigt. Teilschritte:
- Alle Gate-Pfade durchgehen: `User::hasActiveBooking()`, `hasUnlimitedPressReleaseQuota`, `openSinglePurchaseCount()`, `UserPaymentOption::scopeActiveNow()`/`scopeGrandfatheredNow()`, Submit-Gate im `PressReleaseService`.
- **Legacy-Buchungen end-to-end testen:** migrierter User mit Alt-Abo (grandfathered, gültiges `grandfathered_until`) kann einreichen/veröffentlichen; abgelaufener Bestandsschutz wird korrekt geblockt (`NULL = abgelaufen`, Entscheidung 19.06.).
- Config-Default absichern: `config/billing.php` default `false` — bei vergessenem Prod-Env-Key liefert `hasActiveBooking()` für **alle** `true` → Default auf `true` drehen oder Runbook-Pflichtkey; `.env.example` korrigieren.
- Suite + manueller Submit-Flow (mit/ohne Buchung, mit Legacy-Buchung) gegengetestet.
- [ ] **R1.2 Stripe produktiv vorbereiten** — `billing:sync-stripe-plans` gegen **Live-Mode** ausführen (bislang nur Test-Mode); `STRIPE_PRICE_SINGLE_PM` setzen (sonst ist Einzel-PM-Checkout stumm deaktiviert, `CheckoutController.php:58`); Plan-`stripe_price_id_monthly/_yearly` gepflegt (sonst Abo-Checkout deaktiviert). Danach Checkout-Smoke-Test inkl. Webhook. **[EXTERN: Live-Stripe-Zugang]**
- [x] **R1.3 Quota-Reset-Provisorium festschreiben** — `ResetMonthlyPressReleaseQuota` ist als „temporärer Stub bis zum echten Tarif-/Credit-Modul" markiert, läuft aber monatlich per Scheduler. Prüfen ob das Verhalten fürs Release korrekt ist (Zusammenspiel mit Credit-Wallet/Add-ons); Doc-Kommentar aktualisieren oder Logik ersetzen.
- [ ] **R1.4 Preise/Lineup final abstimmen** — `PreisePageData.php:44` TODO („Lineup/Preise hängen am Bookings-/Magic-Link-Rework"): öffentliche Preisseite (correction/update/depublish-Pfade) gegen finale Preisliste prüfen. **[EXTERN: Preisfreigabe]**
- [ ] **R1.5 Refund-Workflow: Entscheidung** — laut hub-flux offen. Empfehlung: nachgelagert (Refunds initial direkt im Stripe-Dashboard), Entscheidung dokumentieren.
---
## Phase R2 — Web-Frontend funktional fertigstellen
- [x] **R2.1 Kontaktformular anbinden** — `kontakt.blade.php:52` TODO: aktuell nur `mailto:`, kein Form/Handler; Telefon/Adresse sind Fake-Daten. Umsetzung analog `LegalRequestController` (POST-Controller + Validierung + Honeypot + `throttle` + Mailversand an konfigurierbare Adresse). Feature-Tests. Echte Kontaktdaten **[EXTERN]**.
- [x] **R2.2 Hub-Pricing datengetrieben** — `pressekonto.blade.php:401/469/538`: 3 hartcodierte Pricing-`<article>`s (0 €/49 €/149 €) → `<x-web.pricing-teaser>` (iteriert `Plan::active()`, wird von beiden Spokes schon genutzt). Beseitigt Drift-Risiko zum Plan-Katalog. *(= alt Phase 6.1)*
- [x] **R2.3 Tote `href="#"`-Links in Live-Seiten fixen** — pressekonto (16×, u. a. Kundenlogos + Support-/Datenschutz-Links), release-detail (3×, Share-Aktionen), `hub/top-utility-bar` (3×), `events-week`/`highlight-card`. Echte Ziele setzen oder Elemente entfernen.
- [x] **R2.4 SEO-Basics** — Erledigt 03.07.: Host-aware `sitemap.xml` (Hub: Landing + Rechts-/Info-Seiten; Spokes: zusätzlich Kategorien, öffentliche Newsrooms, alle im Archiv sichtbaren PMs des Portals; 1h-Cache pro Host). `robots.txt` jetzt dynamische Route mit Sitemap-Verweis + Disallow (/admin, /login, /register, Suche) — die statische `public/robots.txt` wurde entfernt (**Deploy-Hinweis:** nicht wieder anlegen, sie würde die Route verschatten). karriere/partner/presse/team: Brand pro Request via `WebPortalContext` (statt hartcodiert „Business Portal 24") + Meta-Descriptions; bp24-Home auf config-getriebenes meta_title/meta_description-Muster vereinheitlicht. Sitemap listet bewusst nur die de-Edition (en-Entscheidung offen, s. Rechtstexte-Doku O3) und lässt Suche + Marketing-Platzhalterseiten (R3.2) draußen. Tests: `SitemapRobotsTest`.
- [x] **R2.5 legal-request-Hinweistexte finalisieren** — Erledigt 10.07.: Die drei Hinweistexte (Meldung/DSGVO/Persönlichkeitsrecht) aus dem am 06.07. inhaltlich freigegebenen Meldeverfahren v0.1 (`static.report.*`) abgeleitet und auf den echten Flow gebracht (Vorgangsnummer M-{id}, unverzügliche Eingangsbestätigung nach DSA Art. 16 Abs. 4 sofern E-Mail angegeben, kostenlose manuelle Prüfung, DSGVO ohne pauschale 30-Tage-Zusage, Querverweis auf /melden). ENTWURF-Marker entfernt; Render-Test erweitert (`LegalRequestTest`). Die ausstehende anwaltliche Freigabe des Rechtstexte-Pakets [EXTERN] deckt diese Texte mit ab.
---
## Phase R3 — Inhalte [EXTERN, früh anstoßen, parallelisierbar]
- [x] **R3.0 Online-Widerruf (gesetzlicher Widerrufsbutton)** — Umgesetzt 03.07., **danach per Entscheidung E2 (B2B-only, `docs/legal/Entscheidung.md` v2.0) zurückgebaut:** Das Angebot richtet sich ausschließlich an Unternehmer (§ 14 BGB), ein Widerrufsrecht besteht nicht — Button + Modal aus Hub-Footer/Static-Shell entfernt, Widerrufsbelehrung archiviert. Backend (`POST /widerruf`, `withdrawal_requests`, Mails, Admin-Queue) bleibt schlafend erhalten und ist bei B2C-Öffnung reaktivierbar.
- [x] **R3.0b Kündigungsbutton** — Erledigt 03.07.: „Verträge hier kündigen"-Button (Hub-Footer + jetzt auch Spoke-Footer) → öffentliche Bestätigungsseite `/de/kuendigung` ohne Login → persistiert `termination_requests` + sofortige Zugangsbestätigung per Mail + Betreiber-Mail; Admin-Queue mit K-Vorgängen. **Einordnung nach E2:** § 312k ist im B2B keine Pflicht — der Selfservice bleibt bewusst als Anti-Zombie-Standard (T3neu).
- [x] **R3.0c Erlöschens-Checkbox → Archiv-Hinweis (T2neu)** — Die § 356-Erlöschens-Bestätigung entfiel mit E2 (B2B-only); an ihrer Stelle verlangt das Einreichungs-Modal jetzt die von AGB § 13 Abs. 1 geforderte Bestätigung der dauerhaften Archivierung (Depublikation nur als kostenpflichtiger Service, gesetzliche Ansprüche unberührt). Zeitpunkt wird weiter als `withdrawal_waiver_consented_at` protokolliert (Spaltenname historisch — Umbenennung optional). Index-Schnell-Submit (wire:confirm) bleibt durch das gemeinsame Modal ersetzt.
- [x] **R3.1 Rechtstexte final** (Code komplett; Rest nur [EXTERN]) — **Inhalte eingebaut 03.07. (Quelle: `docs/legal/*`, untracked):** Impressum (echte Anbieterdaten adametz.media/Bielefeld, DDG, MStV, DSA-Kontaktpunkt via neuer `REPORT_EMAIL_*`-Keys), AGB v0.6 (B2B-Fassung, §§ 1–16, interne Anwalts-Anmerkungen entfernt, Version zentral in `config/legal.php`), **Datenschutzerklärung v0.3** (host-aware Bausteine: Hub Kundenkonto/Login/Stripe, Spokes Medienprivileg/Newsroom/Änderungslinks; Magic-Link-TTL 15 Min. aus dem Code, Logs 14 Tage, Stripe Payments Europe), neue Seiten `/richtlinien` (Rahmentext + K1–K6 statisch) und `/melden` (DSA/DSGVO/Änderung/Depublikation, verdrahtet mit den bestehenden Melde-Links je PM); **Cookie-Seite zurückgebaut** (User-Entscheidung: Cookies in Datenschutz Abschnitt 4, kein Banner; 301 auf /datenschutz). **T1neu erledigt:** Unternehmer-Pflicht-Checkbox im Buchungs-Modal, protokolliert in `booking_confirmations` (Timestamp, IP, AGB-Version); CheckoutController verlangt serverseitig eine zeitnahe Bestätigung (30 Min.) für Abo/Einzel-PM/Topup. Footer + Sitemap ergänzt; Preise-Seite nach Leistungsbeschreibung v0.2 (B2B-Satz, T9-Credit-Anker, Slots/Credits/Boost/Partner-Blöcke, T16-Wording inkl. PlanSeeder). **Restbacklog erledigt 06.07.** (Details im Stand-Block oben): legal_documents-Versionierung + legal_document_id an Buchungen (E3), rejection_categories-DB live auf /richtlinien, T10-Meldeformulare mit Vorgangsnummer + Eingangsbestätigung, T12-Depublikations-Checkout mit Tombstone (config-gated), T13-Melde-Zeile an jeder PM; Umami eingebaut (aus bis Go-Live), zentrale Rechts-Mail info@pressekonto.com. **Noch offen [EXTERN]:** anwaltliche Freigabe AGB v0.6/Datenschutz v0.3 · Postfach info@pressekonto.com. (Depublikations-Preis entschieden: 35 EUR netto, O6.)
- [x] **R3.2 Marketing-Seiten: Inhalt oder Rückbau** — Entschieden + umgesetzt 03.07. (User: Rückbau): `presse`, `team`, `karriere`, `partner` und (2. Runde) `kontakt`, `ueber-uns` samt Routen entfernt (erfundene Adresse/Telefon/Anfahrt bzw. Team/Kennzahlen/Timeline); GET-Alt-Pfade → 301 Startseite, `kontakt.store`-POST bleibt fürs Hub-Modal, Spoke-Footer-Kontakt als mailto (Fallback Betreiber-Postfach bis R2.1). `hilfe` und `faq` inhaltlich auf den echten Funktionsstand gebracht (tote #-Links raus, echte Limits/Features), `veroeffentlichen` vom Fantasie-Preismodell („ab 89 €", Credits je PM, Cross-Publishing separat) auf echte Konditionen umgestellt (datengetrieben via `VeroeffentlichenPageData`). `api`-Seite komplett neu als echte Referenz + OpenAPI `docs/api/v1.yml` (Route lief vorher auf nie existierende Datei → 500 aus Hub-Footer/FAQ).
---
## Phase R4 — Web-Frontend aufräumen (= alt Phase 6; vor jedem Löschen bestätigen)
> Kontext (User, 03.07.): Bei der Frontend-Entwicklung gab es eine **alte Version** — es können noch **Fragmentseiten aus dieser Version** herumliegen. Die Funde unten (examples/, altes engl. Header/Footer-Layout, welcome, brand-mark, Variant-Views) passen dazu; beim Aufräumen gezielt nach weiteren Alt-Fragmenten suchen (auch in `components/web/**` und ungerouteten Blades).
- [x] **R4.1 Tote Dateien entfernen** — Erledigt 03.07. (User-Freigabe): `web/examples/` (4 Demo-Views), `welcome`, `home` („B2IN"-Fragment), `brand-mark`-View (die gleichnamige, viel genutzte *Komponente* bleibt), `layouts/web-master-test`, altes engl. `layouts/header`+`footer` — alle per grep verifiziert (inkl. dynamischer Auflösung).
- [x] **R4.2 Tote Komponenten entfernt** — Erledigt 03.07.: die 12 gelisteten `x-web`-Komponenten + 7 zusätzlich gefundene Chain-Dead-Komponenten (`main-navigation`, `breadcrumb`, `page-header`, `content-layout`, `article-card`, `section-header`, `sidebar-widget` — nur von examples/ bzw. anderen toten Kandidaten referenziert). Verwaiste `.highlights-slider`-CSS-Regeln mit entfernt, Web-Build gegengeprüft. Suite grün.
- [x] **R4.3 Dedup** — Erledigt 10.07. (angepasster Zuschnitt nach dem Frontend-Umbau): Die 5× kopierte Karten-Metadaten-Logik (Kategorie-Übersetzung mit Locale-Fallback, card-Bild-URL, Teaser, Lesezeit, Kompakt-Zeitstempel) in Präsentations-Helper am `PressRelease`-Model gezogen (`localizedCategoryName`, `cardImageUrl`, `teaserText`, `wordCount`, `readTimeMinutes`, `compactPublishedLabel`); feed-item, feed-top-item, focus-side-item, focus-hero, kategorie, newsroom, release-detail, veroeffentlichen + `HomePageData` umgestellt — Markup unverändert. Lesezeit-Formel vereinheitlicht (2 Stellen nutzten `ceil` statt `round`). Tests: `Web/PressReleaseCardHelpersTest`. **Bewusst gelassen:** Inline-SVGs → Icon-Set (max. 2–3 echte Dubletten in 13 Komponenten, hoher Churn im frischen User-Frontend, kein funktionaler Gewinn); Industrie-Icon-Map hatte der Umbau bereits erledigt.
---
## Phase R5 — Deploy-Readiness
- [x] **R5.1 `.env.example` vervollständigen** — Erledigt 03.07.: alle projektspezifischen Keys ergänzt und dokumentiert (Multi-Domain `APP_PORTAL_*`/`APP_PRESSEECHO_*`/`APP_BUSINESSPORTAL_*`/`APP_PROTOCOL`, `SANCTUM_STATEFUL_DOMAINS`, Legacy-DBs, Stripe/Cashier/Billing inkl. `STRIPE_PRICE_SINGLE_PM` + `BILLING_OWN_VAT_ID`, KI `OPENAI_*`/`CLASSIFICATION_*`/`CONTENT_SCORE_*`, `NEWSLETTER_SYNC_*`, `VITE_DEV_SERVER_URL`, `SESSION_SECURE_COOKIE`-Prod-Hinweis). Werte selbst (Live-Keys, Adressen) bleiben [EXTERN].
- [x] **R5.2 Deploy-Runbook schreiben** — Erledigt 03.07.: `dev/release/deploy-runbook.md` — Server-Voraussetzungen, Prod-`.env`-Solltabelle, Build-/Release-Schritte (composer, `npm run build`, storage:link, migrate --force, Stammdaten-Seeder, Caches, queue:restart), Queue-Worker (Queue `classification`, systemd-Beispiel), Cron/Scheduler (5 Tasks mit Ausfall-Folgen), Stripe-Webhook, Erst-Einrichtung (Legacy-Import-Reihenfolge), Smoke-Test-Checkliste, Rollback. Enthält den robots.txt-Deploy-Hinweis aus R2.4.
- [x] **R5.3 Monitoring/Alerting** — Umgesetzt 09.07. (Entscheidung: Mail-Alerts + Log-Rotation, ohne neues Dependency): zentraler `OperationalAlert`-Helper (immer `Log::critical`, zusätzlich Best-Effort-Mail an `MONITORING_ALERT_EMAIL`, Fallback `LEGAL_CONTACT_EMAIL`, feature-flagged via `MONITORING_ALERTS_ENABLED`); alle 6 Scheduler-Tasks mit `->onFailure()` verdrahtet, Queue-`failing`-Listener in `AppServiceProvider`; `.env.example`/Runbook: `LOG_STACK=daily` als Prod-Sollwert (Rotation 14 Tage). Tests: `OperationalAlertTest`. **Bewusst offen (nachgelagert):** echtes APM/Sentry (neues Dependency + DSN [EXTERN]) — der Log-+Mail-Weg deckt Ausfall-Erkennung fürs Release ab.
- [ ] **R5.4 Favicons je Domain** — `domains:generate-favicons` erzeugt bewusst leere Platzhalter-Dateien; echte Favicons für pressekonto/presseecho/businessportal24 deployen. **[EXTERN: Assets]**
---
## Phase R6 — QA & Go-Live (teils extern/Ops)
- [~] **R6.1 Manuelle QA-Checkliste durcharbeiten** — **Vorprüfung + aktualisierte Checkliste erledigt 06.07.:** `dev/release/qa-checkliste.md` ersetzt §15 der Scope-Doku (jeder Punkt mit Beleg-Tests bzw. konkreten manuellen Schritten; Testzugänge, Reihenfolge lokal → Staging). **Befunde:** (1) /thumbnails-Redirect fehlte trotz „ANGEPASST"-Vermerk → gebaut (`LegacyMediaRedirectController` + Tests, 301 auf neue Storage-URLs für PM-Bilder/Logos). (2) Newsletter-DOI unvollständig → bewusst vertagt, kein Launch-Blocker (kein öffentliches Formular verlinkt), vor Newsletter-Aktivierung DOI-Flow bauen. **Offen:** manuelle Browser-Durchgänge (User) — Kategorieseite/Veröffentlichen-Landing erst nach Abschluss des laufenden Frontend-Umbaus abnehmen; Stripe-/Scheduler-/Mail-Punkte beim Staging-Rehearsal (R6.3).
- [x] **R6.2 Security-Review** — Audit + Härtung erledigt 03.07., zweimal erweitert 14.07. **Solide befunden:** CSRF (Stripe-Webhook via Signatur, keine Ausnahmen), Magic-Link-Krypto (64-Zeichen-Token, sha256-gehasht, atomare Einmalverwendung), HTMLPurifier-Sanitizing der PM-Texte, Raw-Queries durchgängig mit Bindings, IDOR-Schutz doppelt (Scope + Policy), BasicAuth timing-sicher, Session host-only korrekt fürs Hub-and-Spoke-Setup. **Gefixt:** Security-Header-Middleware (app-seitig), Logo-Upload im Company-Create über ImageService (vorher SVG/XSS möglich), Rate-Limits für Passwort-Reset/Registrierung/Consume-Routen, `EnsureUserIsCustomer` als persistente Livewire-Middleware; PM-Medien liegen jetzt privat statt nur URL-obskur und werden kontrolliert ausgeliefert; Legacy-API-Key nur noch im Header und Legacy-Schreibpfade erzwingen Token-Abilities; Admin-Autorisierung, Impersonation-Token-Sperre, Billing-/Boost-/Review-/Quota-/Slug-Races, Sanitizing, MIME und Newsletter-Rebind laut Optimierungs-Audit geschlossen. Formular-Mails sind gequeued, externe HTTP-Aufrufe gehärtet. Tests siehe `dev/optimize/optimize-14-07-2026.md`. **Bewusste Restentscheidung:** KI-Autopublishing bleibt aktiv; Prompt-Inhalte sind gehärtet, das verbleibende Modellrisiko ist dokumentiert. **Offen/bewusst:** CSP separat einführen (Livewire/Alpine-Inline-Strategie nötig); HSTS am Proxy setzen; **[EXTERN]** Secret-Rotation und Produktiv-Env-Härtung.
- [ ] **R6.3 Staging-Import-Rehearsal** — Datenimport gegen produktiven Legacy-Snapshot auf Staging (mehrfach als der offene Vollständigkeitsnachweis referenziert, P6.10/P11.3). Achtung: `mysql_businessportal`-Verbindung lokal ist nur ein ~April-Snapshot — fürs Rehearsal echten aktuellen Dump ziehen. **[EXTERN: Server/Snapshot]**
- [ ] **R6.4 Go-Live-Kette** — Go-Live-Mailing (Passwort-Reset) + API-Kunden-Mailing: Code fertig, **finale Texte + Freigabeliste [EXTERN]**; DNS-Cutover `pressekonto.de`; Legacy-System read-only während Final-Import; alte Symfony-Server abschalten. *(= P11.x)*
- [ ] **R6.5 Git-Hygiene** — Branch `fix/zeitzone-europe-berlin` (**~180 Commits vor `main`**, Stand 09.07.) pushen und nach `main` mergen; danach Release-Tag. **Vorher:** den uncommitteten Arbeitsbaum-WIP (Kontakt-Nachrichten, Validierungs-Feedback, Page-Cache, Company-Portal-Restriction, Lang-Auslagerungen; s. Stand-Block) in fokussierte Commits zerlegen. Die drei `user_filter_presets`-Migrations zusammenführen (rev-5, „Hygiene vor Go-Live") optional davor.
- [x] **R6.6 Doku-Sync** — Erledigt 03.07.: `12-NAECHSTE-SCHRITTE.md` + `08-PROGRESS.md` tragen deutliche Verweis-Banner auf den aktiven Release-Plan (Phase 8/Stripe als erledigt markiert, Test-Stand korrigiert).
---
## Bewusst NACHGELAGERT (kein Teil dieses Release-Plans)
- **Kompletter getesteter User-Panel-Flow** (User-Entscheidung 03.07.2026).
- Echte Newsletter-Sync-Implementierung (No-Op bleibt, R0.3 macht es nur ehrlich); Newsletter-Subscriber-Admin/CSV (P3.7, vertagt).
- **Newsletter-Frontend (User-Entscheidung 06.07.):** `newsletter-strip` + Newsletter-Box (release-detail) aus dem Web-Frontend entfernt. Vor Reaktivierung: DOI-Flow bauen (s. R6.1), dann Komponente `components/web/newsletter-strip.blade.php` aus der Git-Historie wiederherstellen und einbinden.
- **EN-Uebersetzung der Langform-Seiten** (veroeffentlichen, faq, hilfe, api, Rechtstexte O-C): Kern-UI ist lokalisiert (lang/{de,en}/web.php), Langform-Inhalte folgen nachgelagert; bis dahin dort kein `hreflang="en"`.
- Coupons (UI-Skeleton, D-16 vertagt), Google-Login (Socialite), Regionen-Übersichten `/region/{slug}` (301-Redirect bleibt), Blacklist-Admin-UI (Config reicht), PM-Nice-to-haves (Inline-Edit, Bulk, In-App-Notifications, Permission-Granularität).
- Legacy-Logging/-Performance-Deployments (betreffen nur das Alt-System als Überbrückung bis zum Cutover).
- Performance-Polishing aus `11-NOCH-ZU-TUN-OPTIMIERUNGEN.md` (Slow-Query-Auswertung braucht echte Live-Daten).
- Coverage-Ziel 70 % / arch()-Tests / `test --parallel` (P10.1–10.3) — Suite ist grün, Ausbau nachgelagert.
---
## Reihenfolge-Empfehlung (TL;DR)
1. **R0** sofort — kaputte Pfade, DSGVO, Seeder, Fehlerseiten (alles klein, Test-First).
2. **[EXTERN]-Punkte parallel anstoßen** — Rechtstexte (R3.1), Stripe-Live-Zugang (R1.2), Enforce-Booking-Entscheidung (R1.1), Kontaktdaten (R2.1), Mailing-Texte (R6.4). Das sind die längsten Wartezeiten.
3. **R1 + R2** — Billing scharf + Frontend funktional (Kern der Fertigstellung).
4. **R4 + R5** — Aufräumen + Deploy-Runbook, sobald R1/R2 stehen.
5. **R6** — QA, Security, Rehearsal, Cutover.

View file

@ -0,0 +1,104 @@
# Manuelle QA-Checkliste (R6.1) — Stand 10.07.2026
Quelle: `dev/migration 2026/06-FEATURES-SCOPE.md` §15, aktualisiert auf den echten
Funktionsstand. **Vorprüfung 06.07. (Claude):** Jeder Punkt wurde gegen Code und
Testsuite geprüft; „Automatisiert belegt" heißt: der Flow ist End-to-End auf
HTTP-/Service-Ebene durch grüne Tests abgedeckt — manuell bleibt die visuelle
Abnahme im Browser. **Nachprüfung 10.07. (Claude):** alle referenzierten
Beleg-Tests existieren, Suite 1156 grün; Abschnitte B–G um die seit 06.07.
gebauten Features ergänzt (KI-Kennzeichnung, PM-Tracking, Kontakt-Queue,
Tarifwechsel, eVatR, Monitoring, Börsenticker, Page-Cache); der
Frontend-Umbau ist seit 09.07. committet — die dadurch blockierten visuellen
Abnahmen (F) sind entsperrt. Testzugänge lokal: `admin@pressekonto.test` /
`password` (Admin), `test@example.com` / `password` (Customer, Seeder).
Empfohlene Reihenfolge: erst lokal durchklicken, dann dieselbe Liste beim
Staging-Rehearsal (R6.3) mit echtem Datenbestand wiederholen.
## A. Login & Konto
- [x] **Admin-Login inkl. 2FA** — Automatisiert: `Auth/AuthenticationTest`, `Auth/TwoFactorLoginTest`. Manuell: 2FA im Portal einrichten (QR-Code scannen), abmelden, Login mit TOTP-Code und einmal mit Recovery-Code. **Getestet 07.07.** — 3 Befunde gefunden und gefixt (Commits `62fbafa`, `af9f238`): 2FA-Einrichtung verlangt jetzt Code-Bestätigung (vorher keine Login-Challenge), Recovery-Codes nur noch einmalig sichtbar, falscher Challenge-Code crasht nicht mehr; E-Mail-Wechsel jetzt als Pending-Flow mit Bestätigungslink an die neue Adresse.
- [x] **Customer-Login via Magic-Link** — Automatisiert: `Auth/MagicLinkLoginTest` (TTL 15 Min., Einmalverwendung). Manuell: Link anfordern, Mail-Optik (neues CI-Layout) prüfen, Login klappt, zweiter Klick auf denselben Link wird abgewiesen. **Getestet 07.07.**
- [x] **Customer-Login via Passwort + Google** — Automatisiert: `Auth/AuthenticationTest`, `Auth/GoogleLoginTest`. Manuell: Passwort-Login **getestet 07.07.**; Google-Round-Trip geht erst live mit echter Domain (WS-7-Checkliste).
- [x] **Passwort-Reset-Flow** (kritisch fürs Go-Live-Mailing) — Automatisiert: `Auth/PasswordResetTest`, Mail-CI: `Mail/EmailBrandingTest`. Manuell: Reset anfordern → Mail → neues Passwort → Login. **Getestet 07.07.**
- [x] **Registrierung + E-Mail-Verifizierung** — Automatisiert: `Auth/RegistrationTest`, `Auth/EmailVerificationTest`. Manuell: einmal komplett durchlaufen inkl. Freischaltung. **Getestet 07.07.** (inkl. Verifizierungsmails).
## B. Admin-Backend
- [ ] **Portal-Scoping-Filter** (Presseecho-only / BP24-only / alle) — Automatisiert: `PressReleaseCompanyScopeTest`, PortalScope-Abdeckung in Admin-Tests. Manuell: Filter in PM-/Firmen-/User-Listen umschalten und Stichproben zählen.
- [ ] **PM-Workflow Admin**: Create → Draft → Publish → Mail an Autor → sichtbar im Frontend — Automatisiert: `Admin/AdminPressReleaseActionsTest`, `Admin/AdminPressReleaseSchedulingTest`, `Admin/AdminPressReleaseShowTest`. Manuell: einmal komplett, Frontend-Sichtbarkeit auf beiden Spokes prüfen.
- [ ] **Recht & Compliance-Queue** — Automatisiert: `LegalRequestTest`, `Web/ReportFormTest` (Vorgangsnummern, Eingangsbestätigung). Manuell: Meldung über /melden/rechtswidrig absenden → taucht mit M-Nummer in der Queue auf → erledigen.
- [ ] **Admin-Statuswechsel/Depublikation sichtbar** — Automatisiert: `Billing/DepublicationTest` (Tombstone 410). Manuell: depublizierte PM im Frontend aufrufen (Entfernt-Hinweis statt Inhalt).
- [ ] **Kontakt-Queue** (neu 09.07.) — Web-Kontakt-Widget (`contact-launcher`) persistiert Anfragen zusätzlich zur Brand-Mail in `contact_messages`. Automatisiert: `Web/ContactFormTest`, `Admin/ContactMessagesAdminTest`. Manuell: Anfrage übers Widget absenden → erscheint in der Admin-Queue, Status auf „erledigt" setzen.
- [ ] **Ablehnungsbegründung im Editor** (UX-Runde 07.07.) — Ablehnung mit Begründung direkt im Admin-Editor; Kunde sieht die Begründung an der PM. Automatisiert: `Admin/AdminPressReleaseShowTest` (Rejection-Banner mit letzter Begründung). Manuell: einmal ablehnen und Kundenansicht prüfen.
## C. Customer-Panel
- [ ] **PM-Workflow Customer**: Create → Draft → Submit → Admin sieht zur Prüfung — Automatisiert: `PressReleaseBookingGateTest` (Submit-Gate), `PressReleaseQuotaTest`, `PressReleaseWithdrawalWaiverTest` (Archiv-Hinweis T2neu), `PressReleaseAiDeclarationTest` (KI-Pflicht-Deklaration Art. 50 KI-VO, neu 09.07.). Manuell: einmal komplett inkl. Submit-Modal — jetzt inkl. KI-Deklaration für Text/Bilder; ohne Deklaration muss der Submit blocken. Editor-UX neu (09.07.): Kategorie in der Schreibfläche, Bild-Upload als Modal-Flow — visuell abnehmen.
- [ ] **Image-Upload**: mehrere Bilder, Preview-Flag, Rechteangaben — Automatisiert: `PressReleaseImageLicenseTest`, `SyncPressReleaseImagesTest`, Api-Upload: `Api/V1/PressReleaseImageApiTest` (16 MB, jpg/png/webp). Manuell: Drag&Drop, Sortierung, Preview-Wechsel visuell.
- [ ] **Company-Logo-Upload** — Automatisiert: `SecurityHardeningTest` (ImageService, kein SVG/XSS). Manuell: Logo hochladen, Varianten in Firmenübersicht + Newsroom prüfen.
- [ ] **Blacklist blockiert Publish** — Automatisiert: `PressReleaseBlacklistTest`. Manuell: PM mit Blacklist-Wort einreichen → automatische Ablehnung mit Begründung.
- [ ] **Buchungsseite komplett**: Abo, Einzel-PM, Credits, Unternehmer-Checkbox (T1neu) — Automatisiert: `Billing/BookingsPageTest`, `Billing/CheckoutFlowTest`, `CreditTopupTest`. Manuell: Modal-Optik, USt-Aufschlüsselung, Checkbox-Zwang; Buchungs-/Verlängerungsdatum des Abos wird angezeigt (`SubscriptionPeriodResolver`, 09.07. — braucht Stripe-Daten, also Staging).
- [ ] **Tarifwechsel im Buchungs-Modal** (neu 07.07.) — Abo-Swap mit anteiliger Verrechnung, Downgrade ohne Gutschrift erst zum Periodenende, Kontingent-Sperre beim Wechsel. Automatisiert: `Billing/CheckoutFlowTest`. Manuell auf Staging: Upgrade + Downgrade je einmal durchspielen.
- [ ] **Add-ons Boost/Veröffentlichungsnachweis** — Buchung über Credits an der PM-Seite; Add-ons-Modal statt Toast bei fehlendem Guthaben (07.07.). **Neu 13.07.:** zwei Boost-Stufen (Startseiten-Boost 12/20/35, Kategorie-Boost nur Branchenseite 6/10/18) mit Slot-Grenzen (5 Startseite, 3 je Branchenseite) — bei vollem Inventar zeigt das Panel „Derzeit ausgebucht — frei ab TT.MM." statt der Kauf-Buttons. Automatisiert: `BoostServiceTest`, `ProofPdfAndExtraPmTest`, `PressReleaseShowAddonsTest`, `Web/BoostPlacementTest`. Manuell: beide Boost-Stufen buchen (Startseiten-Boost erscheint auf Start- + Branchenseite, Kategorie-Boost nur auf der Branchenseite), Nachweis-PDF herunterladen.
- [ ] **PM-Statistiken sichtbar** (neu 10.07.) — Kachel „Aufrufe" + „Geteilt" (mit Kanal-Aufschlüsselung) auf der Kunden-PM-Seite. Automatisiert: `Web/PressReleaseTrackingTest`. Manuell: PM im Frontend aufrufen/teilen → Zahlen steigen.
## D. Billing & Stripe (Staging mit Stripe-Testmode)
- [ ] **Stripe-Checkout End-to-End inkl. Webhook** — Automatisiert: `Billing/CheckoutFlowTest`, `Billing/StripeWebhookProcessingTest`, `CreditTopupTest` (Gutschrift genau einmal). Manuell auf Staging: echter Testmode-Checkout (Karte 4242…), Webhook via `stripe listen`, Buchung erscheint im Panel.
- [ ] **USt korrekt** (vervollständigt 07.07.) — `automatic_tax` in der Checkout-Session, STR-Rechnungsspiegelung liest USt aus `total_taxes`, Profil-Rechnungsadresse ist Stripe-Rechnungsempfänger (Wallet-Daten überschreiben nicht). Automatisiert: `Billing/StripeWebhookProcessingTest`, `Billing/VatResolverTest`. Manuell auf Staging: Checkout mit DE-Adresse → 19 % ausgewiesen; Rechnung zeigt USt.
- [ ] **USt-ID-Prüfung eVatR** (neu 08.07.) — vor jeder Buchung gegen EU-VIES geprüft, Stripe-Tax-IDs abgeglichen; eigene USt-IdNr. aus `BILLING_OWN_VAT_ID` [EXTERN: Live-Wert]. Automatisiert: `Billing/VatIdValidationServiceTest`. Manuell auf Staging: Buchung mit gültiger + ungültiger USt-ID.
- [ ] **Rechnungsseite Kunde** (07.07.) — listet neue STR-/MAN-Rechnungen (STR verlinkt ins Stripe-Portal), Altsystem-Archiv nur für Bestandskunden. Automatisiert: `CustomerPortalTest`, `PanelConsolidationTest`. Manuell: Sichtprüfung mit einem Konto mit beiden Rechnungskreisen.
- [ ] **Rechnungen**: STR-Kreis (Stripe/Billing-Portal) + MAN-Kreis (`billing:generate-manual-invoices`) — Automatisiert: `Billing/InvoiceNumberGeneratorTest`, `Billing/ManualInvoiceGenerationTest`. Manuell: Rechnung im Stripe-Billing-Portal abrufbar.
- [ ] **Grandfathering**: Alt-Abo wird korrekt angezeigt/weiterberechnet, Bestandskunde sieht keinen Buchungs-Hinweis — Automatisiert: `Billing/GrandfatherLegacySubscriptionsTest`, `Billing/HasActiveBookingTest`, `Billing/LegacyBundleTest`. Manuell: mit echtem Grandfather-User nach dem Staging-Import (R6.3).
- [ ] **Rechnungsadressen der Bestandskunden** (Befund 06.07.: ohne Adresse überspringt der MAN-Lauf die Vereinbarung → Bestandsschutz läuft am Periodenende aus) — Automatisiert: `Billing/LegacyBillingAddressImportTest`. Manuell nach Staging-Import: `legacy:import-billing-addresses --dry-run` prüfen, echter Lauf, dann die im Report gelisteten Kunden (leere/unparsebare Alt-Adressen) manuell nachpflegen oder anschreiben.
- [ ] **Legacy-Rechnungen im Archiv** (Admin + Customer, PDF) — Automatisiert: `Admin/AdminLegacyInvoiceArchiveTest`, `LegacyInvoiceArchiveCommandTest`. Manuell: PDF-Download eines Altbelegs.
- [ ] **Depublikations-Checkout** (35 € netto, O6) — Kauf wird als eingelöst markiert, erzeugt Rechnung + Bestätigungs-Mail (08.07.); abgebrochener Checkout blockiert nicht (07.07.). Automatisiert: `Billing/DepublicationTest`. Manuell auf Staging: einmal komplett bis Tombstone (410) inkl. Rechnung + Mail.
## E. API v1
- [ ] **`POST /v1/press-releases` mit Sanctum-Token** — Automatisiert: `Api/V1/PressReleaseApiTest`, `PressReleaseSubmitApiTest`, `PressReleaseImageApiTest`, `CustomerDataApiTest`. Manuell: Token im Kundenkonto erzeugen (setzt aktive Buchung voraus), curl-Workflow aus der /api-Doku durchspielen.
- [ ] **Legacy-API-Übernahme** (geändert 06.07., Härtung 14.07.): Importierte Alt-Schlüssel bleiben gültig — als Bearer oder per `X-ApiKey`/`X-Api-Key`-Header und auf den `/service/…`-Pfaden. Die frühere `api_key`-Übergabe per Query/Body wird **nicht mehr für die Auth akzeptiert** (Zugangsdaten dürfen nicht in URLs/Logs landen), erhält aber weiterhin den 410-Migrationshinweis. Unbekannte Keys bekommen ebenfalls 410 mit Migrationshinweis. Automatisiert: `Api/LegacyApiCompatibilityTest`, `ApiAccessSecurityAndLoggingTest`. Manuell nach Staging-Import: `legacy:import-api-keys --dry-run` prüfen (lokal: 406 Keys), dann echter Lauf; einen echten Alt-Key als Header gegen `/service/pressreleases` testen.
- [ ] **Rate-Limit 60/min** — Automatisiert: abgedeckt. Manuell: entfällt.
- [ ] **KI-Deklaration via API** (Entscheidung 10.07.) — v1-Submit verlangt `ai_text_declaration` (Call = verbindliche Bestätigung + Archiv-Kenntnisnahme AGB § 13), Bild-Upload verlangt `ai_declaration`; Legacy-/service-Pfade defaulten auf none/original. Automatisiert: `PressReleaseSubmitApiTest`, `PressReleaseImageApiTest`, `LegacyApiCompatibilityTest`. Manuell: entfällt; **R6.4-Mailing muss Pflichtfelder + Legacy-Default erwähnen.**
## F. Frontend Spokes + Hub
- [ ] **Alter `/thumbnails/...`-Link redirectet** — **Lücke am 06.07. gefunden und geschlossen** (Scope-Doku führte den Redirect fälschlich als „ANGEPASST"): `LegacyMediaRedirectController`, Tests `Web/LegacyMediaRedirectTest`. Manuell nach Staging-Import: 2–3 echte Alt-URLs aus dem Live-Portal stichprobenartig aufrufen.
- [ ] **Legacy-.html-Redirects** (Startseite, FAQ, Newsrooms, Detail-URLs) — Automatisiert: `Web/LegacyRedirectTest`. Manuell: Stichprobe mit echten Alt-URLs nach Staging-Import.
- [ ] **Kategorieseite + Veröffentlichen-Landing visuell abnehmen** — Automatisiert: `Web/CategoryPageTest`, `Web/VeroeffentlichenPageTest`/`StaticPagesTest`. Manuell: visuelle Abnahme — **entsperrt: der Frontend-Umbau ist seit 09.07. committet** (Spokes/Hub-Umbau, `contact-launcher`).
- [ ] **PM-Tracking im Frontend** (neu 10.07.) — Aufrufe-Anzeige in der Meta-Zeile der Detailseite, Teilen-Buttons melden den Kanal (LinkedIn/X/E-Mail/Link kopieren). Automatisiert: `Web/PressReleaseTrackingTest`. Manuell: Detailseite aufrufen (Zähler steigt), einmal teilen → Ereignis in der „Geteilt"-Kachel des Kunden.
- [ ] **Öffentliches KI-Label** (neu 09.07.) — PMs mit deklariertem KI-Grad tragen das Transparenz-Label (`portal/ai-public-label`, Einreicher-Stimme). Automatisiert: `PressReleaseAiDeclarationTest`. Manuell: eine PM mit KI-Deklaration veröffentlichen und Label auf der Detailseite prüfen.
- [ ] **Melde-Zeile unter jedem Artikel (T13)** — Automatisiert: `Web/ReleaseDetailTest`/`LegalRequestTest` (Formular + Vorgangsnummer). Manuell: Links „Inhalt melden"/„Eigene Daten entfernen" an einer PM durchklicken; Hinweistexte sind final (R2.5, 10.07.).
- [ ] **Kontakt-Widget (contact-launcher)** — schwebendes Widget ersetzt das Kontakt-Modal (09.07.), speist Brand-Mail + Admin-Queue. Automatisiert: `Web/ContactFormTest`. Manuell: auf Hub + beiden Spokes öffnen und absenden.
- [ ] **Börsenticker** — `live-ticker` zeigt nur echte Daten (Yahoo Finance via `market:refresh`, alle 15 Min.) und blendet sich ohne Daten aus. Automatisiert: `Web/MarketDataTest`. Manuell auf Staging: nach erstem `market:refresh`-Lauf sichtbar; ohne Netz/Fehler kein leerer Ticker.
- [ ] **Page-Data-Cache: Aktualität** (09.07.) — Home/Kategorie cachen 5 Min. ohne explizite Invalidierung; eine frisch publizierte PM darf bis zu 5 Min. brauchen, bis sie auf Home/Kategorie erscheint (Detailseite sofort). Automatisiert: `Web/PageDataCacheTest`. Manuell: Verhalten einmal bewusst beobachten — falls störend, Invalidierung beim Publish nachrüsten (kein Blocker).
- [ ] **Rechtsseiten auf allen drei Domains** (Impressum, AGB, Datenschutz, Richtlinien, Melden, Kündigung) — Automatisiert: `Web/StaticPagesTest`, `Web/HubStaticPageShellTest`, `Legal/LegalDocumentTest`. Manuell: Sichtprüfung Hub + beide Spokes, Links im Footer.
- [ ] **Sitemap/robots je Host** — Automatisiert: `Web/SitemapRobotsTest`. Manuell: /sitemap.xml und /robots.txt auf allen drei Hosts aufrufen.
## G. Betrieb (Staging/Live, Ops)
- [ ] **Scheduler greift** — Vorgeprüft 06.07., erneut 10.07.: `schedule:list` zeigt 6 Tasks (magic-links:purge, press-releases:purge-drafts, publish-scheduled, billing:generate-manual-invoices, reset-monthly-quota, market:refresh). Manuell auf Staging: Cron-Eintrag + `php artisan schedule:run` + `queue:work` laufen (Runbook WS-7).
- [ ] **Monitoring-Alerts (R5.3, neu 09.07.)** — alle 6 Scheduler-Tasks mit `->onFailure()`, Queue-`failing`-Listener; Alerts gehen als `Log::critical` + Mail an `MONITORING_ALERT_EMAIL` (Fallback `LEGAL_CONTACT_EMAIL`), Schalter `MONITORING_ALERTS_ENABLED`. Automatisiert: `OperationalAlertTest`. Manuell auf Staging: `MONITORING_ALERT_EMAIL` setzen, einen Task absichtlich fehlschlagen lassen (z. B. DB kurz weg) → Mail kommt; `LOG_STACK=daily` prüfen (Rotation 14 Tage).
- [ ] **KI-Queue**: PM einreichen → Worker klassifiziert, kein Stau in `jobs`/`failed_jobs` — Automatisiert: `PressReleaseClassificationJobTest`. Manuell: mit echtem `OPENAI_API_KEY` auf Staging.
- [ ] **Mail-Zustellung real** (nicht `log`), Absender/SPF/DKIM — nur Staging/Live [EXTERN].
- [ ] **Umami**: `UMAMI_ENABLED=true` erst beim Go-Live; danach Snippet je Domain im Quelltext prüfen (`Web/UmamiSnippetTest` sichert die IDs).
## Nicht Teil von R6.1
- **Import-Rehearsal** gegen produktiven Snapshot → eigener Punkt **R6.3** [EXTERN: Server/Snapshot].
- **Newsletter Double-Opt-In End-to-End**: bewusst **vertagt** (Release-Plan „Bewusst NICHT im Plan": echte Newsletter-Sync bleibt No-Op; R0.3 macht das Binding nur ehrlich). Die API v1 legt Subscriptions unbestätigt an (`is_confirmed=false` + Token) — ein Bestätigungs-/Versandweg existiert noch nicht. Vor Aktivierung des Newsletters: DOI-Flow bauen. Kein öffentliches Anmeldeformular verlinkt → kein Launch-Blocker.
- ~~**Frontend-Redesign-Abnahme**~~ — Umbau seit 09.07. committet; die visuelle Abnahme ist damit regulärer Teil von Abschnitt F.
## Befunde der Vorprüfung 06.07.
1. **/thumbnails-Redirect fehlte** (Scope-Doku behauptete „ANGEPASST") → gebaut + getestet (siehe F).
2. **Newsletter-DOI unvollständig** → bewusst vertagt, dokumentiert (siehe oben).
3. Alles Übrige aus §15 ist durch die grüne Suite (957 Tests) auf Funktionsebene belegt; offen ist die **visuelle bzw. operative Abnahme** im Browser und auf Staging.
## Nachprüfung 10.07.
1. Alle referenzierten Beleg-Tests existieren; Suite **1156 grün** (5065 Assertions).
2. B–G um die Features vom 07.–10.07. ergänzt: Kontakt-Queue + Widget, Ablehnungsbegründung, KI-Kennzeichnung (Submit-Pflicht, API, öffentliches Label), Tarifwechsel/Downgrade, Add-ons, USt/`automatic_tax`, eVatR, Rechnungsseite STR/MAN, Depublikations-Kauf komplett, PM-Tracking (Aufrufe + Shares), Börsenticker, Monitoring-Alerts (R5.3), Buchungs-/Verlängerungsdatum.
3. Frontend-Umbau-Vorbehalt in F aufgehoben (Umbau committet 09.07.) — Kategorieseite/Veröffentlichen-Landing sind zur Abnahme frei.
4. Einziger neuer Befund: **Page-Data-Cache ohne Publish-Invalidierung** — Home/Kategorie können einer frischen PM bis zu 5 Min. hinterherhängen (TTL 300 s). Als bewusster Tradeoff dokumentiert (siehe F); bei Bedarf Invalidierung beim Publish nachrüsten, kein Launch-Blocker.