Test-Übersicht — Medidentas (lebend)
Welche automatischen Tests sichern die Anwendung ab? Sie dienen True North #3 (vollständige & rechtssichere Dokumentation): jeder Test sichert, dass das dokumentierte Verhalten reproduzierbar stimmt und nicht unbemerkt driftet. Diese Übersicht ist lebend (ohne Versions-Stempel, MED-D-363).
1259 Tests / 112 Dateien (Server, Vitest) · 149 Tests / 38 Dateien (E2E, Playwright: 116 gemockt + 33 Full-Stack) — beim Bauen gemessen (scripts/testzahlen_messen.py), nicht von Hand gepflegt.
Erzeugte Datei (MED-D-701):
docs/betrieb/Test-Uebersicht.mdwird nach dem Merge austest.d/gebaut (fragmente.json→scripts/fragmente_bauen.py; die Zahlenzeile stempeltscripts/testzahlen_messen.py). Je PR nur ein Fragmenttest.d/<YYYY-MM-DD>-<branch-slug>.mdanlegen — diese Datei nie von Hand ändern. Grund: die eine Kopfzeile war der letzte Hot-File, parallele PRs haben sich Einträge überschrieben (Issue #701).
Was zuletzt dazukam
-
#840 (Rate-Limiting): neue Testdatei
server/src/api/token-rate-limit.test.ts(Token-Grenze, IP-Grenze, Audit nur beim ersten Treffer, nicht erfasste Routen). -
#840 (Nacharbeit):
server/src/api/token-rate-limit.test.tsprüft jetzt alle fünf Routen-Gruppen je Token. -
MED-D-703 (Ablösung
onboarding-public): keine neuen Testdateien;server/src/api/portal.test.ts(4 neue Fälle: Katalog-Aufgabe statt Einladung, Rückfall bei nicht freigegebenem Text, Bestandskunde bleibt alt, Link-Erneuerung) undclient/e2e/portal.spec.ts(Katalog-Aufgabe in der Portal-Liste). -
MED-D-660-3 (netto): keine neuen Testdateien;
client/e2e/cockpit.spec.ts,client/e2e/stundensatz-kunde.spec.ts,client/e2e/verwaltung.spec.tsundclient/e2e-fullstack/stundensatz.spec.tsprüfen jetzt die Beschriftung "netto". -
MED-D-831 (Auswertungs-Stand): neue
server/src/domain/auswertung.test.ts+8 (Kern inkl. Oder-Gruppe und Sichtbarkeit; Kennzahl-Bedarf gegen die echte Rechnung; Katalog-Bedarf eigene Unterlage, Platzhalter, Vorausfüllen über Attribute); neueclient/e2e/auswertung-stand.spec.ts+2 (Katalog live, Dentmarking-Fragebogen mit Oder-Gruppe). Erweitert:server/src/api/dentmarking.test.ts,server/src/api/katalog-service.test.ts,client/e2e/berater-kataloge.spec.ts,client/e2e-fullstack/katalog-flow.spec.ts.
MED-D-832 (Mandats-Unterlagen-Matrix): neue Testdatei unterlagen-vorlage-umbenennen.test.ts (2 Tests: Umbenennen behält Produkt/Reihenfolge; Admin-only, 400/404/409); verwaltung.spec.ts Matrix-Test ersetzt den Listen-Test (Ankreuzen → POST, Abwählen → DELETE, Umbenennen → PUT) und +1 (ein Speichern-Knopf für Einstellungen).
MED-D-739 (Link-Gültigkeit): ausfuellformular.test.ts +4 (14 Tage, Altbestand fail-closed, Link erneuern, Korrektur-Frist) und 3 Erwartungen auf „nur Status nach Abschluss“ angepasst; dokument.test.ts +1 (Upload-Link ≤ 14 Tage, Erneuern widerruft); r2.test.ts +1 (Marker ohne Datum/Widerruf); onboarding-formular.test.ts +1 und dentmarking.test.ts +1 (nur Status bei Ablauf); TTL-Default 14 in einstellungen.test.ts/dentmarking.test.ts; formular.spec.ts +1 (abgelaufen), berater-kataloge.spec.ts +1 (Link erneuern).
MED-D-703 / N-5 (Katalog-Flow Full-Stack): neu client/e2e-fullstack/katalog-flow.spec.ts +3 (Kundendaten-Katalog bis Stammdaten-Übernahme · Textbaustein-Katalog mit Widerrufsbelehrung · Link-Erneuern).
MED-D-413 (Kunden-Gruppierung): neue domain/mandant-gruppe.test.ts +1 (Gruppenname prüfen), neue api/mandant-gruppe.test.ts +3 (anlegen mit zweitem Kunden, Querverweise, beitreten, umbenennen, austreten, Audit PII-arm; letzte Mitgliedschaft löst Gruppe auf; Rechte R7: fremde Mitglieder nur als Anzahl, fremde Kunden/Gruppen 404, Eingabe 400), neue e2e/kunden-gruppe.spec.ts +1 (Akte: Querverweise, neue Gruppe mit zweitem Kunden), neue e2e-fullstack/kunden-gruppe.spec.ts +1 (echter Worker + D1 v82: Gruppe anlegen → Querverweis beim zweiten Kunden); Mock-Default /gruppen in e2e/support/mock-api.ts.
MED-D-773 (Audit-Katalog): neue scripts/test_audit_katalog_bauen.py +1 (Literale, Bedingungen, Marker-Kommentar, Lesezugriffe, variable Entität, keine Nicht-Audit-Werte); Doku-Check §10 warnt bei veraltetem Katalog. MED-D-775: Doku-Site-Build bricht bei toten Links ab (onBrokenLinks: 'throw').
MED-D-425 (Warum-Texte): neue domain/warum-texte.test.ts +2 (jedes Kunden-Feld hat einen Text, keine fremden Schlüssel, max. 160 Zeichen; sichtbar nur bei Freigabe genau dieser Fassung), neue api/warum-texte.test.ts +2 (Entwürfe für Mitarbeiter, Freigabe nur Admin, Kunde sieht Texte erst danach, Audit; geänderte Fassung gilt als nicht freigegeben), e2e/katalog.spec.ts +1 (Aufklapper nur mit Text), e2e/verwaltung.spec.ts +1 (Entwurf ansehen, freigeben); e2e/kunden-gruppe.spec.ts + Full-Stack auf den App-Bestätigungsdialog umgestellt; Mock-Default /warum-texte.
MED-D-811 (Gruppenart/Archiv/Unterlagen): api/mandant-gruppe.test.ts +2 (Vorlage je Gruppenart + Ergänzung → Vollständigkeit, Zuordnung beim Hochladen, Gruppenart wechseln, nur Ergänzungen entfernbar; archivierte Gruppe lesbar, Änderungen 409, nicht in der Auswahl, Reaktivieren); e2e/kunden-gruppe.spec.ts +1 (Stand der Unterlagen, Zuordnung, Archivieren), e2e/verwaltung.spec.ts +1 (Gruppen-Unterlagen je Gruppenart), e2e-fullstack/kunden-gruppe.spec.ts erweitert (Archivieren bleibt nach Neuladen); Mock-Default /gruppen-unterlagen-vorlagen.
MED-D-801 (Gruppenordner): api/mandant-gruppe.test.ts +2 (Hochladen, Liste und Abruf für alle Mitglieder, Pfad im Gruppenordner, Zugriff protokolliert, Audit ohne Dateiname; Nicht-Mitglied 404, Typ 400, letztes Mitglied mit Dokumenten 409), domain/zugriff.test.ts (Whitelist um gruppen_dokument), e2e/kunden-gruppe.spec.ts +1 (Liste, Hochladen, Link), e2e-fullstack/kunden-gruppe.spec.ts erweitert (echte Ablage: beim einen Kunden ablegen, beim anderen öffnen).
MED-D-703 / FK-4: onboarding-kataloge.test.ts +4 (Beispieltexte gekennzeichnet, pma unbelegt; je Vollmacht-Katalog: ohne Freigabe nicht bestätigbar, mit Freigabe bestätigbar, keine Sichtung) + Registry/Entwurfsdateien angepasst; textbaustein.test.ts +1 Service (Entwurf bearbeiten, freigegeben unveränderlich) und HTTP-Erweiterung (PUT: 403/400/200/409/404); verwaltung.spec.ts +1 (Entwurf bearbeiten); katalog-flow.spec.ts (Full-Stack) +1 (FK-4: Beispieltext → bearbeiten → freigeben → Kunde bestätigt → Unterschrift); Hydrate-Test auf sechs Entwürfe.
MED-D-812 („Akte" → „Kunde", „Aktenreife" → „Vollständigkeit"): domain/aktenreife.test.ts umbenannt in domain/kunden-vollstaendigkeit.test.ts (Zahl unverändert); api/app.test.ts ruft /api/vollstaendigkeit auf; E2E-Titel „Akte:" → „Kunden-Detail:" (Zahl unverändert).
MED-D-804/805 (§ 23a und Unfallversicherung): domain/dexman-ag-kosten.test.ts +6 netto (§ 23a Abs. 3 im März, Dezember mit ausgeschöpfter KV-Grenze, Märzklausel mit Vorjahr, Sammeleintrag, Unfallversicherung mit Jahresbeitrag, ohne Beitrag/pauschal, Freiberufler; der bisherige Fall "sonstiger Bezug ohne Arbeitgeberanteile" ist ersetzt), domain/dexman-kern.test.ts +1 (Jahresbeitrag Unfallversicherung), e2e/dexman.spec.ts +1 (Praxis-Parameter sendet den Jahresbeitrag).
MED-D-779 (apoBank-Stammnummer + Rückschreiben): neu katalog-uebernahme.test.ts (5: Regeln je Katalog, Katalog v2 optional, offen → nicht übernehmbar, Diff/Auswahl/Versionierung/Audit PII-arm/Idempotenz, fremder Kunde); onboarding-kataloge.test.ts (fill_6 befüllt, Mapping v2); Snapshot-Fixtures erweitert; berater-kataloge.spec.ts +1 (Übernahme-Diff und Auswahl).
MED-D-777 (Widerrufsbelehrung per Mail): neu katalog-belehrung.test.ts (7: nicht freigegeben, keine E-Mail, PDF + Link + Fassung festgehalten + idempotent/erneut, Versandfehler, Audit PII-arm, Katalog ohne Belehrung, manipulierte Fassung); cloudflare-email.test.ts +1 (PDF-Anhang als Base64); onboarding-kataloge.test.ts angepasst (Belehrung vor Bestätigung); berater-kataloge.spec.ts +1 (Hinweis, Senden, erneut senden).
MED-D-660 (Stundensätze): neue domain/stundensatz.test.ts +4 (Satzprüfung; Beratersatz je Eintrag; Kundensatz für alle; Minuten ohne Satz + Rundung), neue api/stundensatz.test.ts +3 (Beratersatz in Benutzerliste und Cockpit-Betrag; Kundensatz setzen/entfernen; Rechte 403, Eingabe 400, unbekannter Benutzer 404, Audit PII-arm), domain/rechte.test.ts +1 (stundensatz_pflegen admin/backoffice), e2e/cockpit.spec.ts +1 (Zeit, Betrag, Minuten ohne Satz), e2e/verwaltung.spec.ts +1 (Satz anzeigen, übernehmen, in Cent senden), neue e2e/stundensatz-kunde.spec.ts +1 (Akte: offener Betrag, abweichender Satz), neue e2e-fullstack/stundensatz.spec.ts +1 (echter Worker + D1 v81: Berater- und Kundensatz → Betrag in Akte und Cockpit); Mock-Default /stundensatz in e2e/support/mock-api.ts.
MED-D-687 (Praxis-Pflicht): beratungsauftrag.test.ts +1 (Praxisfinanzierung ohne Praxis abgelehnt, mit Praxis mehrfach anlegbar, Existenzgründung ohne Praxis erlaubt); Katalog-Test erwartet jetzt fünf Pflicht-Produkte; app.test.ts Cockpit-Test legt statt Praxisfinanzierung eine Kapitalanlage ohne Praxis an.
MED-D-749 (SPA-Fallback): serving.test.ts +1 (Browser-Validatoren If-None-Match/If-Modified-Since/Range erzeugen im Fallback kein 304 mehr — immer 200 mit Inhalt, übrige Header bleiben); in e2e-fullstack/beratungsvorschlag.spec.ts und dexman-kachel.spec.ts entfällt die Test-Umgehung für #749 (zweites Laden derselben Route läuft jetzt direkt); beratungsvorschlag.spec.ts öffnet das Dexman über die Kachel statt über die Praxen-Liste (gleicher Button-Name an zwei Stellen → sporadischer Fehler).
MED-D-774 (Doku-Audit): keine neuen Testdateien — Doku-Änderungen; geprüft mit scripts/check-doc-consistency.sh (Warnung „Audit veraltet" entfällt) und dem Build der Doku-Site (docs-site, Glossar neu erzeugt).
MED-D-754 (Start-Bundle): keine neuen Testdateien — die Routen laden jetzt lazy, abgedeckt durch die bestehenden E2E-Suiten (gemockt und Full-Stack), die jede Seite über den echten Build ansteuern und unverändert grün laufen.
MED-D-764 (Dexman-Kachel: Umsatz vs. Plan): domain/dexman-kern.test.ts +6 (ohne Ist kein Vergleich; Beispielpraxis Aug 56.000 gegen 60.000 = „Leicht unter Plan"; Teilmeldung zählt nur den Plan der Meldenden; Schwellen −5 %/−15 % inkl. Grenzfälle; ohne Plan „keine Daten"; jüngster Monat gewinnt), api/dexman-kachel.test.ts +1 (Umsatz vs. Plan: ohne Ist keine Zeile, mit Ist Text/Ampel, Beratungsmodus rundet die Beträge); e2e-fullstack/dexman-kachel.spec.ts erweitert (Ist-Meldung → Zeile „Umsatz vs. Plan: Leicht unter Plan", Datenstand, Wirkung der Empfehlung; Zeile folgt dem Beratungsmodus).
MED-D-710 (Dexman Kassen-/Privatleistungen, Bezugsbasis, Verzug): domain/dexman-zufluss.test.ts +13 (neu; Zahlungseingang von Hand nachgerechnet: Leistungs-/Zuflussmonat, Kassenanteil je Monat, Verzug 0 bis 12 Monate, Rückwärtskompatibilität zuflussbasiert, Auswahl überstimmt die Praxis, Umsatzbeteiligung folgt der Basis, Plausibilitäts-Hinweis), api/dexman-leistung.test.ts +9 (neu; Erfassen mit dem Ist-Monat und allein, Korrektur, Prüfung beider Beträge, künftige Monate, Sperre durch die Festschreibung, Audit ohne Beträge, HTTP inkl. Rechte je Kunde), e2e/dexman.spec.ts +4 (Radio-Buttons in Klassisch und Kompass ohne Speichern, Erfassen mit Hinweis bei nur einem Betrag, Vorbelegung und gesperrte Monate), e2e-fullstack/dexman-leistung.spec.ts +1 (neu; echter Worker + D1: Mix persistiert, Ablehnung nur eines Betrags, Radio-Buttons speichern nichts).
MED-D-790 (Portal-Meldung Beteiligung + Kasse/Privat): api/dexman-monatsmeldung-zusatz.test.ts +9 (neu; Formular nur für Personen mit Beteiligung, Verhalten ohne Zusatzfelder, Einreichen → Sichten schreibt Fakten mit Quelle portal, Ablehnen/Korrektur je Feld, Prüfung der Beträge, Festschreibung sperrt die Übernahme, unbekannte Felder, Cent-Beträge, Portal-Route), e2e/portal.spec.ts +1 (freiwillige Felder melden, Hinweis bei nur einem Betrag), e2e/dexman.spec.ts +1 (Berater sichtet Beteiligung und Leistung einzeln).
MED-D-709 Slice 5 (ausgezahlte Umsatzbeteiligung): api/dexman-beteiligung.test.ts +7 (neu, davon 1 HTTP; Erfassen und Lesen im Stand, Korrektur, Wirkung auf die Rechnung, Abweisung von Inhaber/unbekannter/nicht angestellter Person und ungültigen Beträgen, künftige und festgeschriebene Monate, Audit ohne Beträge) und Rechte je Kunde über HTTP, domain/dexman-ag-kosten.test.ts +6 (Planung, Ersatz im Zuflussmonat, 0, sonstiger Bezug ohne Arbeitgeberanteile, Addition, Pauschale), e2e/dexman.spec.ts +2 (Erfassen beider Lohnarten, Vorbelegung und gesperrter Monat), e2e-fullstack/dexman-beteiligung.spec.ts +1 (neu; echter Worker + D1).
MED-D-709 Slice 4 (Arbeitgeberkosten in der Rechnung): domain/dexman-ag-kosten.test.ts +14 (neu; von Hand nachgerechnet: Pauschale-Rückfall, regulär 21,3 %, eigener Zusatzbeitrag mit U1, PKV-Zuschuss, Sachsen, Minijob, Beitragsbemessungsgrenzen, Beteiligung als laufender Lohn, Draft, Lücke → Pauschale, Freiberufler, Sammeleintrag mit Anzahl Personen), domain/dexman-kern.test.ts +1 (Anzahl Personen), api/dexman.test.ts +1 (stand() liefert Parameter) und Beispielpraxis-Erwartung angepasst, db/migrate-v72.test.ts prüft v72 + v80, e2e/dexman.spec.ts +1 (Herkunft der Arbeitgeberkosten im Reiter Personal).
MED-D-709 Slice 3 (AG-Kosten-Felder): domain/dexman-kern.test.ts +4 (Vorgaben für fehlende Felder, gültige Werte und PKV-Zuschuss nur bei privat, Abweisung unzulässiger Werte mit sprechendem Fehler, pvSachsen), e2e/dexman.spec.ts +1 (Formular: Beschäftigung, PKV-Zuschuss, Umlage U1 werden mit der neuen Version gesendet); e2e-fullstack/dexman-flow.spec.ts angepasst (erwartet die normalisierten Vorgaben).
MED-D-735 (Dexman-Empfehlung → Vorschlag für die Beratungsdoku): neue api/beratungsvorschlag.test.ts +15 (Vorschlagen legt den Vorschlag am Dexman-Mandat an, idempotent, schreibt nichts ins Protokoll; ohne/mit abgeschlossenem Mandat 409, unbekannte Empfehlung 404; Übernehmen hängt den vom Berater geänderten Text als Absatz an; ohne eigenen Text die vorgeschlagene Fassung, ein geprüfter Entwurf wird zurückgesetzt; Übernehmen abgewiesen bei fehlender Doku/freigegebener Doku/Dokumentationsverzicht — Vorschlag bleibt offen, Protokoll unverändert; leerer/zu langer Text 400; Verwerfen verlangt Grund, ist endgültig und lässt das Protokoll unberührt; Umgesetzt nur für Übernommenes; nach der Übernahme „nicht umgesetzt" (Grund Pflicht, Absatz bleibt, danach endgültig); Gleichzeitigkeit: doppeltes Übernehmen fügt den Absatz genau einmal ein, Verwerfen und Übernehmen widersprechen sich nicht, doppeltes Vorschlagen legt genau einen an; scheitert das Protokoll, wird die Beanspruchung zurückgenommen; Audit PII-arm; HTTP mit Rechten je Kunde — fremder Berater 404), e2e-fullstack/beratungsvorschlag.spec.ts +1 (gegen den echten Worker: Empfehlung vorschlagen — idempotent, Protokoll bleibt leer —, im Mandat mit eigener Formulierung übernehmen, umgesetzt melden, weiteren Vorschlag nur mit Grund verwerfen, einen übernommenen als „nicht umgesetzt" melden); Mock-Default /beratungsvorschlaege für das Mandat-Overlay.
MED-D-737 (Dexman-Kachel in der Kundenakte): neue api/dexman-kachel.test.ts +8 (ohne laufendes Dexman-Mandat keine Kachel — abgeschlossenes/anderes Produkt zählen nicht; eine Kachel je Praxis, nach Name sortiert; leere Planung ehrlich „keine Daten"; Beispielpraxis: Kernaussage, Ampel und wichtigste Empfehlung stimmen mit der Lage überein, Datenstand = letzter Ist-Monat; zählt nur offene Vorschläge; Anzeige-Raster rundet nur die Texte, nie die Lage; Verdichtung ohne Lesezugriff-Event und ohne Speicherung; HTTP mit Scope je Kunde — fremder Berater 404, nur die Raster 1/100 zulässig), e2e-fullstack/dexman-kachel.spec.ts +1 (gegen den echten Worker: ohne Mandat keine Kachel; mit Mandat steht die Kernaussage ohne Klick da, Ampel mit drei Bereichen, Empfehlung, Datenstand, offener Vorschlag; „Dexman öffnen →"; Beratungsmodus im Dexman einschalten → in der Akte sind die Beträge auf 100 € gerundet, „ausschalten" macht sie wieder exakt); Mock-Default /dexman-kacheln.
MED-D-709 (Dexman-Arbeitgeberkosten, Slice 1+2): domain/dexman-lnk.test.ts +22 (neu; "gültig ab" und Draft-Ableitung, reguläre Beschäftigung von Hand nachgerechnet inkl. Beitragsbemessungsgrenzen, Sachsen, privat versichert, Unfallversicherung, Minijob mit Pauschalen und Umlagen, Draft-Grenze 633 € und Draft-Bemessungsgrenzen 2027, Übergangsbereich-Hinweis, unbelegte/fehlende Parameter, sonstiger Bezug als Hinweis, Jahres- und Teiljahressumme, Korrektur bei gleichem "gültig ab", Eingabeprüfung), api/lnk-parameter.test.ts +6 (neu; Lesen alle Rollen, Draft/gültig bis abgeleitet, Berater 403, Admin legt an + Audit PII-arm, Korrektur append-only, ungültige Eingaben 400), db/migrate-v72.test.ts +2 (neu; Seed und Idempotenz gegen echtes SQLite, Seed = Kern-Startwerte), e2e/verwaltung.spec.ts +2 (Lohnnebenkosten-Karte: Tabelle mit Entwurf/"nicht belegt", Anlegen sendet Prozent als Dezimalzahl; ohne Pflege-Recht kein Formular), e2e-fullstack/lnk-parameter.spec.ts +1 (neu; echter Worker + D1: Seed per Migration, Entwurf, Korrektur ersetzt, Verwaltung zeigt es).
Katalog-Zuordnung je Angebot + Berater-Antworten (MED-D-703, N-2/N-3): client/e2e/verwaltung.spec.ts +1 (Verwaltung → Fragenkataloge je Beratungsangebot: zuordnen, Standardsatz, entfernen), client/e2e/berater-kataloge.spec.ts +2 (Akte lädt die Kataloge für Onboarding plus gebuchte Produkte, je Produkt einmal; Berater-Antworten: nur Felder mit Rolle Berater, bedingte Sichtbarkeit, Speichern sendet nur die Beratungsangaben); Mock-Defaults /api/angebot-kataloge.
Scan des unterschriebenen Originals (MED-D-703, N-4): api/katalog-fremdformular.test.ts +2 (Scan wird als Dokument ‚… — unterschriebenes Original‘ am Kunden abgelegt und die Dokument-ID im Audit geführt; ohne Ausdruck 409 und unzulässiger Dateityp 400 legen nichts ab und lassen den Katalog bestätigt; ohne Scan funktioniert die Meldung weiter), client/e2e/berater-kataloge.spec.ts +1 (Scan wählen → multipart mit Dateiname).
FK-3 apoBank + Fremdformular-Vorlagen (MED-D-703, 2026-10-16): api/onboarding-kataloge.test.ts +1 (Vordruck-Nachbau mit Feldern fill_6/7/8/9/11/19/29: Name „Nachname, Vorname“ und Anschrift aus den Antworten, Vertriebspartner-Vorbelegung fill_19 und Stammnummer/Datum bleiben unberührt) und Registry-Liste um den vierten Katalog; api/katalog-fremdformular.test.ts +1 (Übersicht der Vorlagen: fehlt/vorhanden, Hash, Feldzahl, Mapping-Felder, die im PDF fehlen); client/e2e/verwaltung.spec.ts +1 (Panel Fremdformular-Vorlagen: Status, Hochladen als application/pdf, Mapping-Hinweis).
Entwurfstexte per Hydrate-Skript und ‚Aus Datei laden‘ (MED-D-703, S9): client/e2e/verwaltung.spec.ts +1 (Datei hochladen → Fassung 1; vorhandener Baustein übersprungen; Datei ohne Kopf gemeldet); api/onboarding-kataloge.test.ts +1 (liest scripts/textbausteine/*.md: Platzhalter passen, nach Freigabe bestätigbar, Wortlaut nicht im Server-Code) + scripts/textbausteine-hydrate.test.mjs 4 (node:test).
Standard-Onboarding-Kataloge (MED-D-703, S9 Teil 1): neue api/onboarding-kataloge.test.ts +4 (Registry konsistent; Kundendaten: Kinder nur Vorname/Geburtsmonat, Praxisfelder bedingt; Textbaustein-Kataloge FK-2/FK-2b: Platzhalter passen zu den Feldern, ohne freigegebene Fassung nicht bestätigbar, mit Freigabe bestätigbar).
Akte: Nachweise + Kontaktsteuerberater (MED-D-703, BU-1): neue client/e2e/berater-nachweise.spec.ts +3 (fehlende Nachweise anfordern; Ablaufdatum setzen; Steuerberater speichern, Übernahme nur angeboten); Mock-Defaults /nachweise, /steuerberater.
Berater-Oberfläche Fragenkataloge (MED-D-703): neue client/e2e/berater-kataloge.spec.ts +3 (Akte-Panel: bereitstellen + Link; Sichtung je Abschnitt, Freigabe schaltet die Unterschrift frei; Rückgabe braucht Grund und sendet ihn je Angabe); support/mock-api.ts um Defaults /kataloge und /sichtung ergänzt.
MED-D-703 (Fragenkatalog S8, Sichtung je Abschnitt): neue api/katalog-sichtung.test.ts +7 (Übersicht je Abschnitt ohne Berater-Felder/leere Abschnitte; Gate: ohne Freigabe weder Unterschrift noch Ausdruck; Freigabe idempotent und PII-arm auditiert; katalogübergreifend ‚alle freigeben‘; Ablehnung braucht Grund je Feld, öffnet den Katalog, Gründe beim Kunden, unveränderte Abschnitte behalten die Freigabe; Freigabe veraltet bei geänderten Antworten; nur bestätigte Kataloge); HTTP-Tests der Unterschrift rufen jetzt die Sichtung auf; client/e2e/katalog.spec.ts +1 (Rückfrage-Banner).
MED-D-703 (Fragenkatalog S7, Fremdformular + Ausdruck-Fallback): neue api/katalog-fremdformular.test.ts +10 (Registry-Prüfung pdfMapping; ohne Vorlage nicht bestätigbar; Vorlagen-Hash bei der Bestätigung eingefroren, PDF aus Antworten vor Stammdaten befüllt, ein Dokument/ein Signatur-Vorgang; geänderte Vorlage → Ablehnung; Ausdruck mit Wiedervorlage „Original erwartet“ einmalig; Original erhalten nur nach Ausdruck, ohne Signatur-Vorgang signiert; Bestätigungsblatt mit Unterschriftszeile; HTTP: Vorlage hochladen/ablehnen, Ausdruck + Original erhalten); client/e2e/katalog.spec.ts +1 (Hinweis ‚Formular noch nicht hinterlegt‘).
MED-D-703 (Fragenkatalog S6, Textbausteine + Kontaktsteuerberater): neue domain/textbaustein.test.ts +6 (Platzhalter, formale Prüfung, Rendern, keine Platzhalter-Injektion über Werte, Hash, Baustein-Absätze im PDF), neue api/textbaustein.test.ts +9 (Verwaltung: Validierung, append-only Fassungen, Freigabe einmalig und idempotent, Platzhalter ohne Feld blockieren die Freigabe, Übersicht ohne Volltexte; Katalog: Vorschau mit Stammdaten-Namen, Bestätigung nur mit freigegebener Fassung, fehlender Platzhalterwert benannt, Fassung bei der Bestätigung eingefroren, Eingriff in den Text → baustein_veraendert; HTTP: nur Admin, 400/404/409), neue api/steuerberater.test.ts +8 (privat und je Praxis getrennt, Übernahme nur als Angebot/auf Wunsch, Überschreiben nur ausdrücklich, Mandanten-Trennung, Audit ohne Klartext, Versionierung, Praxis-Vorbelegung nur bei genau einer Praxis, HTTP), e2e/verwaltung.spec.ts +3 (Textbausteine: Liste/Text/Freigabe, unbekannte Platzhalter, Anlegen), e2e/katalog.spec.ts +1 (ausgefüllter Text vor der Bestätigung, Hinweis ohne Freigabe); Standard-Mock /api/textbausteine. Davor MED-D-703 (Fragenkatalog S5, Nachweise): neue domain/nachweis.test.ts +6 (Monat-Vorher mit Monatsende/Schaltjahr/Jahreswechsel, Status fehlt/angefordert/Ablauf fehlt/vorhanden/läuft ab/abgelaufen, Typ ohne Ablauf, Zuordnung markierter und erkennbarer Altdokumente, Zusammenführung mehrerer Kataloge), neue api/nachweis.test.ts +6 (ein Dokument je Typ und Kunde, Altdokument ‚Personalausweis‘ wird übernommen, Aufgabe genau 1 Monat vor Ablauf, neues Datum verschiebt statt zu verdoppeln, Entfernen schließt, andere Dokument-Fristen unverändert, Mandanten-Trennung, Katalog-Integration katalogübergreifend inkl. ‚läuft ab‘/‚abgelaufen‘, HTTP-Ablauf mit Datums-Validierung), e2e/katalog.spec.ts Nachweis-Block ergänzt (ohne Upload-Feld). Davor MED-D-703 (Fragenkatalog S4, Unterschrift je Katalog): neue domain/katalog-dokument.test.ts +3 (Bestätigungsblatt aus derselben Anzeige-Logik wie die Browser-Zusammenfassung: Auswahl-Beschriftung, Ja/Nein, abgeleiteter Wert, Gruppeneinträge, Markierung, ausgeblendete Felder fehlen; Bezeichnung/Dateiname ohne PII), domain/fragenkatalog.test.ts +2 (mehrere Fassungen: neueste/exakte Version, Duplikat nur bei gleicher Version), neue api/katalog-unterschrift.test.ts +6 (ein Dokument/ein Vorgang je Katalog mit Katalog-Niveau und PDF in der Ablage, Rückweg → signiert idempotent, gescheiterter Hook wird beim nächsten Abgleich nachgeholt, Schutzriegel: offen/Beleg manipuliert/Katalog ohne Versionserhöhung geändert/bereits aktiv, frühere Fassung nur mit Registry, HTTP-Ablauf bis signiert). Davor MED-D-703 (Katalog-Link begrenzt gültig, RISK-39): api/katalog-service.test.ts +7 (14 Tage absolut, Aktivität verlängert nicht, Berater unabhängig vom Kunden-Link, Altbestand ohne Ablauf = abgelaufen, Link erneuern rotiert den Token und hält Antworten, nur für offene Kataloge, nach Bestätigung nur Status, Wiedereröffnen setzt die Frist neu), api/katalog-routes.test.ts +1 (HTTP: 410 auf allen öffentlichen Routen ohne Daten, Erneuern, alter Link 404, Fremd-Kunde 404), e2e/katalog.spec.ts +2 (abgelaufener Link; Ablauf während der Bearbeitung). Davor MED-D-703 (Fragenkatalog S3): api/katalog-service.test.ts +6 (Delta: Stammdaten-Vorbelegung mit Zuordnung der Auswahl per Beschriftung, Antwort aus einem anderen Katalog schlägt Stammdaten, Bestätigung hält bekannte Werte fest, kein Drift bei späteren Stammdaten-Änderungen), neue api/katalog-routes.test.ts +5 (HTTP: kompletter Ablauf Berater → Kunden-Link → Beraterteil → Bestätigung, Verwaltung nur Admin, Mandanten-Trennung, 404 auf allen öffentlichen Routen, Delta über HTTP), neue e2e/katalog.spec.ts +5 (gemockt: Delta-Ansicht mit ‚bekannt‘-Liste und nur Eigenem in der Nutzlast, Bedingung, Wiederholungsgruppe, Zusammenfassung des ganzen Katalogs, Bestätigung, Pflichthinweis, serverseitig unvollständig, bereits bestätigt, 404). Full-Stack-E2E folgt mit S9, sobald der Worker einen echten Katalog führt (die Registry ist bis dahin leer). Davor MED-D-703 (S2): neue domain/fragenkatalog.test.ts +4 (Fassung = Version + Hash, deterministische Serialisierung, Registry-Konsistenz), neue api/katalog-service.test.ts +12 (Kataloge je Angebot und Reihenfolge, Vorgang je Fassung idempotent und bei neuer Version neu, Antworten nur für erlaubte Schlüssel je Rolle, Bestätigung nur bei vollständigen sichtbaren Pflichtangaben mit Fassung + Beleg-Hash, Audit PII-arm ohne Link-Token, Wiedereröffnen, Signatur-Status idempotent, Stand-Übersicht, Mandanten-Trennung). Davor MED-D-703 (S1, Feld-Kern): neue domain/feld-kern.test.ts +15 (Bedingung ist/gesetzt/alle/eine, Wiederholungsgruppen-Schlüssel und Eintrags-Kontext, parseZahl/runde/zahlText, Rechenbausteine, Wohnflächen-Beispiel mit Gruppensumme, Pflicht nur für sichtbare Felder je Rolle und je Gruppeneintrag, Schema-Konsistenz inkl. Zyklus); Client: Build grün, gemockte E2E 46/46 unverändert (die Engine-Erweiterung hat vor S3 noch keinen Nutzer-Pfad, E2E-Spec folgt mit dem ersten Konsumenten). Davor MED-D-703 (S0b-2): neue domain/kind.test.ts +4 (Geburtsmonat-Normalisierung, volles Datum abgelehnt, Kind-Bereinigung — G-6), neue repo/kind-repo.test.ts +3 (Kinder 1:n je Mandant, ändern/löschen, Kontaktsteuerberater je Praxis getrennt vom privaten), domain/fremdformular.test.ts +1 (Attribute praxis.steuerberater.*). Davor MED-D-703 (S0b-1): domain/stammdaten-version.test.ts +1 (Aufnahme von Steuer-ID/Familienstand bricht keine Schema-3-Bestandskette, null ≙ fehlender Key, Feld-Historie), domain/stammdaten-uebernahme.test.ts +1 (bauePatch: Familienstand nur aus der Aufzählung; Schutz-Test Steuer-ID umgestellt, Ausweisnummer bleibt ausgeschlossen), domain/fremdformular.test.ts +1 (Weltmodell-Attribute steuer.*, steuerberater.*, person.familienstand).
MED-D-736 (Dexman-Beratungsmodus rundet Beträge auf 100 €): domain/dexman-kern.test.ts +3 (anzeigeFormat rundet gröber und verfeinert nie; Zahlen bleiben exakt — Monate/Konto/Tageslinie identisch, jeder Betrag in Herkunft und Sätzen ein Vielfaches von 100; Empfehlungen und Plausibilitäts-Hinweise folgen dem Raster, ohne Raster unverändert), e2e/dexman.spec.ts +1 (Beratungsmodus: Personal-Betrag 8.049 € → 8.000 € und zurück; das Eingabefeld der Ist-Tabelle behält den genauen Wert, Initialen statt Name).
MED-D-701 (Test-Übersicht als Fragment-Verzeichnis): neue scripts/test_testzahlen_messen.py +8
(statisches Zählen der drei Suiten: eingerückte Fälle und Template-Literale zählen, Fließtext nicht;
Generatoren it.each und abgeschaltete Fälle werden gemeldet statt gezählt; fehlendes Verzeichnis = 0;
die echten Suiten werden gefunden und sind plausibel; Kopfzeile mit der Server-Zahl zuerst — kanonisch für
den Doku-Check B34; Ersetzen nur zwischen den Markern, fehlende Marker brechen ab; das Ziel trägt die Marker).
Keine Server-/E2E-Testdatei berührt — die Gesamtzahlen bleiben unverändert und werden ab jetzt gemessen.
MED-D-678 (Mailkanal im Audit): api/portal.test.ts +2 (Log-Kanal erzeugt ebenfalls zustellung: ok — erst der Kanal-Name macht den stummen Versand sichtbar; ohne Kanal kein_kanal + kanal: null), die zwei Bestandstests prüfen den Kanal mit.
MED-D-730/731 (Mailkanal an allen Sendepfaden, Wartezeit an die abgeschickte Adresse): api/formular-vorgaenge.test.ts, api/unterlagen-erinnerung.test.ts und api/dexman-monatsmeldung.test.ts prüfen kanal im Audit mit (Zahl unverändert), api/wiedervorlage.test.ts +2 (zusätzlich: werfender Adapter → Zuweisung bleibt, Audit zustellung: fehler) (Zuweisung: kanal + zustellung nur bei tatsächlicher Mail — Neuzuweisung ja, dieselbe Person und Entziehen nein, deckungsgleich mit der Zahl gesendeter Mails), api/dentmarking.test.ts +1 (Ablauf-Hook: werfender Adapter bricht den Lauf nicht ab, beide Entwürfe behandelt, Audit zustellung: fehler + Kanal) und der bestehende Ablauf-Hook-Test prüft zustellung: ok, e2e/portal.spec.ts +1 (Feld während der laufenden Anfrage ändern: die Wartezeit liegt auf der abgeschickten Adresse, die nie angeforderte bleibt anforderbar; Antwort per Route-Gate festgehalten). Alle sechs Prüfungen wurden gegen den Stand ohne Fix rot gegengeprüft.
MED-D-693/694 (Dexman: Person + Anstellungs-Zeitspannen, Performance-Tabelle mit Festschreiben): domain/dexman-kern.test.ts +2 (Gehaltserhöhung als zweite Spanne; Alt-Bestand rechnet identisch — Read-Fallback mit abgeleitetem Eintritt/Austritt; Spannen-Prüfung: Rahmen abgeleitet, Lücke erlaubt, Überlappung und offene Mitte abgewiesen), domain/rechte.test.ts +1 (dexman_festschreibung_aufheben ist admin-exklusiv), api/dexman.test.ts +3 (Wasserzeichen sperrt Monate für jede Quelle → 409, erfasster Wert bleibt; nur abgeschlossene Monate und nur vorwärts, Log append-only; Aufheben nur Rolle admin, auditiert, danach wieder erfassbar), e2e/dexman.spec.ts +4 (Monate als Zeilen, mehrere Monate in einem Durchgang; „Wie Vormonat" + weiche Ausreißer-Hinweise; gesperrte Zeile ohne Eingabefeld + Aufheben nur für die Administration; Festschreiben sendet den gewählten Monat), e2e-fullstack/dexman-flow.spec.ts (Tabellen-Erfassung + Festschreiben gegen den echten Worker inkl. 409 auf den gesperrten Monat und Spannen in echter D1, Zahl unverändert). Davor: MED-D-699 (Dexman Plausibilitäts-Hinweise): domain/dexman-kern.test.ts +4 (Annuitätenrate unter den Zinsen, fallende Tilgung ausgenommen; Ist vor Eintritt/nach Austritt; Ist ohne Planumsatz, nach Austritt kein Hinweis; Beispielpraxis plausibel), e2e/dexman.spec.ts +2 (Hinweiskasten, Initialen im Beratungsmodus; plausible Planung ohne Kasten). MED-D-700 (Dexman-Monatsmeldung im Kundenportal): neue api/dexman-monatsmeldung.test.ts +4 (Aufgabe nur bei laufendem Mandat und fehlenden Ist-Werten; Formular nur Behandler, Vorbelegung, Scope; Einreichen → annehmen = Fakt Quelle portal, ablehnen = Korrektur + Mail, Korrektur nur abgelehnte Zeile, Audit ohne Beträge/Namen; Portal-Routen dormant/401/Dashboard/Einreichen/Scope), e2e/portal.spec.ts +1 (Melden mit Vorbelegung, Ausreißer-Hinweis, Senden), e2e/dexman.spec.ts +1 (Berater sichtet, Grund ist Pflicht), neue e2e-fullstack/dexman-monatsmeldung.spec.ts +1 (Portal-Einreichung → Annehmen → praxis_jahreswert Quelle portal; Worker mit PORTAL_TEST_MODE). Davor: MED-D-686 (Praxis-Pflicht für Dexman-Mandate): neue db/migrate-v69.test.ts +6 (Bestands-Zuordnung gegen echtes SQLite statt gegen das Fake-D1: eine Praxis → zuordnen; zwei offene Mandate ohne Praxis bleiben beide leer; belegte Praxis blockiert; abgeschlossene Mandate immer; 0/>1 Praxen und Fremd-Praxis nie; zweiter Lauf idempotent — deckt den CodeRabbit-Befund zu v69 ab), api/beratungsauftrag.test.ts +4 (Katalog: Pflicht nur für Dexman und impliziert Praxis-Bezug; Anlegen ohne Praxis abgelehnt; je Praxis nur EIN offenes Mandat, abgeschlossene blockieren nicht; nachträgliche Praxis-Zuordnung inkl. Audit/Eindeutigkeit/Fremd-Praxis), neue e2e-fullstack/dexman-mandat-praxispflicht.spec.ts +1 (API lehnt ohne Praxis ab, Produkt im „+ Mandat"-Dialog gesperrt, zweites Mandat je Praxis abgelehnt, Nachzug-Endpoint setzt die Praxis). Davor: MED-D-654 (Erinnerungs-Schalter aus der Akte): e2e-fullstack/onboarding-item-vorgang.spec.ts Erinnerungs-Test umgestellt (kein Schalter/Hinweis mehr; ausgeschaltet → leiser Stand in den Basis-Dokumenten), Zahl unverändert. Davor: MED-D-652 (Dexman-Monatsaufgaben): neue api/dexman-monatsaufgaben.test.ts +3 (Vormonat inkl. Jahreswechsel; nur laufende Dexman-Mandate, Fälligkeit 5./10./15.; Cron legt je Monat genau einmal an, gebunden an das Mandat). Davor: MED-D-649 (Folgeaufgaben für Dexman-Mandate): api/beratungsauftrag.test.ts (Playbook-Test prüft jetzt alle buchbaren Produkte + die vier Dexman-Aufgaben mit Fristen, Zahl unverändert), e2e-fullstack/dexman-flow.spec.ts prüft die Wiedervorlagen beim Buchen in echter D1. Davor: MED-D-646 (Standard-Unterlagen für Dexman-Mandate): domain/unterlagen-vorlagen.test.ts +1 (vier Unterlagen, eindeutige stabile IDs, idempotentes Seed-SQL v68), e2e-fullstack/dexman-flow.spec.ts prüft die Unterlagen beim Buchen in echter D1. Davor: MED-D-643 (Dexman-Controlling buchbar): api/beratungsauftrag.test.ts (Ablehnungs-Test durch "buchbar, an die Praxis gebunden" ersetzt, Zahl unverändert), api/lead.test.ts (Lead bietet genau die buchbaren Produkte an), e2e-fullstack/dexman-flow.spec.ts +1 (Mandat buchen → "Dexman öffnen" zeigt Dexman für die Praxis). Davor: MED-D-638 (Kunden-Erinnerung an fehlende Unterlagen): neue api/unterlagen-erinnerung.test.ts +5 (offene Unterlagen ohne Aufgaben/Dubletten; nächste Aktion; 7-Tage-Takt mit max. 3 Mails und genau einer Wiedervorlage; Zyklus-Ende/abgeschaltet/dormant ohne Portal; HTTP Stand + Schalter), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Schalter in der Akte persistiert in D1). Davor: MED-D-635/637/639 (Basis-Dokumente, Einmal-Link entfällt, 4-Stand-Schieber): api/portal.test.ts +1 (Onboarding-Einladung entsteht automatisch und idempotent über das Portal-Dashboard), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Überschrift „Basis-Dokumente", kein Einmal-Link-Abschnitt, „Entfällt" im Schieber und zurück). Davor: CodeRabbit-Nachlese #627: api/beratungsauftrag.test.ts +3 (abgelaufene Unterlage zählt als fehlend, unterschriebene nicht; eigenes Flag unterlagenFehler; Rollen-Update ohne team lässt das Team unverändert). Davor: MED-D-630 R-13 (Standard-Mandats-Unterlagen per Migration v66): neue domain/unterlagen-vorlagen.test.ts +2 (nur buchbare Produkte, stabile IDs, idempotentes Seed-SQL); e2e-fullstack/mandats-unterlagen.spec.ts prüft zusätzlich die Standard-Vorlage in echter D1. Davor: MED-D-630 Slice 2 (Mandats-Unterlagen je Produkt): api/beratungsauftrag.test.ts +4 (Reife mit fehlenden Unterlagen, Anlage aus Vorlage idempotent + auditiert, Unterlage ergänzen mandant-scoped, HTTP: Pflege nur Admin/Validierung/Löschen), e2e/verwaltung.spec.ts +1 (Vorlagen je Produkt), neue e2e-fullstack/mandats-unterlagen.spec.ts +1 (Vorlage → Anforderung im Mandat, abhaken persistiert, ergänzen). Davor: MED-D-630 (Onboarding als Abhak-Liste): e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Kundenakte zeigt Basis-Unterlagen ohne Mandat-Auswahl und ohne "Formular anfordern"). Davor: MED-D-629 (Teams + "Nicht zugewiesen"): domain/rechte.test.ts +2 (Team-Sicht, normalisiereTeam), api/rbac.test.ts +1 (Team setzen nur Admin, Team-Kunde sehen/bearbeiten, anderes Team 404, Team entfernen), e2e/verwaltung.spec.ts +1 (Team je Benutzer, "Chef / Backoffice"), e2e/cockpit.spec.ts +1 (Filter "Nicht zugewiesen"). Davor: MED-D-623 (übrige By-ID-Routen im Kunden-Scope): api/rbac.test.ts +1 (Dokumente, Bankkonten, Onboarding-Items, Signaturen, Dokumentgenerierung, Leistungszeit: fremder Kunde → 404 ohne Wirkung, eigener Kunde → 200, unbekannte ID → Handler-404). Davor: MED-D-384 (eigene Rechtsseiten): neue rechtliches.test.ts +10 (Pflichtkern inkl. Vertretung, nur https://-Overrides, Links nur bei vollständigen Angaben bzw. externe URL mit Vorrang, nur konfigurierte Angaben im Impressum, HTML-Escaping, Datenschutz-Inhalte + Turnstile/DSB/Aufsicht nur wenn konfiguriert) + oeffentlich-site.test.ts +2 (/impressum·/datenschutz auf allen Hosts, Landing verlinkt beide nur bei vollständigem Pflichtkern). Vorher: MED-D-382 (Praxis-Routen im Kunden-Scope): api/rbac.test.ts +1 (Praxis-, Gutachten-, Befund- und Dexman-Routen für fremden Kunden → 404 inkl. PATCH/POST ohne Wirkung, eigener Kunde → 200, Backoffice sieht alles, Flag aus = keine Regression). Davor: MED-D-383 (formelle Fachsprache, keine neuen Tests): domain/dexman-kern.test.ts, api/dexman.test.ts, e2e/dexman.spec.ts, e2e-fullstack/dexman-flow.spec.ts auf die neuen Texte angepasst; Kern-Test prüft zusätzlich die Statusbegriffe je Bereich. Davor: MED-D-381 (Dexman umgesetzt, zwei Oberflächen): neue domain/dexman-kern.test.ts +25 (Rechenkern aus shared/dexman-kern/: Monat/Format, Umsatz/Personal/Material/Fixkosten, Tageslinie, Ist vor Plan, Ein-/Austritt, Gehaltsänderung + Umsatzbeteiligung, Saison/Umsatzänderung, zuflussbasiert, Darlehen fallend/Annuität/Zinsbindung/privat/vermietet/Laufzeitende, Anschaffung/AfA, Ereignisse, Kontokorrentzins, Beispielpraxis deterministisch, Sätze, Hebel, Szenario ohne Seiteneffekt, Prüfregeln) + neue api/dexman.test.ts +8 (Stand/Lage, Versionen append-only, Archivieren, Ist in praxis_jahreswert mit Vorrang, Hebel → Wiedervorlage idempotent, PII-armes Audit + Lesezugriff, HTTP inkl. Rechte je Kunde); domain/zugriff.test.ts um dexman_planung erweitert; neue gemockte e2e/dexman.spec.ts +5 (Kompass: Satz/Tageslinie/Herkunft, Was-wäre-wenn ohne Speichern, Beratungsmodus; Klassisch: Kacheln/Personal/Monate + Umschalten, Formularprüfung) + Full-Stack e2e-fullstack/dexman-flow.spec.ts +1 (Akte → Praxen → Dexman, Ist-Erfassung persistiert, Übernehmen = neue Version, Klassisch zeigt dieselben Daten). Vorher: MED-D-337 (Unterschrift über den Assistenten): api/dokumentgenerierung.test.ts +2 (inkl. wiederherstellbare Verknüpfung) + api/individualdokument.test.ts +1 (Assistenten-Weg führt Quelle nach: Status/Audit, Stufen-Vorgabe AES, zweiter Start 409); neue Full-Stack-Spec e2e-fullstack/unterschrift-generierung.spec.ts +1. Vorher: MED-D-335 (Kundenseite Slice B): domain/naechste-schritte.test.ts +1 (Klartext ohne interne IDs/Jargon); neue Full-Stack-Spec e2e-fullstack/kundenseite-einfach.spec.ts +1 (Nächster Schritt oben, ein Status, "Mehr"-Reiter, kein Unterschrift-Direktversand). Vorher: MED-D-334 (interne Oberfläche Slice A): cockpit.spec.ts +3 (Leitstand-Kopfzeile ohne Überlauf bei 1280 px, Zuweisen per Personenauswahl, Fehlermeldung mit Ursache + nächstem Schritt). Vorher: MED-D-333 (Portal-Aufgabenliste/Fragebogen seitenweise): api/portal.test.ts +3 (Aufgabenliste aus Einladung/Formular/Fragebogen/Dokument, Portal-Mail-Texte, Einladungs-Betreff); E2E portal.spec.ts +1 (Aufgabenliste-Links) + public-strecken.spec.ts +1 (Fragebogen-Assistent). Vorher: MED-D-332 (Kundenseiten Slice A): neue domain/kunden-kontakt.test.ts +3 (leer = dormant, nur http(s)-Links, Berater nur mit Anzeigename) + api/onboarding-formular.test.ts +1 (Kontext liefert Kontakt-Block, nie Roh-E-Mail der Betreuung) → 793; api/portal.test.ts/api/ausfuellformular.test.ts/api/dokumentgenerierung.test.ts an neue Labels/Config-Form angepasst; gemockt e2e/onboarding.spec.ts +2 (Kontakt-Fußzeile mit tel:-Link + fehlender Link ausgeblendet + "Zum Absenden fehlt noch"; keine leere Hilfe-Box ohne Konfig) → 33, e2e/portal.spec.ts um Upload-Button/accept/Bestätigung erweitert. Davor MED-D-331 (K-10): api/formular-vorgaenge.test.ts +2 (ohne Kunden-E-Mail keine Erinnerung/keine Markierung + Nachholen; Zustellfehler nicht-fatal, harte Eskalation läuft weiter) und bestehender Weich-Test auf Empfänger = Kontakt-E-Mail + Fortsetzungs-Link umgestellt → 789. Davor MED-D-330 (Begriffe "Kunde"/"Mandat"): keine neuen Tests, Zahl unverändert — angepasst an die neuen UI-/Fehlertexte: api/ausfuellformular.test.ts (Festwerte "Kunde"/"Kundennummer"), api/dokumentgenerierung.test.ts (Mapping "Kunde"), api/formular-vorgaenge.test.ts (Betreff "beim Kunden nachfassen"), e2e/cockpit.spec.ts + e2e/smoke.spec.ts + e2e-fullstack/smoke.spec.ts + e2e-fullstack/mandant-create.spec.ts (Überschrift "Kunden", "+ Neuer Kunde", Leerzustand). Davor MED-D-329/328 (Einfachheits-Audit): api/ausfuellformular.test.ts +2 (Korrektur-Mail an Kontakt-E-Mail mit absolutem Link, ohne E-Mail kein Versand + kundeBenachrichtigt=false, Provider-Fehler nicht-fatal) → 787; gemockt e2e/cockpit.spec.ts +3 ("Heute fällig" zeigt nur datierte überfällige/heute fällige Punkte, Leerzustand, Fokus-Kürzel Default aus) + e2e/portal.spec.ts +1 (Portal ohne Staff-Kopfzeile, data-theme=light) → 31; Full-Stack neue e2e-fullstack/timer-kein-autostart.spec.ts +1 (Öffnen startet keinen Timer, Klick startet ihn serverseitig) → 9. Davor MED-D-326 (Betriebsinfo im Footer nur für Admins): +1 gemockte E2E in client/e2e/smoke.spec.ts — Nicht-Admin (Berater) sieht keinen Betriebsinfo-Fuß (footer.mw-footer gar nicht gerendert, keine Versionsinfo). 34→35 E2E (26→27 gemockt), Server-Vitest unverändert. Davor MED-D-324 (Version/Deploy dezent in den Footer): +2 gemockte E2E in client/e2e/smoke.spec.ts — Cockpit (Route "/"): Version + Deploy stehen im footer.mw-footer, nicht mehr im header.mw-header; App-Shell (/verwaltung): Version + Deploy im footer.md-footer, nicht im header.md-header. Server-Vitest unverändert (reine Client-/CSS-Änderung). 32→34 E2E (24→26 gemockt), 785 Vitest unverändert. Davor MED-D-323 (MED-OP-MIGRATE-1: Auto-Migration restart-sicher): +3 Server-Vitest / +1 Datei — neue db/migrate.test.ts mit minimalem Fake-D1 (modelliert _meta, ALTER TABLE … ADD/DROP COLUMN-Semantik inkl. Duplikat-Erkennung, atomare batch()): Erststart erreicht die Ziel-Schema-Version; zweiter Aufruf ist ein No-op (idempotent, keine DDL erneut); Absturz mitten in der Migration → Neustart läuft sauber weiter (Version bleibt beim Vorgänger, kein duplicate column). Erste Bausteine Richtung MED-OP-TEST-1 (echtes D1-Harness). 782→785 Vitest (63→64 Dateien). Davor MED-D-322 (OP-DM-8 Mail-Selbsttest): +4 Server-Vitest in api/app.test.ts (admin→Log-Kanal zugestellt=false, keine echte Mail; echter Kanal→sende() mit der eigenen Adresse aufgerufen→zugestellt=true; Provider-Fehler→502 ok:false; Nicht-Admin (berater)→403) +3 gemockte E2E / +1 Datei (neue client/e2e/verwaltung.spec.ts: echter Kanal→Erfolgshinweis, Log-Kanal→"keine Mail zugestellt", 502→Fehlermeldung im Status-Absatz). 778→782 Vitest (Dateien unverändert 63), 29→32 E2E (21→24 gemockt, 14→15 Playwright-Dateien). Davor MED-D-320 (MED-OP-PORTAL-2 Teil 2: mehrere Kontakt-Logins je Mandant): +3 Server-Vitest in api/portal.test.ts (Staff legt mehrere Kontakte an + listet sie (sortiert), doppelte E-Mail→409 email_vergeben; Sperren beendet die laufende Session (Dashboard 200→401) + wird als gesperrt gelistet, Reaktivieren gibt Zugang zurück; Scope-Guard: fremdes Konto sperren→404, unverändert). Client (Kontakt-Liste + Sperren/Reaktivieren in mandant-einladungen) ng build-verifiziert (das Register-Panel liegt tief in mandant-detail — wie die bestehende Einladungen-UI build-only, kein eigenes E2E-Harness). 775→778 Vitest (Dateien unverändert 63), E2E unverändert (29). Davor MED-D-319 (MED-OP-UX-14: Button-CTA-Vereinheitlichung + Verlassen-Modal → md-overlay + Command-Palette-Absicherung): +3 gemockte E2E / +1 Datei — neue client/e2e/cockpit-overlays.spec.ts (Command-Palette: Öffnen über den Kopf-Button + Fokus-Pin aufs Suchfeld + Esc-Schließen; Filtern nach Eingabe + Treffer per ↓ +↵ ausführen → Theme dunkel; Backdrop-Klick schließt). Server-Vitest unverändert (reine Client-/CSS-Änderung: Primary-CTA --md-green überall, Verlassen-Modal nutzt jetzt die geteilte md-overlay-Dialogmechanik statt hand-gerollt). 26→29 E2E (18→21 gemockt, 13→14 Playwright-Dateien), 775 Vitest unverändert. Davor MED-D-318 (UI/UX-Konsistenz- & Rough-Edges-Pass): +0 Tests — reine Konsistenz-/Client-/CSS-Änderungen (Theming-Tokens, Terminologie/Labels, Ladeindikatoren, Fokus-/Doppel-Submit-Guards, labels.ts-Nachbauten zurückgeführt). Kein neues Server-Verhalten (die Landing-Farb-/Wortmarken-Änderung berührt oeffentlich-site.ts nur kosmetisch → bestehende oeffentlich-site.test.ts unverändert grün); die Client-Änderungen sind über ng build + die bestehende client/e2e-Suite (u. a. Portal-Upload) gedeckt, die Fokus-/CSS-Pfade sind ohne zusätzlichen Harness nicht sinnvoll unit-testbar. Button-/Overlay-Vereinheitlichung ist als MED-OP-UX-14 getrackt (braucht erst Visual-Regression-Deckung). 775 Vitest / 18 gemockt unverändert. Davor MED-D-317 (WIP-Landing auf medidentas.com + robots.txt/KI-Trainings-Hinweise; inkl. CodeRabbit-Nachlese #343): +11 Server-Vitest / +1 Datei — neue oeffentlich-site.test.ts (istMarketingHost Apex/www vs. App-Host; robotsTxt host-abhängig — App-Host Disallow:/ + KI-Crawler gesperrt, Marketing-Host Allow:/ + KI-Crawler gesperrt; ai.txt nennt DSM/§44b + Disallow:/; tdmrep.json reserviert /; behandleOeffentlicheSite — robots/ai/tdmrep auf allen Hosts mit X-Robots-Tag: noai/tdm-reservation:1-Headern, Apex /→WIP-Landing noindex, App-Host /→null (regulär weiter), HEAD ohne Body, nicht-GET→null, Landing verweist auf app.medidentas.com). Die CodeRabbit-Nachlese #343 (dokFarbe-Theme-Tokens --warn/--muted→--amber/--mut; per-Dokument-Upload-Set statt Einzel-Signal; Decision-Log-Backtick) ist über den bestehenden client/e2e-Portal-Upload-Test + ng build abgedeckt (reine Client-/Doku-Korrektur, kein neuer Vitest); die magic-byte-Content-Prüfung bleibt bewusst Backlog (dokumentierter Tradeoff). 764→775 Vitest (62→63 Dateien), E2E unverändert (26). Davor MED-D-316 (Portal-Dokument-Upload, MED-OP-PORTAL-2 Teil 1): +9 Server-Vitest / +1 Datei — neue domain/upload-pruefung.test.ts (+4: pruefeUpload ok · leere Datei → leer · über 15 MB → zu_gross · unerlaubte Endung inkl. Doppel-Endung script.pdf.js → typ_unzulaessig) + api/portal.test.ts (+5: Upload-Route dormant → 404 / ohne Session → 401; fremdes/unbekanntes Dokument → 404 kein Cross-Mandant-Leak; angefordertes Dokument → Upload 200 + Status erhalten + im Dashboard sichtbar; bereits geprüft → 409 nicht_offen; unzulässiger Typ → 400) + +1 gemockte E2E in client/e2e/portal.spec.ts (angefordertes Dokument im Portal hochladen → Erfolg, kein Fehler). Server-Route unter /oeffentlich/portal/dokumente/:docId/upload (Session-scoped, eigener Mandant, R2 via bestehende DokumentService.hochladen); Token-Upload-Route zusätzlich mit pruefeUpload gehärtet. 755→764 Vitest (61→62 Dateien), 25→26 E2E (17→18 gemockt, Dateien unverändert 13). Davor MED-D-315 (Portal-Zugang automatisch beim Kunden-Anlegen): +4 Server-Vitest in api/portal.test.ts (zugangSicherstellen: dormant → kein Konto/kein Send; aktiv + neue E-Mail → Konto angelegt + Invite genau einmal, zweiter Aufruf idempotent; ohne verwertbare Kontakt-E-Mail nichts; HTTP POST /api/mandanten mit E-Mail + aktivem Portal → Konto auto-angelegt + Invite). 751→755 Vitest (Dateien unverändert 61), E2E unverändert. Davor MED-D-314 (Cloudflare Email Service als einziger Mail-Provider, MailChannels raus): −6/+5 Server-Vitest (netto −1) — benachrichtigung/mailchannels.test.ts entfernt (−6); neue benachrichtigung/cloudflare-email.test.ts (+5): cloudflareEmailKonfigAusEnv-Defaults/Trim; Versand über das gemockte send_email-Binding mit From-Objekt + Magic-Link im Text-Körper; empfänger-loser Aufruf still übersprungen (kein send); Binding-Fehler wirft mit code, ohne Empfänger-PII. 752→751 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-312 (CodeRabbit-Nachlese zu #339: Login-Härtung + Mailversand-Robustheit + Rate-Limit-Retention; inkl. Round-2-Review nach "ready for review"): +6 Server-Vitest — api/staff-login.test.ts +1 (Staff-Session überschreibt keine abweichende Access-Identität: A's Session-Cookie + B's Access-Header → es gilt B/backoffice, nicht A/admin; passende Access-Identität → A/admin) + api/portal.test.ts +3 (bereinigeRateLimits löscht nur abgelaufene Fenster; Login-Mail-Zustellfehler nicht-fatal → Login-Flow läuft weiter + Audit zustellung:fehler; erfolgreiche Zustellung → zustellung:ok) + api/wiedervorlage.test.ts +2 (Erinnerungs-Item-Isolation: ein Fehlversand stoppt die übrigen nicht + bleibt retrybar; Audit-Fehler NACH erfolgreicher Zustellung → keine Wiederholung/kein Doppelversand). Übrige Fixes (MailChannels-8s-Timeout inkl. Fehler-Body-Read, PORTAL_AKTIV-Startup-Warnung, Turnstile-Token-Reset im Client) sind über die bestehenden Tests + tsc/ng build abgedeckt (die Timeout- und Client-Widget-Pfade sind ohne Netz-/Browser-Harness nicht sinnvoll unit-testbar). 746→752 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-311 (Portal-Login-Härtung: Turnstile + Rate-Limiting am öffentlichen anmelden, löst MED-OP-PORTAL-3): +3 Server-Vitest in api/portal.test.ts (GET /oeffentlich/portal/config dormant→404 / aktiv→{turnstileSitekey:null}; Rate-Limit je E-Mail — die 6. Anmeldung im 10-Min-Fenster → 429 rate_limit, eine andere E-Mail behält ihr eigenes Fenster; bei gesetztem turnstileSecret ohne Token → 400 bot_check vor dem Rate-Limit). Der neue repo.pruefeRateLimit (Tabelle rate_limit, Migration v62) ist über den Rate-Limit-Test gegen den Memory-Repo abgedeckt; Client (Turnstile-Widget auf der Portal-Login-Seite) ng build-verifiziert + portal-E2E-Login-Mock um /portal/config ergänzt. 743→746 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-310 (Portal-Go-Live: echter Mailversand MailChannels/OP-DM-8 + Kundenportal scharf): +6 Server-Vitest / +1 Datei — neue benachrichtigung/mailchannels.test.ts (gegen gemocktes fetch: mailChannelsKonfigAusEnv dormant ohne MAILCHANNELS_API_KEY; Defaults + DKIM nur bei allen drei Feldern; sende POSTet Payload + X-Api-Key, Magic-Link steht im Körper; DKIM je Personalisierung mitgesendet; Empfänger-loser Aufruf still übersprungen (kein fetch); HTTP-Fehler wirft mit Status, ohne Empfänger-PII). portal.test.ts an die korrigierte Magic-Link-Form (/portal?token= Client-Route statt API-Endpunkt) angepasst — Token wird aus dem Link gezogen und gegen den API-Endpunkt eingelöst. Client (neuer "Kundenportal"-Abschnitt in der Akte, mandant-einladungen) ng build-verifiziert; der Portal-Konto-Endpunkt ist bereits durch portal.test.ts abgedeckt. 737→743 Vitest (60→61 Dateien), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-309 (Staff-Magic-Link-Login "hinter Access", dormant): +7 Server-Vitest / +1 Datei — neue api/staff-login.test.ts (alle 3 Routen dormant→404 ohne STAFF_LOGIN_AKTIV, Golden Rule; Anmelden ohne Konto-Enumeration → unbekannte E-Mail identische ok-Antwort ohne Link; ungültige E-Mail → 400; Bootstrap-Admin m.kirchhof@drkv.com UND per Verwaltung angelegter Mitarbeiter erhalten je einen Link = zweite Nutzeranforderung; voller Magic-Link-Flow anfordern→einlösen→staff_session-Cookie + Token-Replay → 401; ungültiger Token → 401; Staff-Session etabliert die Identität hinter Access unter enforced OHNE Access-Header → /api/ich 200 Rolle=admin, ohne beides → 401, nach Logout → 401) + +3 gemockte E2E / +1 Datei — neue client/e2e/staff-login.spec.ts (ohne Token → Mitarbeiter-Anmeldeformular → Magic-Link angefordert; Token einlösen bei dormantem Login (404) → "zurzeit nicht verfügbar"; ungültiger Token (401) → "neuen Link anfordern"). Migration v61 (staff_anmelde_token/staff_session, additiv) via Memory-Repo + tsc mitverifiziert; Client staff-login.component.ts ng build-verifiziert. 730→737 Vitest (59→60 Dateien), 22→25 E2E (14→17 gemockt, 12→13 Playwright-Dateien). Davor MED-D-308 (S5-UI: Kunden-Self-Service-Portal Client-UI, dormant): +3 gemockte E2E / +1 Datei — neue client/e2e/portal.spec.ts (dormant PORTAL_AKTIV=aus → "Kundenportal zurzeit nicht verfügbar"; nicht angemeldet → Anmelde-Formular → Magic-Link angefordert; angemeldet → Dashboard zeigt Onboarding-Fortschritt/offene Items/Formular-Status + proaktive S4-Einreichung wird bestätigt), gegen die gemockten Portal-Endpunkte. Server unverändert (nur bestehende Endpunkte konsumiert); portal-public.component.ts + Portal-API-Methoden ng build-verifiziert. 19→22 E2E (11→14 gemockt, 11→12 Playwright-Dateien), 730 Vitest unverändert. Davor MED-D-307 (N5: Onboarding-Einwilligung über DocuSign statt SES-Attrappe, flag-gated/dormant, OP-SIGN-1): +3 Server-Vitest in api/onboarding-formular.test.ts (Flag an + Signatur-Dienst → Einwilligung als echte SES über den Fake-Provider angefordert/pending/PDF-Beleg, Audit einwilligung.angefordert statt .erteilt; Flag an ohne Dienst → SES-Attrappe (dormant-sicher); Provider-Fehler → consent_provider_fehler, Einladung bleibt offen — kein stiller Fallback). Client unverändert (Danke-Seite bleibt; die DocuSign-Mail ist der Folgeschritt). Der DocuSign-Pipeline-Pfad (anfordern→Envelope→Webhook→abgleich) ist bereits durch die Full-Stack-docusign-flow.spec.ts abgedeckt; der globale Fullstack-Flag bleibt aus (Onboarding-E2E prüft weiter den SES-Default). 727→730 Vitest, E2E unverändert (19). Davor MED-D-306 (kollisionssicheres Hash-Schema 3 + Ablehnung unbekannter Schema-Marker, G-4; CodeRabbit-Nachlese zu #332): +4 Server-Vitest in domain/stammdaten-version.test.ts (Schema 2 kollidiert bei =/|-Freitext, Schema 3 nicht — behebt den Trenner-Injektions-Fund; kanonischerSnapshot wirft bei unbekanntem Schema statt still zurückzufallen; pruefeVersionsKette meldet einen unbekannten hashSchema-Marker als Bruch; Umschreiben eines Bestands-Markers 2→3 wird erkannt). Server-only Härtung; Full-Stack-E2E 8/8 (schema-3-Schreibpfad gegen echten Worker) unverändert. 723→727 Vitest, E2E unverändert (19). Davor MED-D-305 (feldset-tolerante Stammdaten-Hash-Kette, G-4; die 6 Onboarding-Grunddaten jetzt versioniert): +6 Server-Vitest in domain/stammdaten-version.test.ts (neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil = Regressions-Lock der Bestands-Ketten; Schema-2 serialisiert feldset-tolerant über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation an einer Schema-1-Zeile erkannt; gemischte Kette Schema-1 + neue Schema-2-Zeile mit den 6 neuen Feldern bleibt intakt — Kern von MED-D-305; künftiges Feld-Wachstum bricht eine Schema-2-Zeile nicht, ein nachträglich eingemischtes Feld schon = Tamper-Evidenz). Client (StammdatenSnapshot/StammdatenVersion-Typen + 6 md-versioned-field-Zeilen build-verifiziert; Migration v60 von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert, 8/8 grün). 717→723 Vitest, E2E unverändert (19). Davor MED-D-304 (Kunden-Self-Service-Portal S3 Dashboard + S4 proaktive Einreichung, dormant): +5 Server-Vitest in api/portal.test.ts (S3 Dashboard dormant→404/ohne-Session→401/kompakte Sicht; S4 Vorschlag dormant→404/ohne-Session→401/keine-Whitelist-Felder→400; voller Round-Trip Einreichen→Berater-Liste→Übernahme (4-Augen, self_service→Onboarding-Regelset)→Stammdaten geschrieben; Idempotenz je Mandant). Client (Portal server-only; Berater-Sektion mandant-formulare build-verifiziert — kein e2e-Harness für diese Fläche). 712→717 Vitest, E2E unverändert (19). Davor MED-D-303 (Item↔Mandat-Zuordnung): +3 Server-Vitest + 1 Full-Stack-E2E / +1 Datei — api/app.test.ts +3 (Item einem Mandat zuordnen + wieder lösen; fremde/unbekannte Auftrags-ID → 404 kein Cross-Mandant-Verweis; fremdes Item → 404 + ungültige auftragRef → 400) + neue client/e2e-fullstack/onboarding-item-mandat.spec.ts 1 (Zuordnen → Persistenz-Roundtrip aus D1 → Lösen + Cross-Mandant-Guard, Migration v59 gegen den echten Worker). 709→712 Vitest, 18→19 E2E (7→8 Full-Stack, 10→11 Playwright-Dateien). Davor MED-D-302 (Onboarding-Greenfield G5 — dormante ausfuellformular-Tabelle + Repo-Methoden entfernt): +0 Tests — reiner Dead-Code-Abbau (die entfernten Methoden hatten seit G3E2 keine Test-Usages mehr); Migration v58 (DROP TABLE IF EXISTS ausfuellformular) wird von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert (7/7 grün, sauberer Boot). 709 Vitest unverändert. Davor MED-D-301 (Onboarding-Greenfield G4 — Item↔Vorgang-Link + Bedarf je Mandat): +2 Server-Vitest + 2 Full-Stack-E2E / +1 Datei — api/ausfuellformular.test.ts +1 (erstellen mit onboardingItemId setzt den Item-Link + auditiert ihn; zweites Anfordern desselben Items ist idempotent, ein Vorgang je Item), api/beratungsauftrag.test.ts +1 (liste liefert den abgeleiteten benoetigt-Bedarf aus dem Produkt-Playbook, G-2) + neue client/e2e-fullstack/onboarding-item-vorgang.spec.ts 2 (Item↔Vorgang-Roundtrip gegen echten Worker/D1: "Formular aus Item anfordern" → Link + Liste + idempotente Wiederverwendung; gebuchtes Mandat → benoetigt abgeleitet). 706→709 Vitest, 16→18 E2E (5→7 Full-Stack, 9→10 Playwright-Dateien). Davor MED-D-299 (Onboarding-Greenfield G3 Etappe 3a — Onboarding-Strecke auf die Feld-Engine): +0 Tests, verhaltensneutral — die Onboarding-Grunddaten rendern jetzt über PubFeldEngineComponent; die bestehende client/e2e/onboarding.spec.ts (Lücken-Report · Happy-Path bis "Vielen Dank!" · ungültiger Token) deckt die Strecke über dieselben Feld-IDs weiter ab und bleibt 11/11 grün. Davor MED-D-298 (Onboarding-Greenfield G3 Etappe 2 — Ausfüll-Flow auf den einen Service): −7 Vitest / −1 Datei — die G2-Lean-Service-Tests (api/formular-vorgang.test.ts) sind entfallen (der Lean-Service wurde durch den voll-ausfüll-fähigen FormularVorgangService ersetzt); die bestehende api/ausfuellformular.test.ts (Domäne + Service + HTTP-Fluss) testet jetzt den neuen FormularVorgangService gegen den formular_vorgang-Store (Import/Repo-Zugriffe umgestellt, identische Erwartungen = Contract-Netz); api/formular-vorgaenge.test.ts (SLA/Öffnungs-Tracking) ebenso auf den neuen Store repointet. Full-Stack-E2E (formular-sichtung.spec + DocuSign) verifiziert den Ausfüll-Flow gegen den echten Worker auf dem neuen Store (5/5). 713→706, 60→59 Dateien. Davor MED-D-297 (Onboarding-Greenfield G3 Etappe 1 — geteilte Feld-Engine): +2 gemockte E2E / +1 Datei — client/e2e/formular.spec.ts (die PubFeldEngineComponent rendert die Ausfüll-Formular-Felder inkl. Marker-Chips; Pflichtfeld füllen → Vorschau → rechtsverbindlich absenden → "Vielen Dank!" mit Beleg-Nr.; leeres Pflichtfeld → Vorschau blockiert mit ehrlichem Hinweis). Server-Vitest unverändert (reine Client-Konsolidierung). 14→16 E2E, 8→9 Playwright-Dateien. Davor MED-D-296 (Onboarding-Greenfield G2 — eine FormularVorgang-Entität + Service): +8 Server-Vitest / +1 Datei — api/formular-vorgang.test.ts 7 (schema-getriebener Lebenszyklus: erstellen inkl. Item-Link + Audit · kontext/Öffnungs-Tracking · einreichen mit Pflichtprüfung/Prüfsumme + Doppel-Einreichung-Konflikt · Abschnitts-Freigabe N2 idempotent · Feld-Ablehnung → Korrektur-Schleife inkl. Rücksetzen bei Re-Einreichung · Wächter · Integration mit dem G1-Adapter-Schema) + domain/feld.test.ts +1 (fehlendePflicht: Wert ODER Markierung erfüllt Pflicht, DM-9). Migration v57 (formular_vorgang) via Full-Stack-E2E gegen den echten Worker verifiziert (5/5, sauberer Boot). 705→713, 59→60 Dateien. Davor MED-D-295 (Onboarding-Greenfield G1 — kanonisches Feld-Modell): +9 Server-Vitest / +2 Dateien — domain/feld.test.ts 5 (reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt + Schema-Konsistenz-Invariante schemaVerstoesse) + domain/formular-schema.test.ts 4 (die 3 Adapter überführen die Alt-Kataloge treu: Onboarding pflicht:bool→wichtigkeit+select→auswahl, Ausfüll-KundenFeld 1:1, Fragebogen klasse→wichtigkeit+block→Abschnitt+Öffnungszeiten-strukturTyp; Feldzahl erhalten, jedes Schema konsistent). 696→705, 57→59 Dateien. Davor MED-D-293 (Onboarding-Vereinfachung N1, 1. Schritt — geteilte Bausteine der öffentlichen Kundenstrecken): +0 Tests, verhaltensneutral — die wortgleich kopierte Lade-Fehler-Auswertung + Turnstile-Callback-Bindung sind jetzt geteilte Bausteine in pub-bausteine.ts (ladeFehlerAuswerten/bindeTurnstileCallback); die bestehende gemockte E2E-Suite (client/e2e/, u. a. "ungültiger Token"/"ungültige Fragebogen-Einladung") deckt die Fehler-/Zugriffsschutz-Pfade der 4 Public-Strecken ab und bleibt 9/9 grün (Client hat keine Unit-Test-Infrastruktur). Davor MED-D-292 (Onboarding-Vereinfachung N2, Abschnitts-Freigabe): +4 Server-Vitest (domain/feld-sichtung.test.ts +1 akzeptiereOffene — akzeptiert nur offene Felder, lässt eine Ablehnung mit Grund unangetastet, idempotent; api/ausfuellformular.test.ts +3 alleAkzeptieren — 1-Klick-Freigabe akzeptiert alle offenen + Audit PII-arm, zweiter Aufruf nichts_offen, eine vorherige Ablehnung bleibt bestehen und öffnet beim Abschließen die Korrektur-Schleife, nur für eingereichte Formulare) + +1 Full-Stack-E2E (client/e2e-fullstack/formular-sichtung.spec.ts: einreichen → Abschnitt in 1 Klick freigeben → abschließen gegen den echten Worker; deckt den atomaren read-modify-write der Sichtung in D1 ab). 692→696 Vitest, 13→14 Playwright (7→8 Dateien). Davor MED-D-290 (Full-Stack-E2E gegen den echten Worker, löst MED-OP-E2E-1 ein): +4 Playwright-Tests in client/e2e-fullstack/ gegen den ECHTEN Worker (wrangler dev · workerd · lokale D1 + R2 · SIGNATUR_PROVIDER=fake) statt gemockter API — App-Shell-Smoke + Health, Mandant anlegen → persistiert → Reload (echter Browser→Worker→D1-Pfad), DocuSign-Flow (Mandant→Onboarding→Ablage provisionieren→Datei ablegen→zur Unterschrift senden→Vor-Ort-/Demo-Abschluss→Dokument "erhalten" + signierte Rückablage; echter D1/R2-Write-back + Provider-Lebenszyklus). Ein Origin bedient UI + /api (wie Produktion), Overrides nur fürs Testen (AUTH_ENFORCED=false, Fake-Signatur). CI-Job client-e2e-fullstack (gated bei Client- oder Server-Änderung, Teil des Pflicht-Checks checks). Deckt den echten Datenpfad ab (Migration, D1-SQL, R2-Ablage), den die In-Process-Vitest nicht durchläuft. Davor MED-D-289 (E2E-/UI-Test-Infrastruktur, gemockte API): +9 Playwright-Tests in client/e2e/ — App-Shell-Smoke (bootet + Marke/Cockpit + Navigation), Cockpit (Mandantenliste rendert gelieferte Mandanten / ehrlicher Leerzustand), Onboarding-Kundenstrecke (3-Schritte-Flow: Lücken-Report bei fehlender Pflichtangabe · vollständiger Weg bis "Vielen Dank!" · ungültiger Token), öffentliche Strecken (Lead-Formular rendert · ungültige Fragebogen-Einladung). Sie laufen gegen den fertigen Angular-Build (dist/browser via e2e/static-server.mjs) mit gemockter API (Playwright-route, e2e/support/mock-api.ts) — deterministisch, ohne Worker/D1/Secrets. In CI als client-e2e-Job (gated wie client-build, Teil des Pflicht-Checks checks, "immer laufen lassen"). Erste Client-Test-Infrastruktur überhaupt — löst die bisherige "nur ng build"-Lücke (MED-D-267 ff.). Volllast-E2E gegen echten Worker + D1 (wrangler) ist als Folge-Slice MED-OP-E2E-1 notiert. Server-Vitest 692 unverändert (reine Client-/CI-Ergänzung). Davor MED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem "wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Test — domain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Tests — api/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Tests — api/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → "nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte "Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistik → ng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Tests — signatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Tests — api/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Tests — auth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Tests — api/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Tests — api/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Tests — api/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Tests — api/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Tests — api/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 "keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + "Zu fakturieren"-Filter): +1 Test — api/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Tests — api/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Tests — domain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Tests — domain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Tests — Davor: MED-D-677 (Turnstile deploy-fähig): neue portal-konfig.test.ts +5 (Portal inaktiv → keine Meldung; fehlendes EMAIL-Binding und fehlendes TURNSTILE_SECRET; Secret ohne Sitekey wird zuerst als „Login blockiert" gemeldet; vollständige Konfiguration schweigt). Davor: MED-D-674 (Portal-Login „E-Mail erneut senden"): e2e/portal.spec.ts +1 (Wartezeit-Knopf gesperrt → nach page.clock.runFor klickbar → enumeration-freie Bestätigung → „Andere E-Mail-Adresse verwenden" zurück zur Anmeldung). Davor: MED-D-672 („Nächster Schritt"-Banner entfernt): e2e-fullstack/kundenseite-einfach.spec.ts prüft jetzt die Abwesenheit des Banners statt seiner Inhalte (Titel/Kommentar angepasst), Zahl unverändert. Davor: MED-D-654 (Erinnerungs-Schalter aus der Akte): e2e-fullstack/onboarding-item-vorgang.spec.ts Erinnerungs-Test umgestellt (kein Schalter/Hinweis mehr; ausgeschaltet → leiser Stand in den Basis-Dokumenten), Zahl unverändert. Davor: MED-D-652 (Dexman-Monatsaufgaben): neue api/dexman-monatsaufgaben.test.ts +3 (Vormonat inkl. Jahreswechsel; nur laufende Dexman-Mandate, Fälligkeit 5./10./15.; Cron legt je Monat genau einmal an, gebunden an das Mandat). Davor: MED-D-649 (Folgeaufgaben für Dexman-Mandate): api/beratungsauftrag.test.ts (Playbook-Test prüft jetzt alle buchbaren Produkte + die vier Dexman-Aufgaben mit Fristen, Zahl unverändert), e2e-fullstack/dexman-flow.spec.ts prüft die Wiedervorlagen beim Buchen in echter D1. Davor: MED-D-646 (Standard-Unterlagen für Dexman-Mandate): domain/unterlagen-vorlagen.test.ts +1 (vier Unterlagen, eindeutige stabile IDs, idempotentes Seed-SQL v68), e2e-fullstack/dexman-flow.spec.ts prüft die Unterlagen beim Buchen in echter D1. Davor: MED-D-643 (Dexman-Controlling buchbar): api/beratungsauftrag.test.ts (Ablehnungs-Test durch "buchbar, an die Praxis gebunden" ersetzt, Zahl unverändert), api/lead.test.ts (Lead bietet genau die buchbaren Produkte an), e2e-fullstack/dexman-flow.spec.ts +1 (Mandat buchen → "Dexman öffnen" zeigt Dexman für die Praxis). Davor: MED-D-638 (Kunden-Erinnerung an fehlende Unterlagen): neue api/unterlagen-erinnerung.test.ts +5 (offene Unterlagen ohne Aufgaben/Dubletten; nächste Aktion; 7-Tage-Takt mit max. 3 Mails und genau einer Wiedervorlage; Zyklus-Ende/abgeschaltet/dormant ohne Portal; HTTP Stand + Schalter), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Schalter in der Akte persistiert in D1). Davor: MED-D-635/637/639 (Basis-Dokumente, Einmal-Link entfällt, 4-Stand-Schieber): api/portal.test.ts +1 (Onboarding-Einladung entsteht automatisch und idempotent über das Portal-Dashboard), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Überschrift „Basis-Dokumente", kein Einmal-Link-Abschnitt, „Entfällt" im Schieber und zurück). Davor: CodeRabbit-Nachlese #627: api/beratungsauftrag.test.ts +3 (abgelaufene Unterlage zählt als fehlend, unterschriebene nicht; eigenes Flag unterlagenFehler; Rollen-Update ohne team lässt das Team unverändert). Davor: MED-D-630 R-13 (Standard-Mandats-Unterlagen per Migration v66): neue domain/unterlagen-vorlagen.test.ts +2 (nur buchbare Produkte, stabile IDs, idempotentes Seed-SQL); e2e-fullstack/mandats-unterlagen.spec.ts prüft zusätzlich die Standard-Vorlage in echter D1. Davor: MED-D-630 Slice 2 (Mandats-Unterlagen je Produkt): api/beratungsauftrag.test.ts +4 (Reife mit fehlenden Unterlagen, Anlage aus Vorlage idempotent + auditiert, Unterlage ergänzen mandant-scoped, HTTP: Pflege nur Admin/Validierung/Löschen), e2e/verwaltung.spec.ts +1 (Vorlagen je Produkt), neue e2e-fullstack/mandats-unterlagen.spec.ts +1 (Vorlage → Anforderung im Mandat, abhaken persistiert, ergänzen). Davor: MED-D-630 (Onboarding als Abhak-Liste): e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Kundenakte zeigt Basis-Unterlagen ohne Mandat-Auswahl und ohne "Formular anfordern"). Davor: MED-D-629 (Teams + "Nicht zugewiesen"): domain/rechte.test.ts +2 (Team-Sicht, normalisiereTeam), api/rbac.test.ts +1 (Team setzen nur Admin, Team-Kunde sehen/bearbeiten, anderes Team 404, Team entfernen), e2e/verwaltung.spec.ts +1 (Team je Benutzer, "Chef / Backoffice"), e2e/cockpit.spec.ts +1 (Filter "Nicht zugewiesen"). Davor: MED-D-623 (übrige By-ID-Routen im Kunden-Scope): api/rbac.test.ts +1 (Dokumente, Bankkonten, Onboarding-Items, Signaturen, Dokumentgenerierung, Leistungszeit: fremder Kunde → 404 ohne Wirkung, eigener Kunde → 200, unbekannte ID → Handler-404). Davor: MED-D-384 (eigene Rechtsseiten): neue rechtliches.test.ts +10 (Pflichtkern inkl. Vertretung, nur https://-Overrides, Links nur bei vollständigen Angaben bzw. externe URL mit Vorrang, nur konfigurierte Angaben im Impressum, HTML-Escaping, Datenschutz-Inhalte + Turnstile/DSB/Aufsicht nur wenn konfiguriert) + oeffentlich-site.test.ts +2 (/impressum·/datenschutz auf allen Hosts, Landing verlinkt beide nur bei vollständigem Pflichtkern). Vorher: MED-D-382 (Praxis-Routen im Kunden-Scope): api/rbac.test.ts +1 (Praxis-, Gutachten-, Befund- und Dexman-Routen für fremden Kunden → 404 inkl. PATCH/POST ohne Wirkung, eigener Kunde → 200, Backoffice sieht alles, Flag aus = keine Regression). Davor: MED-D-383 (formelle Fachsprache, keine neuen Tests): domain/dexman-kern.test.ts, api/dexman.test.ts, e2e/dexman.spec.ts, e2e-fullstack/dexman-flow.spec.ts auf die neuen Texte angepasst; Kern-Test prüft zusätzlich die Statusbegriffe je Bereich. Davor: MED-D-381 (Dexman umgesetzt, zwei Oberflächen): neue domain/dexman-kern.test.ts +25 (Rechenkern aus shared/dexman-kern/: Monat/Format, Umsatz/Personal/Material/Fixkosten, Tageslinie, Ist vor Plan, Ein-/Austritt, Gehaltsänderung + Umsatzbeteiligung, Saison/Umsatzänderung, zuflussbasiert, Darlehen fallend/Annuität/Zinsbindung/privat/vermietet/Laufzeitende, Anschaffung/AfA, Ereignisse, Kontokorrentzins, Beispielpraxis deterministisch, Sätze, Hebel, Szenario ohne Seiteneffekt, Prüfregeln) + neue api/dexman.test.ts +8 (Stand/Lage, Versionen append-only, Archivieren, Ist in praxis_jahreswert mit Vorrang, Hebel → Wiedervorlage idempotent, PII-armes Audit + Lesezugriff, HTTP inkl. Rechte je Kunde); domain/zugriff.test.ts um dexman_planung erweitert; neue gemockte e2e/dexman.spec.ts +5 (Kompass: Satz/Tageslinie/Herkunft, Was-wäre-wenn ohne Speichern, Beratungsmodus; Klassisch: Kacheln/Personal/Monate + Umschalten, Formularprüfung) + Full-Stack e2e-fullstack/dexman-flow.spec.ts +1 (Akte → Praxen → Dexman, Ist-Erfassung persistiert, Übernehmen = neue Version, Klassisch zeigt dieselben Daten). Vorher: MED-D-337 (Unterschrift über den Assistenten): api/dokumentgenerierung.test.ts +2 (inkl. wiederherstellbare Verknüpfung) + api/individualdokument.test.ts +1 (Assistenten-Weg führt Quelle nach: Status/Audit, Stufen-Vorgabe AES, zweiter Start 409); neue Full-Stack-Spec e2e-fullstack/unterschrift-generierung.spec.ts +1. Vorher: MED-D-335 (Kundenseite Slice B): domain/naechste-schritte.test.ts +1 (Klartext ohne interne IDs/Jargon); neue Full-Stack-Spec e2e-fullstack/kundenseite-einfach.spec.ts +1 (Nächster Schritt oben, ein Status, "Mehr"-Reiter, kein Unterschrift-Direktversand). Vorher: MED-D-334 (interne Oberfläche Slice A): cockpit.spec.ts +3 (Leitstand-Kopfzeile ohne Überlauf bei 1280 px, Zuweisen per Personenauswahl, Fehlermeldung mit Ursache + nächstem Schritt). Vorher: MED-D-333 (Portal-Aufgabenliste/Fragebogen seitenweise): api/portal.test.ts +3 (Aufgabenliste aus Einladung/Formular/Fragebogen/Dokument, Portal-Mail-Texte, Einladungs-Betreff); E2E portal.spec.ts +1 (Aufgabenliste-Links) + public-strecken.spec.ts +1 (Fragebogen-Assistent). Vorher: MED-D-332 (Kundenseiten Slice A): neue domain/kunden-kontakt.test.ts +3 (leer = dormant, nur http(s)-Links, Berater nur mit Anzeigename) + api/onboarding-formular.test.ts +1 (Kontext liefert Kontakt-Block, nie Roh-E-Mail der Betreuung) → 793; api/portal.test.ts/api/ausfuellformular.test.ts/api/dokumentgenerierung.test.ts an neue Labels/Config-Form angepasst; gemockt e2e/onboarding.spec.ts +2 (Kontakt-Fußzeile mit tel:-Link + fehlender Link ausgeblendet + "Zum Absenden fehlt noch"; keine leere Hilfe-Box ohne Konfig) → 33, e2e/portal.spec.ts um Upload-Button/accept/Bestätigung erweitert. Davor MED-D-331 (K-10): api/formular-vorgaenge.test.ts +2 (ohne Kunden-E-Mail keine Erinnerung/keine Markierung + Nachholen; Zustellfehler nicht-fatal, harte Eskalation läuft weiter) und bestehender Weich-Test auf Empfänger = Kontakt-E-Mail + Fortsetzungs-Link umgestellt → 789. Davor MED-D-330 (Begriffe "Kunde"/"Mandat"): keine neuen Tests, Zahl unverändert — angepasst an die neuen UI-/Fehlertexte: api/ausfuellformular.test.ts (Festwerte "Kunde"/"Kundennummer"), api/dokumentgenerierung.test.ts (Mapping "Kunde"), api/formular-vorgaenge.test.ts (Betreff "beim Kunden nachfassen"), e2e/cockpit.spec.ts + e2e/smoke.spec.ts + e2e-fullstack/smoke.spec.ts + e2e-fullstack/mandant-create.spec.ts (Überschrift "Kunden", "+ Neuer Kunde", Leerzustand). Davor MED-D-329/328 (Einfachheits-Audit): api/ausfuellformular.test.ts +2 (Korrektur-Mail an Kontakt-E-Mail mit absolutem Link, ohne E-Mail kein Versand + kundeBenachrichtigt=false, Provider-Fehler nicht-fatal) → 787; gemockt e2e/cockpit.spec.ts +3 ("Heute fällig" zeigt nur datierte überfällige/heute fällige Punkte, Leerzustand, Fokus-Kürzel Default aus) + e2e/portal.spec.ts +1 (Portal ohne Staff-Kopfzeile, data-theme=light) → 31; Full-Stack neue e2e-fullstack/timer-kein-autostart.spec.ts +1 (Öffnen startet keinen Timer, Klick startet ihn serverseitig) → 9. Davor MED-D-326 (Betriebsinfo im Footer nur für Admins): +1 gemockte E2E in client/e2e/smoke.spec.ts — Nicht-Admin (Berater) sieht keinen Betriebsinfo-Fuß (footer.mw-footer gar nicht gerendert, keine Versionsinfo). 34→35 E2E (26→27 gemockt), Server-Vitest unverändert. Davor MED-D-324 (Version/Deploy dezent in den Footer): +2 gemockte E2E in client/e2e/smoke.spec.ts — Cockpit (Route "/"): Version + Deploy stehen im footer.mw-footer, nicht mehr im header.mw-header; App-Shell (/verwaltung): Version + Deploy im footer.md-footer, nicht im header.md-header. Server-Vitest unverändert (reine Client-/CSS-Änderung). 32→34 E2E (24→26 gemockt), 785 Vitest unverändert. Davor MED-D-323 (MED-OP-MIGRATE-1: Auto-Migration restart-sicher): +3 Server-Vitest / +1 Datei — neue db/migrate.test.ts mit minimalem Fake-D1 (modelliert _meta, ALTER TABLE … ADD/DROP COLUMN-Semantik inkl. Duplikat-Erkennung, atomare batch()): Erststart erreicht die Ziel-Schema-Version; zweiter Aufruf ist ein No-op (idempotent, keine DDL erneut); Absturz mitten in der Migration → Neustart läuft sauber weiter (Version bleibt beim Vorgänger, kein duplicate column). Erste Bausteine Richtung MED-OP-TEST-1 (echtes D1-Harness). 782→785 Vitest (63→64 Dateien). Davor MED-D-322 (OP-DM-8 Mail-Selbsttest): +4 Server-Vitest in api/app.test.ts (admin→Log-Kanal zugestellt=false, keine echte Mail; echter Kanal→sende() mit der eigenen Adresse aufgerufen→zugestellt=true; Provider-Fehler→502 ok:false; Nicht-Admin (berater)→403) +3 gemockte E2E / +1 Datei (neue client/e2e/verwaltung.spec.ts: echter Kanal→Erfolgshinweis, Log-Kanal→"keine Mail zugestellt", 502→Fehlermeldung im Status-Absatz). 778→782 Vitest (Dateien unverändert 63), 29→32 E2E (21→24 gemockt, 14→15 Playwright-Dateien). Davor MED-D-320 (MED-OP-PORTAL-2 Teil 2: mehrere Kontakt-Logins je Mandant): +3 Server-Vitest in api/portal.test.ts (Staff legt mehrere Kontakte an + listet sie (sortiert), doppelte E-Mail→409 email_vergeben; Sperren beendet die laufende Session (Dashboard 200→401) + wird als gesperrt gelistet, Reaktivieren gibt Zugang zurück; Scope-Guard: fremdes Konto sperren→404, unverändert). Client (Kontakt-Liste + Sperren/Reaktivieren in mandant-einladungen) ng build-verifiziert (das Register-Panel liegt tief in mandant-detail — wie die bestehende Einladungen-UI build-only, kein eigenes E2E-Harness). 775→778 Vitest (Dateien unverändert 63), E2E unverändert (29). Davor MED-D-319 (MED-OP-UX-14: Button-CTA-Vereinheitlichung + Verlassen-Modal → md-overlay + Command-Palette-Absicherung): +3 gemockte E2E / +1 Datei — neue client/e2e/cockpit-overlays.spec.ts (Command-Palette: Öffnen über den Kopf-Button + Fokus-Pin aufs Suchfeld + Esc-Schließen; Filtern nach Eingabe + Treffer per ↓ +↵ ausführen → Theme dunkel; Backdrop-Klick schließt). Server-Vitest unverändert (reine Client-/CSS-Änderung: Primary-CTA --md-green überall, Verlassen-Modal nutzt jetzt die geteilte md-overlay-Dialogmechanik statt hand-gerollt). 26→29 E2E (18→21 gemockt, 13→14 Playwright-Dateien), 775 Vitest unverändert. Davor MED-D-318 (UI/UX-Konsistenz- & Rough-Edges-Pass): +0 Tests — reine Konsistenz-/Client-/CSS-Änderungen (Theming-Tokens, Terminologie/Labels, Ladeindikatoren, Fokus-/Doppel-Submit-Guards, labels.ts-Nachbauten zurückgeführt). Kein neues Server-Verhalten (die Landing-Farb-/Wortmarken-Änderung berührt oeffentlich-site.ts nur kosmetisch → bestehende oeffentlich-site.test.ts unverändert grün); die Client-Änderungen sind über ng build + die bestehende client/e2e-Suite (u. a. Portal-Upload) gedeckt, die Fokus-/CSS-Pfade sind ohne zusätzlichen Harness nicht sinnvoll unit-testbar. Button-/Overlay-Vereinheitlichung ist als MED-OP-UX-14 getrackt (braucht erst Visual-Regression-Deckung). 775 Vitest / 18 gemockt unverändert. Davor MED-D-317 (WIP-Landing auf medidentas.com + robots.txt/KI-Trainings-Hinweise; inkl. CodeRabbit-Nachlese #343): +11 Server-Vitest / +1 Datei — neue oeffentlich-site.test.ts (istMarketingHost Apex/www vs. App-Host; robotsTxt host-abhängig — App-Host Disallow:/ + KI-Crawler gesperrt, Marketing-Host Allow:/ + KI-Crawler gesperrt; ai.txt nennt DSM/§44b + Disallow:/; tdmrep.json reserviert /; behandleOeffentlicheSite — robots/ai/tdmrep auf allen Hosts mit X-Robots-Tag: noai/tdm-reservation:1-Headern, Apex /→WIP-Landing noindex, App-Host /→null (regulär weiter), HEAD ohne Body, nicht-GET→null, Landing verweist auf app.medidentas.com). Die CodeRabbit-Nachlese #343 (dokFarbe-Theme-Tokens --warn/--muted→--amber/--mut; per-Dokument-Upload-Set statt Einzel-Signal; Decision-Log-Backtick) ist über den bestehenden client/e2e-Portal-Upload-Test + ng build abgedeckt (reine Client-/Doku-Korrektur, kein neuer Vitest); die magic-byte-Content-Prüfung bleibt bewusst Backlog (dokumentierter Tradeoff). 764→775 Vitest (62→63 Dateien), E2E unverändert (26). Davor MED-D-316 (Portal-Dokument-Upload, MED-OP-PORTAL-2 Teil 1): +9 Server-Vitest / +1 Datei — neue domain/upload-pruefung.test.ts (+4: pruefeUpload ok · leere Datei → leer · über 15 MB → zu_gross · unerlaubte Endung inkl. Doppel-Endung script.pdf.js → typ_unzulaessig) + api/portal.test.ts (+5: Upload-Route dormant → 404 / ohne Session → 401; fremdes/unbekanntes Dokument → 404 kein Cross-Mandant-Leak; angefordertes Dokument → Upload 200 + Status erhalten + im Dashboard sichtbar; bereits geprüft → 409 nicht_offen; unzulässiger Typ → 400) + +1 gemockte E2E in client/e2e/portal.spec.ts (angefordertes Dokument im Portal hochladen → Erfolg, kein Fehler). Server-Route unter /oeffentlich/portal/dokumente/:docId/upload (Session-scoped, eigener Mandant, R2 via bestehende DokumentService.hochladen); Token-Upload-Route zusätzlich mit pruefeUpload gehärtet. 755→764 Vitest (61→62 Dateien), 25→26 E2E (17→18 gemockt, Dateien unverändert 13). Davor MED-D-315 (Portal-Zugang automatisch beim Kunden-Anlegen): +4 Server-Vitest in api/portal.test.ts (zugangSicherstellen: dormant → kein Konto/kein Send; aktiv + neue E-Mail → Konto angelegt + Invite genau einmal, zweiter Aufruf idempotent; ohne verwertbare Kontakt-E-Mail nichts; HTTP POST /api/mandanten mit E-Mail + aktivem Portal → Konto auto-angelegt + Invite). 751→755 Vitest (Dateien unverändert 61), E2E unverändert. Davor MED-D-314 (Cloudflare Email Service als einziger Mail-Provider, MailChannels raus): −6/+5 Server-Vitest (netto −1) — benachrichtigung/mailchannels.test.ts entfernt (−6); neue benachrichtigung/cloudflare-email.test.ts (+5): cloudflareEmailKonfigAusEnv-Defaults/Trim; Versand über das gemockte send_email-Binding mit From-Objekt + Magic-Link im Text-Körper; empfänger-loser Aufruf still übersprungen (kein send); Binding-Fehler wirft mit code, ohne Empfänger-PII. 752→751 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-312 (CodeRabbit-Nachlese zu #339: Login-Härtung + Mailversand-Robustheit + Rate-Limit-Retention; inkl. Round-2-Review nach "ready for review"): +6 Server-Vitest — api/staff-login.test.ts +1 (Staff-Session überschreibt keine abweichende Access-Identität: A's Session-Cookie + B's Access-Header → es gilt B/backoffice, nicht A/admin; passende Access-Identität → A/admin) + api/portal.test.ts +3 (bereinigeRateLimits löscht nur abgelaufene Fenster; Login-Mail-Zustellfehler nicht-fatal → Login-Flow läuft weiter + Audit zustellung:fehler; erfolgreiche Zustellung → zustellung:ok) + api/wiedervorlage.test.ts +2 (Erinnerungs-Item-Isolation: ein Fehlversand stoppt die übrigen nicht + bleibt retrybar; Audit-Fehler NACH erfolgreicher Zustellung → keine Wiederholung/kein Doppelversand). Übrige Fixes (MailChannels-8s-Timeout inkl. Fehler-Body-Read, PORTAL_AKTIV-Startup-Warnung, Turnstile-Token-Reset im Client) sind über die bestehenden Tests + tsc/ng build abgedeckt (die Timeout- und Client-Widget-Pfade sind ohne Netz-/Browser-Harness nicht sinnvoll unit-testbar). 746→752 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-311 (Portal-Login-Härtung: Turnstile + Rate-Limiting am öffentlichen anmelden, löst MED-OP-PORTAL-3): +3 Server-Vitest in api/portal.test.ts (GET /oeffentlich/portal/config dormant→404 / aktiv→{turnstileSitekey:null}; Rate-Limit je E-Mail — die 6. Anmeldung im 10-Min-Fenster → 429 rate_limit, eine andere E-Mail behält ihr eigenes Fenster; bei gesetztem turnstileSecret ohne Token → 400 bot_check vor dem Rate-Limit). Der neue repo.pruefeRateLimit (Tabelle rate_limit, Migration v62) ist über den Rate-Limit-Test gegen den Memory-Repo abgedeckt; Client (Turnstile-Widget auf der Portal-Login-Seite) ng build-verifiziert + portal-E2E-Login-Mock um /portal/config ergänzt. 743→746 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-310 (Portal-Go-Live: echter Mailversand MailChannels/OP-DM-8 + Kundenportal scharf): +6 Server-Vitest / +1 Datei — neue benachrichtigung/mailchannels.test.ts (gegen gemocktes fetch: mailChannelsKonfigAusEnv dormant ohne MAILCHANNELS_API_KEY; Defaults + DKIM nur bei allen drei Feldern; sende POSTet Payload + X-Api-Key, Magic-Link steht im Körper; DKIM je Personalisierung mitgesendet; Empfänger-loser Aufruf still übersprungen (kein fetch); HTTP-Fehler wirft mit Status, ohne Empfänger-PII). portal.test.ts an die korrigierte Magic-Link-Form (/portal?token= Client-Route statt API-Endpunkt) angepasst — Token wird aus dem Link gezogen und gegen den API-Endpunkt eingelöst. Client (neuer "Kundenportal"-Abschnitt in der Akte, mandant-einladungen) ng build-verifiziert; der Portal-Konto-Endpunkt ist bereits durch portal.test.ts abgedeckt. 737→743 Vitest (60→61 Dateien), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-309 (Staff-Magic-Link-Login "hinter Access", dormant): +7 Server-Vitest / +1 Datei — neue api/staff-login.test.ts (alle 3 Routen dormant→404 ohne STAFF_LOGIN_AKTIV, Golden Rule; Anmelden ohne Konto-Enumeration → unbekannte E-Mail identische ok-Antwort ohne Link; ungültige E-Mail → 400; Bootstrap-Admin m.kirchhof@drkv.com UND per Verwaltung angelegter Mitarbeiter erhalten je einen Link = zweite Nutzeranforderung; voller Magic-Link-Flow anfordern→einlösen→staff_session-Cookie + Token-Replay → 401; ungültiger Token → 401; Staff-Session etabliert die Identität hinter Access unter enforced OHNE Access-Header → /api/ich 200 Rolle=admin, ohne beides → 401, nach Logout → 401) + +3 gemockte E2E / +1 Datei — neue client/e2e/staff-login.spec.ts (ohne Token → Mitarbeiter-Anmeldeformular → Magic-Link angefordert; Token einlösen bei dormantem Login (404) → "zurzeit nicht verfügbar"; ungültiger Token (401) → "neuen Link anfordern"). Migration v61 (staff_anmelde_token/staff_session, additiv) via Memory-Repo + tsc mitverifiziert; Client staff-login.component.ts ng build-verifiziert. 730→737 Vitest (59→60 Dateien), 22→25 E2E (14→17 gemockt, 12→13 Playwright-Dateien). Davor MED-D-308 (S5-UI: Kunden-Self-Service-Portal Client-UI, dormant): +3 gemockte E2E / +1 Datei — neue client/e2e/portal.spec.ts (dormant PORTAL_AKTIV=aus → "Kundenportal zurzeit nicht verfügbar"; nicht angemeldet → Anmelde-Formular → Magic-Link angefordert; angemeldet → Dashboard zeigt Onboarding-Fortschritt/offene Items/Formular-Status + proaktive S4-Einreichung wird bestätigt), gegen die gemockten Portal-Endpunkte. Server unverändert (nur bestehende Endpunkte konsumiert); portal-public.component.ts + Portal-API-Methoden ng build-verifiziert. 19→22 E2E (11→14 gemockt, 11→12 Playwright-Dateien), 730 Vitest unverändert. Davor MED-D-307 (N5: Onboarding-Einwilligung über DocuSign statt SES-Attrappe, flag-gated/dormant, OP-SIGN-1): +3 Server-Vitest in api/onboarding-formular.test.ts (Flag an + Signatur-Dienst → Einwilligung als echte SES über den Fake-Provider angefordert/pending/PDF-Beleg, Audit einwilligung.angefordert statt .erteilt; Flag an ohne Dienst → SES-Attrappe (dormant-sicher); Provider-Fehler → consent_provider_fehler, Einladung bleibt offen — kein stiller Fallback). Client unverändert (Danke-Seite bleibt; die DocuSign-Mail ist der Folgeschritt). Der DocuSign-Pipeline-Pfad (anfordern→Envelope→Webhook→abgleich) ist bereits durch die Full-Stack-docusign-flow.spec.ts abgedeckt; der globale Fullstack-Flag bleibt aus (Onboarding-E2E prüft weiter den SES-Default). 727→730 Vitest, E2E unverändert (19). Davor MED-D-306 (kollisionssicheres Hash-Schema 3 + Ablehnung unbekannter Schema-Marker, G-4; CodeRabbit-Nachlese zu #332): +4 Server-Vitest in domain/stammdaten-version.test.ts (Schema 2 kollidiert bei =/|-Freitext, Schema 3 nicht — behebt den Trenner-Injektions-Fund; kanonischerSnapshot wirft bei unbekanntem Schema statt still zurückzufallen; pruefeVersionsKette meldet einen unbekannten hashSchema-Marker als Bruch; Umschreiben eines Bestands-Markers 2→3 wird erkannt). Server-only Härtung; Full-Stack-E2E 8/8 (schema-3-Schreibpfad gegen echten Worker) unverändert. 723→727 Vitest, E2E unverändert (19). Davor MED-D-305 (feldset-tolerante Stammdaten-Hash-Kette, G-4; die 6 Onboarding-Grunddaten jetzt versioniert): +6 Server-Vitest in domain/stammdaten-version.test.ts (neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil = Regressions-Lock der Bestands-Ketten; Schema-2 serialisiert feldset-tolerant über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation an einer Schema-1-Zeile erkannt; gemischte Kette Schema-1 + neue Schema-2-Zeile mit den 6 neuen Feldern bleibt intakt — Kern von MED-D-305; künftiges Feld-Wachstum bricht eine Schema-2-Zeile nicht, ein nachträglich eingemischtes Feld schon = Tamper-Evidenz). Client (StammdatenSnapshot/StammdatenVersion-Typen + 6 md-versioned-field-Zeilen build-verifiziert; Migration v60 von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert, 8/8 grün). 717→723 Vitest, E2E unverändert (19). Davor MED-D-304 (Kunden-Self-Service-Portal S3 Dashboard + S4 proaktive Einreichung, dormant): +5 Server-Vitest in api/portal.test.ts (S3 Dashboard dormant→404/ohne-Session→401/kompakte Sicht; S4 Vorschlag dormant→404/ohne-Session→401/keine-Whitelist-Felder→400; voller Round-Trip Einreichen→Berater-Liste→Übernahme (4-Augen, self_service→Onboarding-Regelset)→Stammdaten geschrieben; Idempotenz je Mandant). Client (Portal server-only; Berater-Sektion mandant-formulare build-verifiziert — kein e2e-Harness für diese Fläche). 712→717 Vitest, E2E unverändert (19). Davor MED-D-303 (Item↔Mandat-Zuordnung): +3 Server-Vitest + 1 Full-Stack-E2E / +1 Datei — api/app.test.ts +3 (Item einem Mandat zuordnen + wieder lösen; fremde/unbekannte Auftrags-ID → 404 kein Cross-Mandant-Verweis; fremdes Item → 404 + ungültige auftragRef → 400) + neue client/e2e-fullstack/onboarding-item-mandat.spec.ts 1 (Zuordnen → Persistenz-Roundtrip aus D1 → Lösen + Cross-Mandant-Guard, Migration v59 gegen den echten Worker). 709→712 Vitest, 18→19 E2E (7→8 Full-Stack, 10→11 Playwright-Dateien). Davor MED-D-302 (Onboarding-Greenfield G5 — dormante ausfuellformular-Tabelle + Repo-Methoden entfernt): +0 Tests — reiner Dead-Code-Abbau (die entfernten Methoden hatten seit G3E2 keine Test-Usages mehr); Migration v58 (DROP TABLE IF EXISTS ausfuellformular) wird von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert (7/7 grün, sauberer Boot). 709 Vitest unverändert. Davor MED-D-301 (Onboarding-Greenfield G4 — Item↔Vorgang-Link + Bedarf je Mandat): +2 Server-Vitest + 2 Full-Stack-E2E / +1 Datei — api/ausfuellformular.test.ts +1 (erstellen mit onboardingItemId setzt den Item-Link + auditiert ihn; zweites Anfordern desselben Items ist idempotent, ein Vorgang je Item), api/beratungsauftrag.test.ts +1 (liste liefert den abgeleiteten benoetigt-Bedarf aus dem Produkt-Playbook, G-2) + neue client/e2e-fullstack/onboarding-item-vorgang.spec.ts 2 (Item↔Vorgang-Roundtrip gegen echten Worker/D1: "Formular aus Item anfordern" → Link + Liste + idempotente Wiederverwendung; gebuchtes Mandat → benoetigt abgeleitet). 706→709 Vitest, 16→18 E2E (5→7 Full-Stack, 9→10 Playwright-Dateien). Davor MED-D-299 (Onboarding-Greenfield G3 Etappe 3a — Onboarding-Strecke auf die Feld-Engine): +0 Tests, verhaltensneutral — die Onboarding-Grunddaten rendern jetzt über PubFeldEngineComponent; die bestehende client/e2e/onboarding.spec.ts (Lücken-Report · Happy-Path bis "Vielen Dank!" · ungültiger Token) deckt die Strecke über dieselben Feld-IDs weiter ab und bleibt 11/11 grün. Davor MED-D-298 (Onboarding-Greenfield G3 Etappe 2 — Ausfüll-Flow auf den einen Service): −7 Vitest / −1 Datei — die G2-Lean-Service-Tests (api/formular-vorgang.test.ts) sind entfallen (der Lean-Service wurde durch den voll-ausfüll-fähigen FormularVorgangService ersetzt); die bestehende api/ausfuellformular.test.ts (Domäne + Service + HTTP-Fluss) testet jetzt den neuen FormularVorgangService gegen den formular_vorgang-Store (Import/Repo-Zugriffe umgestellt, identische Erwartungen = Contract-Netz); api/formular-vorgaenge.test.ts (SLA/Öffnungs-Tracking) ebenso auf den neuen Store repointet. Full-Stack-E2E (formular-sichtung.spec + DocuSign) verifiziert den Ausfüll-Flow gegen den echten Worker auf dem neuen Store (5/5). 713→706, 60→59 Dateien. Davor MED-D-297 (Onboarding-Greenfield G3 Etappe 1 — geteilte Feld-Engine): +2 gemockte E2E / +1 Datei — client/e2e/formular.spec.ts (die PubFeldEngineComponent rendert die Ausfüll-Formular-Felder inkl. Marker-Chips; Pflichtfeld füllen → Vorschau → rechtsverbindlich absenden → "Vielen Dank!" mit Beleg-Nr.; leeres Pflichtfeld → Vorschau blockiert mit ehrlichem Hinweis). Server-Vitest unverändert (reine Client-Konsolidierung). 14→16 E2E, 8→9 Playwright-Dateien. Davor MED-D-296 (Onboarding-Greenfield G2 — eine FormularVorgang-Entität + Service): +8 Server-Vitest / +1 Datei — api/formular-vorgang.test.ts 7 (schema-getriebener Lebenszyklus: erstellen inkl. Item-Link + Audit · kontext/Öffnungs-Tracking · einreichen mit Pflichtprüfung/Prüfsumme + Doppel-Einreichung-Konflikt · Abschnitts-Freigabe N2 idempotent · Feld-Ablehnung → Korrektur-Schleife inkl. Rücksetzen bei Re-Einreichung · Wächter · Integration mit dem G1-Adapter-Schema) + domain/feld.test.ts +1 (fehlendePflicht: Wert ODER Markierung erfüllt Pflicht, DM-9). Migration v57 (formular_vorgang) via Full-Stack-E2E gegen den echten Worker verifiziert (5/5, sauberer Boot). 705→713, 59→60 Dateien. Davor MED-D-295 (Onboarding-Greenfield G1 — kanonisches Feld-Modell): +9 Server-Vitest / +2 Dateien — domain/feld.test.ts 5 (reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt + Schema-Konsistenz-Invariante schemaVerstoesse) + domain/formular-schema.test.ts 4 (die 3 Adapter überführen die Alt-Kataloge treu: Onboarding pflicht:bool→wichtigkeit+select→auswahl, Ausfüll-KundenFeld 1:1, Fragebogen klasse→wichtigkeit+block→Abschnitt+Öffnungszeiten-strukturTyp; Feldzahl erhalten, jedes Schema konsistent). 696→705, 57→59 Dateien. Davor MED-D-293 (Onboarding-Vereinfachung N1, 1. Schritt — geteilte Bausteine der öffentlichen Kundenstrecken): +0 Tests, verhaltensneutral — die wortgleich kopierte Lade-Fehler-Auswertung + Turnstile-Callback-Bindung sind jetzt geteilte Bausteine in pub-bausteine.ts (ladeFehlerAuswerten/bindeTurnstileCallback); die bestehende gemockte E2E-Suite (client/e2e/, u. a. "ungültiger Token"/"ungültige Fragebogen-Einladung") deckt die Fehler-/Zugriffsschutz-Pfade der 4 Public-Strecken ab und bleibt 9/9 grün (Client hat keine Unit-Test-Infrastruktur). Davor MED-D-292 (Onboarding-Vereinfachung N2, Abschnitts-Freigabe): +4 Server-Vitest (domain/feld-sichtung.test.ts +1 akzeptiereOffene — akzeptiert nur offene Felder, lässt eine Ablehnung mit Grund unangetastet, idempotent; api/ausfuellformular.test.ts +3 alleAkzeptieren — 1-Klick-Freigabe akzeptiert alle offenen + Audit PII-arm, zweiter Aufruf nichts_offen, eine vorherige Ablehnung bleibt bestehen und öffnet beim Abschließen die Korrektur-Schleife, nur für eingereichte Formulare) + +1 Full-Stack-E2E (client/e2e-fullstack/formular-sichtung.spec.ts: einreichen → Abschnitt in 1 Klick freigeben → abschließen gegen den echten Worker; deckt den atomaren read-modify-write der Sichtung in D1 ab). 692→696 Vitest, 13→14 Playwright (7→8 Dateien). Davor MED-D-290 (Full-Stack-E2E gegen den echten Worker, löst MED-OP-E2E-1 ein): +4 Playwright-Tests in client/e2e-fullstack/ gegen den ECHTEN Worker (wrangler dev · workerd · lokale D1 + R2 · SIGNATUR_PROVIDER=fake) statt gemockter API — App-Shell-Smoke + Health, Mandant anlegen → persistiert → Reload (echter Browser→Worker→D1-Pfad), DocuSign-Flow (Mandant→Onboarding→Ablage provisionieren→Datei ablegen→zur Unterschrift senden→Vor-Ort-/Demo-Abschluss→Dokument "erhalten" + signierte Rückablage; echter D1/R2-Write-back + Provider-Lebenszyklus). Ein Origin bedient UI + /api (wie Produktion), Overrides nur fürs Testen (AUTH_ENFORCED=false, Fake-Signatur). CI-Job client-e2e-fullstack (gated bei Client- oder Server-Änderung, Teil des Pflicht-Checks checks). Deckt den echten Datenpfad ab (Migration, D1-SQL, R2-Ablage), den die In-Process-Vitest nicht durchläuft. Davor MED-D-289 (E2E-/UI-Test-Infrastruktur, gemockte API): +9 Playwright-Tests in client/e2e/ — App-Shell-Smoke (bootet + Marke/Cockpit + Navigation), Cockpit (Mandantenliste rendert gelieferte Mandanten / ehrlicher Leerzustand), Onboarding-Kundenstrecke (3-Schritte-Flow: Lücken-Report bei fehlender Pflichtangabe · vollständiger Weg bis "Vielen Dank!" · ungültiger Token), öffentliche Strecken (Lead-Formular rendert · ungültige Fragebogen-Einladung). Sie laufen gegen den fertigen Angular-Build (dist/browser via e2e/static-server.mjs) mit gemockter API (Playwright-route, e2e/support/mock-api.ts) — deterministisch, ohne Worker/D1/Secrets. In CI als client-e2e-Job (gated wie client-build, Teil des Pflicht-Checks checks, "immer laufen lassen"). Erste Client-Test-Infrastruktur überhaupt — löst die bisherige "nur ng build"-Lücke (MED-D-267 ff.). Volllast-E2E gegen echten Worker + D1 (wrangler) ist als Folge-Slice MED-OP-E2E-1 notiert. Server-Vitest 692 unverändert (reine Client-/CI-Ergänzung). Davor MED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem "wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Test — domain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Tests — api/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Tests — api/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → "nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte "Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistik → ng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Tests — signatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Tests — api/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Tests — auth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Tests — api/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Tests — api/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Tests — api/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Tests — api/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Tests — api/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 "keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + "Zu fakturieren"-Filter): +1 Test — api/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Tests — api/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Tests — domain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Tests — domain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Tests — Davor: MED-D-690 (Formular-Raster .md-form + Entitäts-Karte .md-entity im Register „Praxen / Gesellschaften“): neue e2e-fullstack/praxen.spec.ts +1 (Leerzustand, Anlegen hinter Umschalter mit Fokus aufs Pflichtfeld, sichtbare Labels je Pflegefeld der Karte, Formular schließt nach dem Anlegen, Inline-Pflege persistiert echt in D1). Davor: MED-D-654 (Erinnerungs-Schalter aus der Akte): e2e-fullstack/onboarding-item-vorgang.spec.ts Erinnerungs-Test umgestellt (kein Schalter/Hinweis mehr; ausgeschaltet → leiser Stand in den Basis-Dokumenten), Zahl unverändert. Davor: MED-D-652 (Dexman-Monatsaufgaben): neue api/dexman-monatsaufgaben.test.ts +3 (Vormonat inkl. Jahreswechsel; nur laufende Dexman-Mandate, Fälligkeit 5./10./15.; Cron legt je Monat genau einmal an, gebunden an das Mandat). Davor: MED-D-649 (Folgeaufgaben für Dexman-Mandate): api/beratungsauftrag.test.ts (Playbook-Test prüft jetzt alle buchbaren Produkte + die vier Dexman-Aufgaben mit Fristen, Zahl unverändert), e2e-fullstack/dexman-flow.spec.ts prüft die Wiedervorlagen beim Buchen in echter D1. Davor: MED-D-646 (Standard-Unterlagen für Dexman-Mandate): domain/unterlagen-vorlagen.test.ts +1 (vier Unterlagen, eindeutige stabile IDs, idempotentes Seed-SQL v68), e2e-fullstack/dexman-flow.spec.ts prüft die Unterlagen beim Buchen in echter D1. Davor: MED-D-643 (Dexman-Controlling buchbar): api/beratungsauftrag.test.ts (Ablehnungs-Test durch "buchbar, an die Praxis gebunden" ersetzt, Zahl unverändert), api/lead.test.ts (Lead bietet genau die buchbaren Produkte an), e2e-fullstack/dexman-flow.spec.ts +1 (Mandat buchen → "Dexman öffnen" zeigt Dexman für die Praxis). Davor: MED-D-638 (Kunden-Erinnerung an fehlende Unterlagen): neue api/unterlagen-erinnerung.test.ts +5 (offene Unterlagen ohne Aufgaben/Dubletten; nächste Aktion; 7-Tage-Takt mit max. 3 Mails und genau einer Wiedervorlage; Zyklus-Ende/abgeschaltet/dormant ohne Portal; HTTP Stand + Schalter), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Schalter in der Akte persistiert in D1). Davor: MED-D-635/637/639 (Basis-Dokumente, Einmal-Link entfällt, 4-Stand-Schieber): api/portal.test.ts +1 (Onboarding-Einladung entsteht automatisch und idempotent über das Portal-Dashboard), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Überschrift „Basis-Dokumente", kein Einmal-Link-Abschnitt, „Entfällt" im Schieber und zurück). Davor: CodeRabbit-Nachlese #627: api/beratungsauftrag.test.ts +3 (abgelaufene Unterlage zählt als fehlend, unterschriebene nicht; eigenes Flag unterlagenFehler; Rollen-Update ohne team lässt das Team unverändert). Davor: MED-D-630 R-13 (Standard-Mandats-Unterlagen per Migration v66): neue domain/unterlagen-vorlagen.test.ts +2 (nur buchbare Produkte, stabile IDs, idempotentes Seed-SQL); e2e-fullstack/mandats-unterlagen.spec.ts prüft zusätzlich die Standard-Vorlage in echter D1. Davor: MED-D-630 Slice 2 (Mandats-Unterlagen je Produkt): api/beratungsauftrag.test.ts +4 (Reife mit fehlenden Unterlagen, Anlage aus Vorlage idempotent + auditiert, Unterlage ergänzen mandant-scoped, HTTP: Pflege nur Admin/Validierung/Löschen), e2e/verwaltung.spec.ts +1 (Vorlagen je Produkt), neue e2e-fullstack/mandats-unterlagen.spec.ts +1 (Vorlage → Anforderung im Mandat, abhaken persistiert, ergänzen). Davor: MED-D-630 (Onboarding als Abhak-Liste): e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Kundenakte zeigt Basis-Unterlagen ohne Mandat-Auswahl und ohne "Formular anfordern"). Davor: MED-D-629 (Teams + "Nicht zugewiesen"): domain/rechte.test.ts +2 (Team-Sicht, normalisiereTeam), api/rbac.test.ts +1 (Team setzen nur Admin, Team-Kunde sehen/bearbeiten, anderes Team 404, Team entfernen), e2e/verwaltung.spec.ts +1 (Team je Benutzer, "Chef / Backoffice"), e2e/cockpit.spec.ts +1 (Filter "Nicht zugewiesen"). Davor: MED-D-623 (übrige By-ID-Routen im Kunden-Scope): api/rbac.test.ts +1 (Dokumente, Bankkonten, Onboarding-Items, Signaturen, Dokumentgenerierung, Leistungszeit: fremder Kunde → 404 ohne Wirkung, eigener Kunde → 200, unbekannte ID → Handler-404). Davor: MED-D-384 (eigene Rechtsseiten): neue rechtliches.test.ts +10 (Pflichtkern inkl. Vertretung, nur https://-Overrides, Links nur bei vollständigen Angaben bzw. externe URL mit Vorrang, nur konfigurierte Angaben im Impressum, HTML-Escaping, Datenschutz-Inhalte + Turnstile/DSB/Aufsicht nur wenn konfiguriert) + oeffentlich-site.test.ts +2 (/impressum·/datenschutz auf allen Hosts, Landing verlinkt beide nur bei vollständigem Pflichtkern). Vorher: MED-D-382 (Praxis-Routen im Kunden-Scope): api/rbac.test.ts +1 (Praxis-, Gutachten-, Befund- und Dexman-Routen für fremden Kunden → 404 inkl. PATCH/POST ohne Wirkung, eigener Kunde → 200, Backoffice sieht alles, Flag aus = keine Regression). Davor: MED-D-383 (formelle Fachsprache, keine neuen Tests): domain/dexman-kern.test.ts, api/dexman.test.ts, e2e/dexman.spec.ts, e2e-fullstack/dexman-flow.spec.ts auf die neuen Texte angepasst; Kern-Test prüft zusätzlich die Statusbegriffe je Bereich. Davor: MED-D-381 (Dexman umgesetzt, zwei Oberflächen): neue domain/dexman-kern.test.ts +25 (Rechenkern aus shared/dexman-kern/: Monat/Format, Umsatz/Personal/Material/Fixkosten, Tageslinie, Ist vor Plan, Ein-/Austritt, Gehaltsänderung + Umsatzbeteiligung, Saison/Umsatzänderung, zuflussbasiert, Darlehen fallend/Annuität/Zinsbindung/privat/vermietet/Laufzeitende, Anschaffung/AfA, Ereignisse, Kontokorrentzins, Beispielpraxis deterministisch, Sätze, Hebel, Szenario ohne Seiteneffekt, Prüfregeln) + neue api/dexman.test.ts +8 (Stand/Lage, Versionen append-only, Archivieren, Ist in praxis_jahreswert mit Vorrang, Hebel → Wiedervorlage idempotent, PII-armes Audit + Lesezugriff, HTTP inkl. Rechte je Kunde); domain/zugriff.test.ts um dexman_planung erweitert; neue gemockte e2e/dexman.spec.ts +5 (Kompass: Satz/Tageslinie/Herkunft, Was-wäre-wenn ohne Speichern, Beratungsmodus; Klassisch: Kacheln/Personal/Monate + Umschalten, Formularprüfung) + Full-Stack e2e-fullstack/dexman-flow.spec.ts +1 (Akte → Praxen → Dexman, Ist-Erfassung persistiert, Übernehmen = neue Version, Klassisch zeigt dieselben Daten). Vorher: MED-D-337 (Unterschrift über den Assistenten): api/dokumentgenerierung.test.ts +2 (inkl. wiederherstellbare Verknüpfung) + api/individualdokument.test.ts +1 (Assistenten-Weg führt Quelle nach: Status/Audit, Stufen-Vorgabe AES, zweiter Start 409); neue Full-Stack-Spec e2e-fullstack/unterschrift-generierung.spec.ts +1. Vorher: MED-D-335 (Kundenseite Slice B): domain/naechste-schritte.test.ts +1 (Klartext ohne interne IDs/Jargon); neue Full-Stack-Spec e2e-fullstack/kundenseite-einfach.spec.ts +1 (Nächster Schritt oben, ein Status, "Mehr"-Reiter, kein Unterschrift-Direktversand). Vorher: MED-D-334 (interne Oberfläche Slice A): cockpit.spec.ts +3 (Leitstand-Kopfzeile ohne Überlauf bei 1280 px, Zuweisen per Personenauswahl, Fehlermeldung mit Ursache + nächstem Schritt). Vorher: MED-D-333 (Portal-Aufgabenliste/Fragebogen seitenweise): api/portal.test.ts +3 (Aufgabenliste aus Einladung/Formular/Fragebogen/Dokument, Portal-Mail-Texte, Einladungs-Betreff); E2E portal.spec.ts +1 (Aufgabenliste-Links) + public-strecken.spec.ts +1 (Fragebogen-Assistent). Vorher: MED-D-332 (Kundenseiten Slice A): neue domain/kunden-kontakt.test.ts +3 (leer = dormant, nur http(s)-Links, Berater nur mit Anzeigename) + api/onboarding-formular.test.ts +1 (Kontext liefert Kontakt-Block, nie Roh-E-Mail der Betreuung) → 793; api/portal.test.ts/api/ausfuellformular.test.ts/api/dokumentgenerierung.test.ts an neue Labels/Config-Form angepasst; gemockt e2e/onboarding.spec.ts +2 (Kontakt-Fußzeile mit tel:-Link + fehlender Link ausgeblendet + "Zum Absenden fehlt noch"; keine leere Hilfe-Box ohne Konfig) → 33, e2e/portal.spec.ts um Upload-Button/accept/Bestätigung erweitert. Davor MED-D-331 (K-10): api/formular-vorgaenge.test.ts +2 (ohne Kunden-E-Mail keine Erinnerung/keine Markierung + Nachholen; Zustellfehler nicht-fatal, harte Eskalation läuft weiter) und bestehender Weich-Test auf Empfänger = Kontakt-E-Mail + Fortsetzungs-Link umgestellt → 789. Davor MED-D-330 (Begriffe "Kunde"/"Mandat"): keine neuen Tests, Zahl unverändert — angepasst an die neuen UI-/Fehlertexte: api/ausfuellformular.test.ts (Festwerte "Kunde"/"Kundennummer"), api/dokumentgenerierung.test.ts (Mapping "Kunde"), api/formular-vorgaenge.test.ts (Betreff "beim Kunden nachfassen"), e2e/cockpit.spec.ts + e2e/smoke.spec.ts + e2e-fullstack/smoke.spec.ts + e2e-fullstack/mandant-create.spec.ts (Überschrift "Kunden", "+ Neuer Kunde", Leerzustand). Davor MED-D-329/328 (Einfachheits-Audit): api/ausfuellformular.test.ts +2 (Korrektur-Mail an Kontakt-E-Mail mit absolutem Link, ohne E-Mail kein Versand + kundeBenachrichtigt=false, Provider-Fehler nicht-fatal) → 787; gemockt e2e/cockpit.spec.ts +3 ("Heute fällig" zeigt nur datierte überfällige/heute fällige Punkte, Leerzustand, Fokus-Kürzel Default aus) + e2e/portal.spec.ts +1 (Portal ohne Staff-Kopfzeile, data-theme=light) → 31; Full-Stack neue e2e-fullstack/timer-kein-autostart.spec.ts +1 (Öffnen startet keinen Timer, Klick startet ihn serverseitig) → 9. Davor MED-D-326 (Betriebsinfo im Footer nur für Admins): +1 gemockte E2E in client/e2e/smoke.spec.ts — Nicht-Admin (Berater) sieht keinen Betriebsinfo-Fuß (footer.mw-footer gar nicht gerendert, keine Versionsinfo). 34→35 E2E (26→27 gemockt), Server-Vitest unverändert. Davor MED-D-324 (Version/Deploy dezent in den Footer): +2 gemockte E2E in client/e2e/smoke.spec.ts — Cockpit (Route "/"): Version + Deploy stehen im footer.mw-footer, nicht mehr im header.mw-header; App-Shell (/verwaltung): Version + Deploy im footer.md-footer, nicht im header.md-header. Server-Vitest unverändert (reine Client-/CSS-Änderung). 32→34 E2E (24→26 gemockt), 785 Vitest unverändert. Davor MED-D-323 (MED-OP-MIGRATE-1: Auto-Migration restart-sicher): +3 Server-Vitest / +1 Datei — neue db/migrate.test.ts mit minimalem Fake-D1 (modelliert _meta, ALTER TABLE … ADD/DROP COLUMN-Semantik inkl. Duplikat-Erkennung, atomare batch()): Erststart erreicht die Ziel-Schema-Version; zweiter Aufruf ist ein No-op (idempotent, keine DDL erneut); Absturz mitten in der Migration → Neustart läuft sauber weiter (Version bleibt beim Vorgänger, kein duplicate column). Erste Bausteine Richtung MED-OP-TEST-1 (echtes D1-Harness). 782→785 Vitest (63→64 Dateien). Davor MED-D-322 (OP-DM-8 Mail-Selbsttest): +4 Server-Vitest in api/app.test.ts (admin→Log-Kanal zugestellt=false, keine echte Mail; echter Kanal→sende() mit der eigenen Adresse aufgerufen→zugestellt=true; Provider-Fehler→502 ok:false; Nicht-Admin (berater)→403) +3 gemockte E2E / +1 Datei (neue client/e2e/verwaltung.spec.ts: echter Kanal→Erfolgshinweis, Log-Kanal→"keine Mail zugestellt", 502→Fehlermeldung im Status-Absatz). 778→782 Vitest (Dateien unverändert 63), 29→32 E2E (21→24 gemockt, 14→15 Playwright-Dateien). Davor MED-D-320 (MED-OP-PORTAL-2 Teil 2: mehrere Kontakt-Logins je Mandant): +3 Server-Vitest in api/portal.test.ts (Staff legt mehrere Kontakte an + listet sie (sortiert), doppelte E-Mail→409 email_vergeben; Sperren beendet die laufende Session (Dashboard 200→401) + wird als gesperrt gelistet, Reaktivieren gibt Zugang zurück; Scope-Guard: fremdes Konto sperren→404, unverändert). Client (Kontakt-Liste + Sperren/Reaktivieren in mandant-einladungen) ng build-verifiziert (das Register-Panel liegt tief in mandant-detail — wie die bestehende Einladungen-UI build-only, kein eigenes E2E-Harness). 775→778 Vitest (Dateien unverändert 63), E2E unverändert (29). Davor MED-D-319 (MED-OP-UX-14: Button-CTA-Vereinheitlichung + Verlassen-Modal → md-overlay + Command-Palette-Absicherung): +3 gemockte E2E / +1 Datei — neue client/e2e/cockpit-overlays.spec.ts (Command-Palette: Öffnen über den Kopf-Button + Fokus-Pin aufs Suchfeld + Esc-Schließen; Filtern nach Eingabe + Treffer per ↓ +↵ ausführen → Theme dunkel; Backdrop-Klick schließt). Server-Vitest unverändert (reine Client-/CSS-Änderung: Primary-CTA --md-green überall, Verlassen-Modal nutzt jetzt die geteilte md-overlay-Dialogmechanik statt hand-gerollt). 26→29 E2E (18→21 gemockt, 13→14 Playwright-Dateien), 775 Vitest unverändert. Davor MED-D-318 (UI/UX-Konsistenz- & Rough-Edges-Pass): +0 Tests — reine Konsistenz-/Client-/CSS-Änderungen (Theming-Tokens, Terminologie/Labels, Ladeindikatoren, Fokus-/Doppel-Submit-Guards, labels.ts-Nachbauten zurückgeführt). Kein neues Server-Verhalten (die Landing-Farb-/Wortmarken-Änderung berührt oeffentlich-site.ts nur kosmetisch → bestehende oeffentlich-site.test.ts unverändert grün); die Client-Änderungen sind über ng build + die bestehende client/e2e-Suite (u. a. Portal-Upload) gedeckt, die Fokus-/CSS-Pfade sind ohne zusätzlichen Harness nicht sinnvoll unit-testbar. Button-/Overlay-Vereinheitlichung ist als MED-OP-UX-14 getrackt (braucht erst Visual-Regression-Deckung). 775 Vitest / 18 gemockt unverändert. Davor MED-D-317 (WIP-Landing auf medidentas.com + robots.txt/KI-Trainings-Hinweise; inkl. CodeRabbit-Nachlese #343): +11 Server-Vitest / +1 Datei — neue oeffentlich-site.test.ts (istMarketingHost Apex/www vs. App-Host; robotsTxt host-abhängig — App-Host Disallow:/ + KI-Crawler gesperrt, Marketing-Host Allow:/ + KI-Crawler gesperrt; ai.txt nennt DSM/§44b + Disallow:/; tdmrep.json reserviert /; behandleOeffentlicheSite — robots/ai/tdmrep auf allen Hosts mit X-Robots-Tag: noai/tdm-reservation:1-Headern, Apex /→WIP-Landing noindex, App-Host /→null (regulär weiter), HEAD ohne Body, nicht-GET→null, Landing verweist auf app.medidentas.com). Die CodeRabbit-Nachlese #343 (dokFarbe-Theme-Tokens --warn/--muted→--amber/--mut; per-Dokument-Upload-Set statt Einzel-Signal; Decision-Log-Backtick) ist über den bestehenden client/e2e-Portal-Upload-Test + ng build abgedeckt (reine Client-/Doku-Korrektur, kein neuer Vitest); die magic-byte-Content-Prüfung bleibt bewusst Backlog (dokumentierter Tradeoff). 764→775 Vitest (62→63 Dateien), E2E unverändert (26). Davor MED-D-316 (Portal-Dokument-Upload, MED-OP-PORTAL-2 Teil 1): +9 Server-Vitest / +1 Datei — neue domain/upload-pruefung.test.ts (+4: pruefeUpload ok · leere Datei → leer · über 15 MB → zu_gross · unerlaubte Endung inkl. Doppel-Endung script.pdf.js → typ_unzulaessig) + api/portal.test.ts (+5: Upload-Route dormant → 404 / ohne Session → 401; fremdes/unbekanntes Dokument → 404 kein Cross-Mandant-Leak; angefordertes Dokument → Upload 200 + Status erhalten + im Dashboard sichtbar; bereits geprüft → 409 nicht_offen; unzulässiger Typ → 400) + +1 gemockte E2E in client/e2e/portal.spec.ts (angefordertes Dokument im Portal hochladen → Erfolg, kein Fehler). Server-Route unter /oeffentlich/portal/dokumente/:docId/upload (Session-scoped, eigener Mandant, R2 via bestehende DokumentService.hochladen); Token-Upload-Route zusätzlich mit pruefeUpload gehärtet. 755→764 Vitest (61→62 Dateien), 25→26 E2E (17→18 gemockt, Dateien unverändert 13). Davor MED-D-315 (Portal-Zugang automatisch beim Kunden-Anlegen): +4 Server-Vitest in api/portal.test.ts (zugangSicherstellen: dormant → kein Konto/kein Send; aktiv + neue E-Mail → Konto angelegt + Invite genau einmal, zweiter Aufruf idempotent; ohne verwertbare Kontakt-E-Mail nichts; HTTP POST /api/mandanten mit E-Mail + aktivem Portal → Konto auto-angelegt + Invite). 751→755 Vitest (Dateien unverändert 61), E2E unverändert. Davor MED-D-314 (Cloudflare Email Service als einziger Mail-Provider, MailChannels raus): −6/+5 Server-Vitest (netto −1) — benachrichtigung/mailchannels.test.ts entfernt (−6); neue benachrichtigung/cloudflare-email.test.ts (+5): cloudflareEmailKonfigAusEnv-Defaults/Trim; Versand über das gemockte send_email-Binding mit From-Objekt + Magic-Link im Text-Körper; empfänger-loser Aufruf still übersprungen (kein send); Binding-Fehler wirft mit code, ohne Empfänger-PII. 752→751 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-312 (CodeRabbit-Nachlese zu #339: Login-Härtung + Mailversand-Robustheit + Rate-Limit-Retention; inkl. Round-2-Review nach "ready for review"): +6 Server-Vitest — api/staff-login.test.ts +1 (Staff-Session überschreibt keine abweichende Access-Identität: A's Session-Cookie + B's Access-Header → es gilt B/backoffice, nicht A/admin; passende Access-Identität → A/admin) + api/portal.test.ts +3 (bereinigeRateLimits löscht nur abgelaufene Fenster; Login-Mail-Zustellfehler nicht-fatal → Login-Flow läuft weiter + Audit zustellung:fehler; erfolgreiche Zustellung → zustellung:ok) + api/wiedervorlage.test.ts +2 (Erinnerungs-Item-Isolation: ein Fehlversand stoppt die übrigen nicht + bleibt retrybar; Audit-Fehler NACH erfolgreicher Zustellung → keine Wiederholung/kein Doppelversand). Übrige Fixes (MailChannels-8s-Timeout inkl. Fehler-Body-Read, PORTAL_AKTIV-Startup-Warnung, Turnstile-Token-Reset im Client) sind über die bestehenden Tests + tsc/ng build abgedeckt (die Timeout- und Client-Widget-Pfade sind ohne Netz-/Browser-Harness nicht sinnvoll unit-testbar). 746→752 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-311 (Portal-Login-Härtung: Turnstile + Rate-Limiting am öffentlichen anmelden, löst MED-OP-PORTAL-3): +3 Server-Vitest in api/portal.test.ts (GET /oeffentlich/portal/config dormant→404 / aktiv→{turnstileSitekey:null}; Rate-Limit je E-Mail — die 6. Anmeldung im 10-Min-Fenster → 429 rate_limit, eine andere E-Mail behält ihr eigenes Fenster; bei gesetztem turnstileSecret ohne Token → 400 bot_check vor dem Rate-Limit). Der neue repo.pruefeRateLimit (Tabelle rate_limit, Migration v62) ist über den Rate-Limit-Test gegen den Memory-Repo abgedeckt; Client (Turnstile-Widget auf der Portal-Login-Seite) ng build-verifiziert + portal-E2E-Login-Mock um /portal/config ergänzt. 743→746 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-310 (Portal-Go-Live: echter Mailversand MailChannels/OP-DM-8 + Kundenportal scharf): +6 Server-Vitest / +1 Datei — neue benachrichtigung/mailchannels.test.ts (gegen gemocktes fetch: mailChannelsKonfigAusEnv dormant ohne MAILCHANNELS_API_KEY; Defaults + DKIM nur bei allen drei Feldern; sende POSTet Payload + X-Api-Key, Magic-Link steht im Körper; DKIM je Personalisierung mitgesendet; Empfänger-loser Aufruf still übersprungen (kein fetch); HTTP-Fehler wirft mit Status, ohne Empfänger-PII). portal.test.ts an die korrigierte Magic-Link-Form (/portal?token= Client-Route statt API-Endpunkt) angepasst — Token wird aus dem Link gezogen und gegen den API-Endpunkt eingelöst. Client (neuer "Kundenportal"-Abschnitt in der Akte, mandant-einladungen) ng build-verifiziert; der Portal-Konto-Endpunkt ist bereits durch portal.test.ts abgedeckt. 737→743 Vitest (60→61 Dateien), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-309 (Staff-Magic-Link-Login "hinter Access", dormant): +7 Server-Vitest / +1 Datei — neue api/staff-login.test.ts (alle 3 Routen dormant→404 ohne STAFF_LOGIN_AKTIV, Golden Rule; Anmelden ohne Konto-Enumeration → unbekannte E-Mail identische ok-Antwort ohne Link; ungültige E-Mail → 400; Bootstrap-Admin m.kirchhof@drkv.com UND per Verwaltung angelegter Mitarbeiter erhalten je einen Link = zweite Nutzeranforderung; voller Magic-Link-Flow anfordern→einlösen→staff_session-Cookie + Token-Replay → 401; ungültiger Token → 401; Staff-Session etabliert die Identität hinter Access unter enforced OHNE Access-Header → /api/ich 200 Rolle=admin, ohne beides → 401, nach Logout → 401) + +3 gemockte E2E / +1 Datei — neue client/e2e/staff-login.spec.ts (ohne Token → Mitarbeiter-Anmeldeformular → Magic-Link angefordert; Token einlösen bei dormantem Login (404) → "zurzeit nicht verfügbar"; ungültiger Token (401) → "neuen Link anfordern"). Migration v61 (staff_anmelde_token/staff_session, additiv) via Memory-Repo + tsc mitverifiziert; Client staff-login.component.ts ng build-verifiziert. 730→737 Vitest (59→60 Dateien), 22→25 E2E (14→17 gemockt, 12→13 Playwright-Dateien). Davor MED-D-308 (S5-UI: Kunden-Self-Service-Portal Client-UI, dormant): +3 gemockte E2E / +1 Datei — neue client/e2e/portal.spec.ts (dormant PORTAL_AKTIV=aus → "Kundenportal zurzeit nicht verfügbar"; nicht angemeldet → Anmelde-Formular → Magic-Link angefordert; angemeldet → Dashboard zeigt Onboarding-Fortschritt/offene Items/Formular-Status + proaktive S4-Einreichung wird bestätigt), gegen die gemockten Portal-Endpunkte. Server unverändert (nur bestehende Endpunkte konsumiert); portal-public.component.ts + Portal-API-Methoden ng build-verifiziert. 19→22 E2E (11→14 gemockt, 11→12 Playwright-Dateien), 730 Vitest unverändert. Davor MED-D-307 (N5: Onboarding-Einwilligung über DocuSign statt SES-Attrappe, flag-gated/dormant, OP-SIGN-1): +3 Server-Vitest in api/onboarding-formular.test.ts (Flag an + Signatur-Dienst → Einwilligung als echte SES über den Fake-Provider angefordert/pending/PDF-Beleg, Audit einwilligung.angefordert statt .erteilt; Flag an ohne Dienst → SES-Attrappe (dormant-sicher); Provider-Fehler → consent_provider_fehler, Einladung bleibt offen — kein stiller Fallback). Client unverändert (Danke-Seite bleibt; die DocuSign-Mail ist der Folgeschritt). Der DocuSign-Pipeline-Pfad (anfordern→Envelope→Webhook→abgleich) ist bereits durch die Full-Stack-docusign-flow.spec.ts abgedeckt; der globale Fullstack-Flag bleibt aus (Onboarding-E2E prüft weiter den SES-Default). 727→730 Vitest, E2E unverändert (19). Davor MED-D-306 (kollisionssicheres Hash-Schema 3 + Ablehnung unbekannter Schema-Marker, G-4; CodeRabbit-Nachlese zu #332): +4 Server-Vitest in domain/stammdaten-version.test.ts (Schema 2 kollidiert bei =/|-Freitext, Schema 3 nicht — behebt den Trenner-Injektions-Fund; kanonischerSnapshot wirft bei unbekanntem Schema statt still zurückzufallen; pruefeVersionsKette meldet einen unbekannten hashSchema-Marker als Bruch; Umschreiben eines Bestands-Markers 2→3 wird erkannt). Server-only Härtung; Full-Stack-E2E 8/8 (schema-3-Schreibpfad gegen echten Worker) unverändert. 723→727 Vitest, E2E unverändert (19). Davor MED-D-305 (feldset-tolerante Stammdaten-Hash-Kette, G-4; die 6 Onboarding-Grunddaten jetzt versioniert): +6 Server-Vitest in domain/stammdaten-version.test.ts (neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil = Regressions-Lock der Bestands-Ketten; Schema-2 serialisiert feldset-tolerant über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation an einer Schema-1-Zeile erkannt; gemischte Kette Schema-1 + neue Schema-2-Zeile mit den 6 neuen Feldern bleibt intakt — Kern von MED-D-305; künftiges Feld-Wachstum bricht eine Schema-2-Zeile nicht, ein nachträglich eingemischtes Feld schon = Tamper-Evidenz). Client (StammdatenSnapshot/StammdatenVersion-Typen + 6 md-versioned-field-Zeilen build-verifiziert; Migration v60 von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert, 8/8 grün). 717→723 Vitest, E2E unverändert (19). Davor MED-D-304 (Kunden-Self-Service-Portal S3 Dashboard + S4 proaktive Einreichung, dormant): +5 Server-Vitest in api/portal.test.ts (S3 Dashboard dormant→404/ohne-Session→401/kompakte Sicht; S4 Vorschlag dormant→404/ohne-Session→401/keine-Whitelist-Felder→400; voller Round-Trip Einreichen→Berater-Liste→Übernahme (4-Augen, self_service→Onboarding-Regelset)→Stammdaten geschrieben; Idempotenz je Mandant). Client (Portal server-only; Berater-Sektion mandant-formulare build-verifiziert — kein e2e-Harness für diese Fläche). 712→717 Vitest, E2E unverändert (19). Davor MED-D-303 (Item↔Mandat-Zuordnung): +3 Server-Vitest + 1 Full-Stack-E2E / +1 Datei — api/app.test.ts +3 (Item einem Mandat zuordnen + wieder lösen; fremde/unbekannte Auftrags-ID → 404 kein Cross-Mandant-Verweis; fremdes Item → 404 + ungültige auftragRef → 400) + neue client/e2e-fullstack/onboarding-item-mandat.spec.ts 1 (Zuordnen → Persistenz-Roundtrip aus D1 → Lösen + Cross-Mandant-Guard, Migration v59 gegen den echten Worker). 709→712 Vitest, 18→19 E2E (7→8 Full-Stack, 10→11 Playwright-Dateien). Davor MED-D-302 (Onboarding-Greenfield G5 — dormante ausfuellformular-Tabelle + Repo-Methoden entfernt): +0 Tests — reiner Dead-Code-Abbau (die entfernten Methoden hatten seit G3E2 keine Test-Usages mehr); Migration v58 (DROP TABLE IF EXISTS ausfuellformular) wird von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert (7/7 grün, sauberer Boot). 709 Vitest unverändert. Davor MED-D-301 (Onboarding-Greenfield G4 — Item↔Vorgang-Link + Bedarf je Mandat): +2 Server-Vitest + 2 Full-Stack-E2E / +1 Datei — api/ausfuellformular.test.ts +1 (erstellen mit onboardingItemId setzt den Item-Link + auditiert ihn; zweites Anfordern desselben Items ist idempotent, ein Vorgang je Item), api/beratungsauftrag.test.ts +1 (liste liefert den abgeleiteten benoetigt-Bedarf aus dem Produkt-Playbook, G-2) + neue client/e2e-fullstack/onboarding-item-vorgang.spec.ts 2 (Item↔Vorgang-Roundtrip gegen echten Worker/D1: "Formular aus Item anfordern" → Link + Liste + idempotente Wiederverwendung; gebuchtes Mandat → benoetigt abgeleitet). 706→709 Vitest, 16→18 E2E (5→7 Full-Stack, 9→10 Playwright-Dateien). Davor MED-D-299 (Onboarding-Greenfield G3 Etappe 3a — Onboarding-Strecke auf die Feld-Engine): +0 Tests, verhaltensneutral — die Onboarding-Grunddaten rendern jetzt über PubFeldEngineComponent; die bestehende client/e2e/onboarding.spec.ts (Lücken-Report · Happy-Path bis "Vielen Dank!" · ungültiger Token) deckt die Strecke über dieselben Feld-IDs weiter ab und bleibt 11/11 grün. Davor MED-D-298 (Onboarding-Greenfield G3 Etappe 2 — Ausfüll-Flow auf den einen Service): −7 Vitest / −1 Datei — die G2-Lean-Service-Tests (api/formular-vorgang.test.ts) sind entfallen (der Lean-Service wurde durch den voll-ausfüll-fähigen FormularVorgangService ersetzt); die bestehende api/ausfuellformular.test.ts (Domäne + Service + HTTP-Fluss) testet jetzt den neuen FormularVorgangService gegen den formular_vorgang-Store (Import/Repo-Zugriffe umgestellt, identische Erwartungen = Contract-Netz); api/formular-vorgaenge.test.ts (SLA/Öffnungs-Tracking) ebenso auf den neuen Store repointet. Full-Stack-E2E (formular-sichtung.spec + DocuSign) verifiziert den Ausfüll-Flow gegen den echten Worker auf dem neuen Store (5/5). 713→706, 60→59 Dateien. Davor MED-D-297 (Onboarding-Greenfield G3 Etappe 1 — geteilte Feld-Engine): +2 gemockte E2E / +1 Datei — client/e2e/formular.spec.ts (die PubFeldEngineComponent rendert die Ausfüll-Formular-Felder inkl. Marker-Chips; Pflichtfeld füllen → Vorschau → rechtsverbindlich absenden → "Vielen Dank!" mit Beleg-Nr.; leeres Pflichtfeld → Vorschau blockiert mit ehrlichem Hinweis). Server-Vitest unverändert (reine Client-Konsolidierung). 14→16 E2E, 8→9 Playwright-Dateien. Davor MED-D-296 (Onboarding-Greenfield G2 — eine FormularVorgang-Entität + Service): +8 Server-Vitest / +1 Datei — api/formular-vorgang.test.ts 7 (schema-getriebener Lebenszyklus: erstellen inkl. Item-Link + Audit · kontext/Öffnungs-Tracking · einreichen mit Pflichtprüfung/Prüfsumme + Doppel-Einreichung-Konflikt · Abschnitts-Freigabe N2 idempotent · Feld-Ablehnung → Korrektur-Schleife inkl. Rücksetzen bei Re-Einreichung · Wächter · Integration mit dem G1-Adapter-Schema) + domain/feld.test.ts +1 (fehlendePflicht: Wert ODER Markierung erfüllt Pflicht, DM-9). Migration v57 (formular_vorgang) via Full-Stack-E2E gegen den echten Worker verifiziert (5/5, sauberer Boot). 705→713, 59→60 Dateien. Davor MED-D-295 (Onboarding-Greenfield G1 — kanonisches Feld-Modell): +9 Server-Vitest / +2 Dateien — domain/feld.test.ts 5 (reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt + Schema-Konsistenz-Invariante schemaVerstoesse) + domain/formular-schema.test.ts 4 (die 3 Adapter überführen die Alt-Kataloge treu: Onboarding pflicht:bool→wichtigkeit+select→auswahl, Ausfüll-KundenFeld 1:1, Fragebogen klasse→wichtigkeit+block→Abschnitt+Öffnungszeiten-strukturTyp; Feldzahl erhalten, jedes Schema konsistent). 696→705, 57→59 Dateien. Davor MED-D-293 (Onboarding-Vereinfachung N1, 1. Schritt — geteilte Bausteine der öffentlichen Kundenstrecken): +0 Tests, verhaltensneutral — die wortgleich kopierte Lade-Fehler-Auswertung + Turnstile-Callback-Bindung sind jetzt geteilte Bausteine in pub-bausteine.ts (ladeFehlerAuswerten/bindeTurnstileCallback); die bestehende gemockte E2E-Suite (client/e2e/, u. a. "ungültiger Token"/"ungültige Fragebogen-Einladung") deckt die Fehler-/Zugriffsschutz-Pfade der 4 Public-Strecken ab und bleibt 9/9 grün (Client hat keine Unit-Test-Infrastruktur). Davor MED-D-292 (Onboarding-Vereinfachung N2, Abschnitts-Freigabe): +4 Server-Vitest (domain/feld-sichtung.test.ts +1 akzeptiereOffene — akzeptiert nur offene Felder, lässt eine Ablehnung mit Grund unangetastet, idempotent; api/ausfuellformular.test.ts +3 alleAkzeptieren — 1-Klick-Freigabe akzeptiert alle offenen + Audit PII-arm, zweiter Aufruf nichts_offen, eine vorherige Ablehnung bleibt bestehen und öffnet beim Abschließen die Korrektur-Schleife, nur für eingereichte Formulare) + +1 Full-Stack-E2E (client/e2e-fullstack/formular-sichtung.spec.ts: einreichen → Abschnitt in 1 Klick freigeben → abschließen gegen den echten Worker; deckt den atomaren read-modify-write der Sichtung in D1 ab). 692→696 Vitest, 13→14 Playwright (7→8 Dateien). Davor MED-D-290 (Full-Stack-E2E gegen den echten Worker, löst MED-OP-E2E-1 ein): +4 Playwright-Tests in client/e2e-fullstack/ gegen den ECHTEN Worker (wrangler dev · workerd · lokale D1 + R2 · SIGNATUR_PROVIDER=fake) statt gemockter API — App-Shell-Smoke + Health, Mandant anlegen → persistiert → Reload (echter Browser→Worker→D1-Pfad), DocuSign-Flow (Mandant→Onboarding→Ablage provisionieren→Datei ablegen→zur Unterschrift senden→Vor-Ort-/Demo-Abschluss→Dokument "erhalten" + signierte Rückablage; echter D1/R2-Write-back + Provider-Lebenszyklus). Ein Origin bedient UI + /api (wie Produktion), Overrides nur fürs Testen (AUTH_ENFORCED=false, Fake-Signatur). CI-Job client-e2e-fullstack (gated bei Client- oder Server-Änderung, Teil des Pflicht-Checks checks). Deckt den echten Datenpfad ab (Migration, D1-SQL, R2-Ablage), den die In-Process-Vitest nicht durchläuft. Davor MED-D-289 (E2E-/UI-Test-Infrastruktur, gemockte API): +9 Playwright-Tests in client/e2e/ — App-Shell-Smoke (bootet + Marke/Cockpit + Navigation), Cockpit (Mandantenliste rendert gelieferte Mandanten / ehrlicher Leerzustand), Onboarding-Kundenstrecke (3-Schritte-Flow: Lücken-Report bei fehlender Pflichtangabe · vollständiger Weg bis "Vielen Dank!" · ungültiger Token), öffentliche Strecken (Lead-Formular rendert · ungültige Fragebogen-Einladung). Sie laufen gegen den fertigen Angular-Build (dist/browser via e2e/static-server.mjs) mit gemockter API (Playwright-route, e2e/support/mock-api.ts) — deterministisch, ohne Worker/D1/Secrets. In CI als client-e2e-Job (gated wie client-build, Teil des Pflicht-Checks checks, "immer laufen lassen"). Erste Client-Test-Infrastruktur überhaupt — löst die bisherige "nur ng build"-Lücke (MED-D-267 ff.). Volllast-E2E gegen echten Worker + D1 (wrangler) ist als Folge-Slice MED-OP-E2E-1 notiert. Server-Vitest 692 unverändert (reine Client-/CI-Ergänzung). Davor MED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem "wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Test — domain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Tests — api/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Tests — api/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → "nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte "Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistik → ng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Tests — signatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Tests — api/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Tests — auth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Tests — api/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Tests — api/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Tests — api/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Tests — api/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Tests — api/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 "keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + "Zu fakturieren"-Filter): +1 Test — api/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Tests — api/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Tests — domain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Tests — domain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Tests — Davor: MED-D-677 (Turnstile deploy-fähig): neue portal-konfig.test.ts +5 (Portal inaktiv → keine Meldung; fehlendes EMAIL-Binding und fehlendes TURNSTILE_SECRET; Secret ohne Sitekey wird zuerst als „Login blockiert" gemeldet; vollständige Konfiguration schweigt). Davor: MED-D-674 (Portal-Login „E-Mail erneut senden"): e2e/portal.spec.ts +1 (Wartezeit-Knopf gesperrt → nach page.clock.runFor klickbar → enumeration-freie Bestätigung → „Andere E-Mail-Adresse verwenden" zurück zur Anmeldung). Davor: MED-D-672 („Nächster Schritt"-Banner entfernt): e2e-fullstack/kundenseite-einfach.spec.ts prüft jetzt die Abwesenheit des Banners statt seiner Inhalte (Titel/Kommentar angepasst), Zahl unverändert. Davor: MED-D-654 (Erinnerungs-Schalter aus der Akte): e2e-fullstack/onboarding-item-vorgang.spec.ts Erinnerungs-Test umgestellt (kein Schalter/Hinweis mehr; ausgeschaltet → leiser Stand in den Basis-Dokumenten), Zahl unverändert. Davor: MED-D-652 (Dexman-Monatsaufgaben): neue api/dexman-monatsaufgaben.test.ts +3 (Vormonat inkl. Jahreswechsel; nur laufende Dexman-Mandate, Fälligkeit 5./10./15.; Cron legt je Monat genau einmal an, gebunden an das Mandat). Davor: MED-D-649 (Folgeaufgaben für Dexman-Mandate): api/beratungsauftrag.test.ts (Playbook-Test prüft jetzt alle buchbaren Produkte + die vier Dexman-Aufgaben mit Fristen, Zahl unverändert), e2e-fullstack/dexman-flow.spec.ts prüft die Wiedervorlagen beim Buchen in echter D1. Davor: MED-D-646 (Standard-Unterlagen für Dexman-Mandate): domain/unterlagen-vorlagen.test.ts +1 (vier Unterlagen, eindeutige stabile IDs, idempotentes Seed-SQL v68), e2e-fullstack/dexman-flow.spec.ts prüft die Unterlagen beim Buchen in echter D1. Davor: MED-D-643 (Dexman-Controlling buchbar): api/beratungsauftrag.test.ts (Ablehnungs-Test durch "buchbar, an die Praxis gebunden" ersetzt, Zahl unverändert), api/lead.test.ts (Lead bietet genau die buchbaren Produkte an), e2e-fullstack/dexman-flow.spec.ts +1 (Mandat buchen → "Dexman öffnen" zeigt Dexman für die Praxis). Davor: MED-D-638 (Kunden-Erinnerung an fehlende Unterlagen): neue api/unterlagen-erinnerung.test.ts +5 (offene Unterlagen ohne Aufgaben/Dubletten; nächste Aktion; 7-Tage-Takt mit max. 3 Mails und genau einer Wiedervorlage; Zyklus-Ende/abgeschaltet/dormant ohne Portal; HTTP Stand + Schalter), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Schalter in der Akte persistiert in D1). Davor: MED-D-635/637/639 (Basis-Dokumente, Einmal-Link entfällt, 4-Stand-Schieber): api/portal.test.ts +1 (Onboarding-Einladung entsteht automatisch und idempotent über das Portal-Dashboard), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Überschrift „Basis-Dokumente", kein Einmal-Link-Abschnitt, „Entfällt" im Schieber und zurück). Davor: CodeRabbit-Nachlese #627: api/beratungsauftrag.test.ts +3 (abgelaufene Unterlage zählt als fehlend, unterschriebene nicht; eigenes Flag unterlagenFehler; Rollen-Update ohne team lässt das Team unverändert). Davor: MED-D-630 R-13 (Standard-Mandats-Unterlagen per Migration v66): neue domain/unterlagen-vorlagen.test.ts +2 (nur buchbare Produkte, stabile IDs, idempotentes Seed-SQL); e2e-fullstack/mandats-unterlagen.spec.ts prüft zusätzlich die Standard-Vorlage in echter D1. Davor: MED-D-630 Slice 2 (Mandats-Unterlagen je Produkt): api/beratungsauftrag.test.ts +4 (Reife mit fehlenden Unterlagen, Anlage aus Vorlage idempotent + auditiert, Unterlage ergänzen mandant-scoped, HTTP: Pflege nur Admin/Validierung/Löschen), e2e/verwaltung.spec.ts +1 (Vorlagen je Produkt), neue e2e-fullstack/mandats-unterlagen.spec.ts +1 (Vorlage → Anforderung im Mandat, abhaken persistiert, ergänzen). Davor: MED-D-630 (Onboarding als Abhak-Liste): e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Kundenakte zeigt Basis-Unterlagen ohne Mandat-Auswahl und ohne "Formular anfordern"). Davor: MED-D-629 (Teams + "Nicht zugewiesen"): domain/rechte.test.ts +2 (Team-Sicht, normalisiereTeam), api/rbac.test.ts +1 (Team setzen nur Admin, Team-Kunde sehen/bearbeiten, anderes Team 404, Team entfernen), e2e/verwaltung.spec.ts +1 (Team je Benutzer, "Chef / Backoffice"), e2e/cockpit.spec.ts +1 (Filter "Nicht zugewiesen"). Davor: MED-D-623 (übrige By-ID-Routen im Kunden-Scope): api/rbac.test.ts +1 (Dokumente, Bankkonten, Onboarding-Items, Signaturen, Dokumentgenerierung, Leistungszeit: fremder Kunde → 404 ohne Wirkung, eigener Kunde → 200, unbekannte ID → Handler-404). Davor: MED-D-384 (eigene Rechtsseiten): neue rechtliches.test.ts +10 (Pflichtkern inkl. Vertretung, nur https://-Overrides, Links nur bei vollständigen Angaben bzw. externe URL mit Vorrang, nur konfigurierte Angaben im Impressum, HTML-Escaping, Datenschutz-Inhalte + Turnstile/DSB/Aufsicht nur wenn konfiguriert) + oeffentlich-site.test.ts +2 (/impressum·/datenschutz auf allen Hosts, Landing verlinkt beide nur bei vollständigem Pflichtkern). Vorher: MED-D-382 (Praxis-Routen im Kunden-Scope): api/rbac.test.ts +1 (Praxis-, Gutachten-, Befund- und Dexman-Routen für fremden Kunden → 404 inkl. PATCH/POST ohne Wirkung, eigener Kunde → 200, Backoffice sieht alles, Flag aus = keine Regression). Davor: MED-D-383 (formelle Fachsprache, keine neuen Tests): domain/dexman-kern.test.ts, api/dexman.test.ts, e2e/dexman.spec.ts, e2e-fullstack/dexman-flow.spec.ts auf die neuen Texte angepasst; Kern-Test prüft zusätzlich die Statusbegriffe je Bereich. Davor: MED-D-381 (Dexman umgesetzt, zwei Oberflächen): neue domain/dexman-kern.test.ts +25 (Rechenkern aus shared/dexman-kern/: Monat/Format, Umsatz/Personal/Material/Fixkosten, Tageslinie, Ist vor Plan, Ein-/Austritt, Gehaltsänderung + Umsatzbeteiligung, Saison/Umsatzänderung, zuflussbasiert, Darlehen fallend/Annuität/Zinsbindung/privat/vermietet/Laufzeitende, Anschaffung/AfA, Ereignisse, Kontokorrentzins, Beispielpraxis deterministisch, Sätze, Hebel, Szenario ohne Seiteneffekt, Prüfregeln) + neue api/dexman.test.ts +8 (Stand/Lage, Versionen append-only, Archivieren, Ist in praxis_jahreswert mit Vorrang, Hebel → Wiedervorlage idempotent, PII-armes Audit + Lesezugriff, HTTP inkl. Rechte je Kunde); domain/zugriff.test.ts um dexman_planung erweitert; neue gemockte e2e/dexman.spec.ts +5 (Kompass: Satz/Tageslinie/Herkunft, Was-wäre-wenn ohne Speichern, Beratungsmodus; Klassisch: Kacheln/Personal/Monate + Umschalten, Formularprüfung) + Full-Stack e2e-fullstack/dexman-flow.spec.ts +1 (Akte → Praxen → Dexman, Ist-Erfassung persistiert, Übernehmen = neue Version, Klassisch zeigt dieselben Daten). Vorher: MED-D-337 (Unterschrift über den Assistenten): api/dokumentgenerierung.test.ts +2 (inkl. wiederherstellbare Verknüpfung) + api/individualdokument.test.ts +1 (Assistenten-Weg führt Quelle nach: Status/Audit, Stufen-Vorgabe AES, zweiter Start 409); neue Full-Stack-Spec e2e-fullstack/unterschrift-generierung.spec.ts +1. Vorher: MED-D-335 (Kundenseite Slice B): domain/naechste-schritte.test.ts +1 (Klartext ohne interne IDs/Jargon); neue Full-Stack-Spec e2e-fullstack/kundenseite-einfach.spec.ts +1 (Nächster Schritt oben, ein Status, "Mehr"-Reiter, kein Unterschrift-Direktversand). Vorher: MED-D-334 (interne Oberfläche Slice A): cockpit.spec.ts +3 (Leitstand-Kopfzeile ohne Überlauf bei 1280 px, Zuweisen per Personenauswahl, Fehlermeldung mit Ursache + nächstem Schritt). Vorher: MED-D-333 (Portal-Aufgabenliste/Fragebogen seitenweise): api/portal.test.ts +3 (Aufgabenliste aus Einladung/Formular/Fragebogen/Dokument, Portal-Mail-Texte, Einladungs-Betreff); E2E portal.spec.ts +1 (Aufgabenliste-Links) + public-strecken.spec.ts +1 (Fragebogen-Assistent). Vorher: MED-D-332 (Kundenseiten Slice A): neue domain/kunden-kontakt.test.ts +3 (leer = dormant, nur http(s)-Links, Berater nur mit Anzeigename) + api/onboarding-formular.test.ts +1 (Kontext liefert Kontakt-Block, nie Roh-E-Mail der Betreuung) → 793; api/portal.test.ts/api/ausfuellformular.test.ts/api/dokumentgenerierung.test.ts an neue Labels/Config-Form angepasst; gemockt e2e/onboarding.spec.ts +2 (Kontakt-Fußzeile mit tel:-Link + fehlender Link ausgeblendet + "Zum Absenden fehlt noch"; keine leere Hilfe-Box ohne Konfig) → 33, e2e/portal.spec.ts um Upload-Button/accept/Bestätigung erweitert. Davor MED-D-331 (K-10): api/formular-vorgaenge.test.ts +2 (ohne Kunden-E-Mail keine Erinnerung/keine Markierung + Nachholen; Zustellfehler nicht-fatal, harte Eskalation läuft weiter) und bestehender Weich-Test auf Empfänger = Kontakt-E-Mail + Fortsetzungs-Link umgestellt → 789. Davor MED-D-330 (Begriffe "Kunde"/"Mandat"): keine neuen Tests, Zahl unverändert — angepasst an die neuen UI-/Fehlertexte: api/ausfuellformular.test.ts (Festwerte "Kunde"/"Kundennummer"), api/dokumentgenerierung.test.ts (Mapping "Kunde"), api/formular-vorgaenge.test.ts (Betreff "beim Kunden nachfassen"), e2e/cockpit.spec.ts + e2e/smoke.spec.ts + e2e-fullstack/smoke.spec.ts + e2e-fullstack/mandant-create.spec.ts (Überschrift "Kunden", "+ Neuer Kunde", Leerzustand). Davor MED-D-329/328 (Einfachheits-Audit): api/ausfuellformular.test.ts +2 (Korrektur-Mail an Kontakt-E-Mail mit absolutem Link, ohne E-Mail kein Versand + kundeBenachrichtigt=false, Provider-Fehler nicht-fatal) → 787; gemockt e2e/cockpit.spec.ts +3 ("Heute fällig" zeigt nur datierte überfällige/heute fällige Punkte, Leerzustand, Fokus-Kürzel Default aus) + e2e/portal.spec.ts +1 (Portal ohne Staff-Kopfzeile, data-theme=light) → 31; Full-Stack neue e2e-fullstack/timer-kein-autostart.spec.ts +1 (Öffnen startet keinen Timer, Klick startet ihn serverseitig) → 9. Davor MED-D-326 (Betriebsinfo im Footer nur für Admins): +1 gemockte E2E in client/e2e/smoke.spec.ts — Nicht-Admin (Berater) sieht keinen Betriebsinfo-Fuß (footer.mw-footer gar nicht gerendert, keine Versionsinfo). 34→35 E2E (26→27 gemockt), Server-Vitest unverändert. Davor MED-D-324 (Version/Deploy dezent in den Footer): +2 gemockte E2E in client/e2e/smoke.spec.ts — Cockpit (Route "/"): Version + Deploy stehen im footer.mw-footer, nicht mehr im header.mw-header; App-Shell (/verwaltung): Version + Deploy im footer.md-footer, nicht im header.md-header. Server-Vitest unverändert (reine Client-/CSS-Änderung). 32→34 E2E (24→26 gemockt), 785 Vitest unverändert. Davor MED-D-323 (MED-OP-MIGRATE-1: Auto-Migration restart-sicher): +3 Server-Vitest / +1 Datei — neue db/migrate.test.ts mit minimalem Fake-D1 (modelliert _meta, ALTER TABLE … ADD/DROP COLUMN-Semantik inkl. Duplikat-Erkennung, atomare batch()): Erststart erreicht die Ziel-Schema-Version; zweiter Aufruf ist ein No-op (idempotent, keine DDL erneut); Absturz mitten in der Migration → Neustart läuft sauber weiter (Version bleibt beim Vorgänger, kein duplicate column). Erste Bausteine Richtung MED-OP-TEST-1 (echtes D1-Harness). 782→785 Vitest (63→64 Dateien). Davor MED-D-322 (OP-DM-8 Mail-Selbsttest): +4 Server-Vitest in api/app.test.ts (admin→Log-Kanal zugestellt=false, keine echte Mail; echter Kanal→sende() mit der eigenen Adresse aufgerufen→zugestellt=true; Provider-Fehler→502 ok:false; Nicht-Admin (berater)→403) +3 gemockte E2E / +1 Datei (neue client/e2e/verwaltung.spec.ts: echter Kanal→Erfolgshinweis, Log-Kanal→"keine Mail zugestellt", 502→Fehlermeldung im Status-Absatz). 778→782 Vitest (Dateien unverändert 63), 29→32 E2E (21→24 gemockt, 14→15 Playwright-Dateien). Davor MED-D-320 (MED-OP-PORTAL-2 Teil 2: mehrere Kontakt-Logins je Mandant): +3 Server-Vitest in api/portal.test.ts (Staff legt mehrere Kontakte an + listet sie (sortiert), doppelte E-Mail→409 email_vergeben; Sperren beendet die laufende Session (Dashboard 200→401) + wird als gesperrt gelistet, Reaktivieren gibt Zugang zurück; Scope-Guard: fremdes Konto sperren→404, unverändert). Client (Kontakt-Liste + Sperren/Reaktivieren in mandant-einladungen) ng build-verifiziert (das Register-Panel liegt tief in mandant-detail — wie die bestehende Einladungen-UI build-only, kein eigenes E2E-Harness). 775→778 Vitest (Dateien unverändert 63), E2E unverändert (29). Davor MED-D-319 (MED-OP-UX-14: Button-CTA-Vereinheitlichung + Verlassen-Modal → md-overlay + Command-Palette-Absicherung): +3 gemockte E2E / +1 Datei — neue client/e2e/cockpit-overlays.spec.ts (Command-Palette: Öffnen über den Kopf-Button + Fokus-Pin aufs Suchfeld + Esc-Schließen; Filtern nach Eingabe + Treffer per ↓ +↵ ausführen → Theme dunkel; Backdrop-Klick schließt). Server-Vitest unverändert (reine Client-/CSS-Änderung: Primary-CTA --md-green überall, Verlassen-Modal nutzt jetzt die geteilte md-overlay-Dialogmechanik statt hand-gerollt). 26→29 E2E (18→21 gemockt, 13→14 Playwright-Dateien), 775 Vitest unverändert. Davor MED-D-318 (UI/UX-Konsistenz- & Rough-Edges-Pass): +0 Tests — reine Konsistenz-/Client-/CSS-Änderungen (Theming-Tokens, Terminologie/Labels, Ladeindikatoren, Fokus-/Doppel-Submit-Guards, labels.ts-Nachbauten zurückgeführt). Kein neues Server-Verhalten (die Landing-Farb-/Wortmarken-Änderung berührt oeffentlich-site.ts nur kosmetisch → bestehende oeffentlich-site.test.ts unverändert grün); die Client-Änderungen sind über ng build + die bestehende client/e2e-Suite (u. a. Portal-Upload) gedeckt, die Fokus-/CSS-Pfade sind ohne zusätzlichen Harness nicht sinnvoll unit-testbar. Button-/Overlay-Vereinheitlichung ist als MED-OP-UX-14 getrackt (braucht erst Visual-Regression-Deckung). 775 Vitest / 18 gemockt unverändert. Davor MED-D-317 (WIP-Landing auf medidentas.com + robots.txt/KI-Trainings-Hinweise; inkl. CodeRabbit-Nachlese #343): +11 Server-Vitest / +1 Datei — neue oeffentlich-site.test.ts (istMarketingHost Apex/www vs. App-Host; robotsTxt host-abhängig — App-Host Disallow:/ + KI-Crawler gesperrt, Marketing-Host Allow:/ + KI-Crawler gesperrt; ai.txt nennt DSM/§44b + Disallow:/; tdmrep.json reserviert /; behandleOeffentlicheSite — robots/ai/tdmrep auf allen Hosts mit X-Robots-Tag: noai/tdm-reservation:1-Headern, Apex /→WIP-Landing noindex, App-Host /→null (regulär weiter), HEAD ohne Body, nicht-GET→null, Landing verweist auf app.medidentas.com). Die CodeRabbit-Nachlese #343 (dokFarbe-Theme-Tokens --warn/--muted→--amber/--mut; per-Dokument-Upload-Set statt Einzel-Signal; Decision-Log-Backtick) ist über den bestehenden client/e2e-Portal-Upload-Test + ng build abgedeckt (reine Client-/Doku-Korrektur, kein neuer Vitest); die magic-byte-Content-Prüfung bleibt bewusst Backlog (dokumentierter Tradeoff). 764→775 Vitest (62→63 Dateien), E2E unverändert (26). Davor MED-D-316 (Portal-Dokument-Upload, MED-OP-PORTAL-2 Teil 1): +9 Server-Vitest / +1 Datei — neue domain/upload-pruefung.test.ts (+4: pruefeUpload ok · leere Datei → leer · über 15 MB → zu_gross · unerlaubte Endung inkl. Doppel-Endung script.pdf.js → typ_unzulaessig) + api/portal.test.ts (+5: Upload-Route dormant → 404 / ohne Session → 401; fremdes/unbekanntes Dokument → 404 kein Cross-Mandant-Leak; angefordertes Dokument → Upload 200 + Status erhalten + im Dashboard sichtbar; bereits geprüft → 409 nicht_offen; unzulässiger Typ → 400) + +1 gemockte E2E in client/e2e/portal.spec.ts (angefordertes Dokument im Portal hochladen → Erfolg, kein Fehler). Server-Route unter /oeffentlich/portal/dokumente/:docId/upload (Session-scoped, eigener Mandant, R2 via bestehende DokumentService.hochladen); Token-Upload-Route zusätzlich mit pruefeUpload gehärtet. 755→764 Vitest (61→62 Dateien), 25→26 E2E (17→18 gemockt, Dateien unverändert 13). Davor MED-D-315 (Portal-Zugang automatisch beim Kunden-Anlegen): +4 Server-Vitest in api/portal.test.ts (zugangSicherstellen: dormant → kein Konto/kein Send; aktiv + neue E-Mail → Konto angelegt + Invite genau einmal, zweiter Aufruf idempotent; ohne verwertbare Kontakt-E-Mail nichts; HTTP POST /api/mandanten mit E-Mail + aktivem Portal → Konto auto-angelegt + Invite). 751→755 Vitest (Dateien unverändert 61), E2E unverändert. Davor MED-D-314 (Cloudflare Email Service als einziger Mail-Provider, MailChannels raus): −6/+5 Server-Vitest (netto −1) — benachrichtigung/mailchannels.test.ts entfernt (−6); neue benachrichtigung/cloudflare-email.test.ts (+5): cloudflareEmailKonfigAusEnv-Defaults/Trim; Versand über das gemockte send_email-Binding mit From-Objekt + Magic-Link im Text-Körper; empfänger-loser Aufruf still übersprungen (kein send); Binding-Fehler wirft mit code, ohne Empfänger-PII. 752→751 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-312 (CodeRabbit-Nachlese zu #339: Login-Härtung + Mailversand-Robustheit + Rate-Limit-Retention; inkl. Round-2-Review nach "ready for review"): +6 Server-Vitest — api/staff-login.test.ts +1 (Staff-Session überschreibt keine abweichende Access-Identität: A's Session-Cookie + B's Access-Header → es gilt B/backoffice, nicht A/admin; passende Access-Identität → A/admin) + api/portal.test.ts +3 (bereinigeRateLimits löscht nur abgelaufene Fenster; Login-Mail-Zustellfehler nicht-fatal → Login-Flow läuft weiter + Audit zustellung:fehler; erfolgreiche Zustellung → zustellung:ok) + api/wiedervorlage.test.ts +2 (Erinnerungs-Item-Isolation: ein Fehlversand stoppt die übrigen nicht + bleibt retrybar; Audit-Fehler NACH erfolgreicher Zustellung → keine Wiederholung/kein Doppelversand). Übrige Fixes (MailChannels-8s-Timeout inkl. Fehler-Body-Read, PORTAL_AKTIV-Startup-Warnung, Turnstile-Token-Reset im Client) sind über die bestehenden Tests + tsc/ng build abgedeckt (die Timeout- und Client-Widget-Pfade sind ohne Netz-/Browser-Harness nicht sinnvoll unit-testbar). 746→752 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-311 (Portal-Login-Härtung: Turnstile + Rate-Limiting am öffentlichen anmelden, löst MED-OP-PORTAL-3): +3 Server-Vitest in api/portal.test.ts (GET /oeffentlich/portal/config dormant→404 / aktiv→{turnstileSitekey:null}; Rate-Limit je E-Mail — die 6. Anmeldung im 10-Min-Fenster → 429 rate_limit, eine andere E-Mail behält ihr eigenes Fenster; bei gesetztem turnstileSecret ohne Token → 400 bot_check vor dem Rate-Limit). Der neue repo.pruefeRateLimit (Tabelle rate_limit, Migration v62) ist über den Rate-Limit-Test gegen den Memory-Repo abgedeckt; Client (Turnstile-Widget auf der Portal-Login-Seite) ng build-verifiziert + portal-E2E-Login-Mock um /portal/config ergänzt. 743→746 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-310 (Portal-Go-Live: echter Mailversand MailChannels/OP-DM-8 + Kundenportal scharf): +6 Server-Vitest / +1 Datei — neue benachrichtigung/mailchannels.test.ts (gegen gemocktes fetch: mailChannelsKonfigAusEnv dormant ohne MAILCHANNELS_API_KEY; Defaults + DKIM nur bei allen drei Feldern; sende POSTet Payload + X-Api-Key, Magic-Link steht im Körper; DKIM je Personalisierung mitgesendet; Empfänger-loser Aufruf still übersprungen (kein fetch); HTTP-Fehler wirft mit Status, ohne Empfänger-PII). portal.test.ts an die korrigierte Magic-Link-Form (/portal?token= Client-Route statt API-Endpunkt) angepasst — Token wird aus dem Link gezogen und gegen den API-Endpunkt eingelöst. Client (neuer "Kundenportal"-Abschnitt in der Akte, mandant-einladungen) ng build-verifiziert; der Portal-Konto-Endpunkt ist bereits durch portal.test.ts abgedeckt. 737→743 Vitest (60→61 Dateien), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-309 (Staff-Magic-Link-Login "hinter Access", dormant): +7 Server-Vitest / +1 Datei — neue api/staff-login.test.ts (alle 3 Routen dormant→404 ohne STAFF_LOGIN_AKTIV, Golden Rule; Anmelden ohne Konto-Enumeration → unbekannte E-Mail identische ok-Antwort ohne Link; ungültige E-Mail → 400; Bootstrap-Admin m.kirchhof@drkv.com UND per Verwaltung angelegter Mitarbeiter erhalten je einen Link = zweite Nutzeranforderung; voller Magic-Link-Flow anfordern→einlösen→staff_session-Cookie + Token-Replay → 401; ungültiger Token → 401; Staff-Session etabliert die Identität hinter Access unter enforced OHNE Access-Header → /api/ich 200 Rolle=admin, ohne beides → 401, nach Logout → 401) + +3 gemockte E2E / +1 Datei — neue client/e2e/staff-login.spec.ts (ohne Token → Mitarbeiter-Anmeldeformular → Magic-Link angefordert; Token einlösen bei dormantem Login (404) → "zurzeit nicht verfügbar"; ungültiger Token (401) → "neuen Link anfordern"). Migration v61 (staff_anmelde_token/staff_session, additiv) via Memory-Repo + tsc mitverifiziert; Client staff-login.component.ts ng build-verifiziert. 730→737 Vitest (59→60 Dateien), 22→25 E2E (14→17 gemockt, 12→13 Playwright-Dateien). Davor MED-D-308 (S5-UI: Kunden-Self-Service-Portal Client-UI, dormant): +3 gemockte E2E / +1 Datei — neue client/e2e/portal.spec.ts (dormant PORTAL_AKTIV=aus → "Kundenportal zurzeit nicht verfügbar"; nicht angemeldet → Anmelde-Formular → Magic-Link angefordert; angemeldet → Dashboard zeigt Onboarding-Fortschritt/offene Items/Formular-Status + proaktive S4-Einreichung wird bestätigt), gegen die gemockten Portal-Endpunkte. Server unverändert (nur bestehende Endpunkte konsumiert); portal-public.component.ts + Portal-API-Methoden ng build-verifiziert. 19→22 E2E (11→14 gemockt, 11→12 Playwright-Dateien), 730 Vitest unverändert. Davor MED-D-307 (N5: Onboarding-Einwilligung über DocuSign statt SES-Attrappe, flag-gated/dormant, OP-SIGN-1): +3 Server-Vitest in api/onboarding-formular.test.ts (Flag an + Signatur-Dienst → Einwilligung als echte SES über den Fake-Provider angefordert/pending/PDF-Beleg, Audit einwilligung.angefordert statt .erteilt; Flag an ohne Dienst → SES-Attrappe (dormant-sicher); Provider-Fehler → consent_provider_fehler, Einladung bleibt offen — kein stiller Fallback). Client unverändert (Danke-Seite bleibt; die DocuSign-Mail ist der Folgeschritt). Der DocuSign-Pipeline-Pfad (anfordern→Envelope→Webhook→abgleich) ist bereits durch die Full-Stack-docusign-flow.spec.ts abgedeckt; der globale Fullstack-Flag bleibt aus (Onboarding-E2E prüft weiter den SES-Default). 727→730 Vitest, E2E unverändert (19). Davor MED-D-306 (kollisionssicheres Hash-Schema 3 + Ablehnung unbekannter Schema-Marker, G-4; CodeRabbit-Nachlese zu #332): +4 Server-Vitest in domain/stammdaten-version.test.ts (Schema 2 kollidiert bei =/|-Freitext, Schema 3 nicht — behebt den Trenner-Injektions-Fund; kanonischerSnapshot wirft bei unbekanntem Schema statt still zurückzufallen; pruefeVersionsKette meldet einen unbekannten hashSchema-Marker als Bruch; Umschreiben eines Bestands-Markers 2→3 wird erkannt). Server-only Härtung; Full-Stack-E2E 8/8 (schema-3-Schreibpfad gegen echten Worker) unverändert. 723→727 Vitest, E2E unverändert (19). Davor MED-D-305 (feldset-tolerante Stammdaten-Hash-Kette, G-4; die 6 Onboarding-Grunddaten jetzt versioniert): +6 Server-Vitest in domain/stammdaten-version.test.ts (neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil = Regressions-Lock der Bestands-Ketten; Schema-2 serialisiert feldset-tolerant über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation an einer Schema-1-Zeile erkannt; gemischte Kette Schema-1 + neue Schema-2-Zeile mit den 6 neuen Feldern bleibt intakt — Kern von MED-D-305; künftiges Feld-Wachstum bricht eine Schema-2-Zeile nicht, ein nachträglich eingemischtes Feld schon = Tamper-Evidenz). Client (StammdatenSnapshot/StammdatenVersion-Typen + 6 md-versioned-field-Zeilen build-verifiziert; Migration v60 von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert, 8/8 grün). 717→723 Vitest, E2E unverändert (19). Davor MED-D-304 (Kunden-Self-Service-Portal S3 Dashboard + S4 proaktive Einreichung, dormant): +5 Server-Vitest in api/portal.test.ts (S3 Dashboard dormant→404/ohne-Session→401/kompakte Sicht; S4 Vorschlag dormant→404/ohne-Session→401/keine-Whitelist-Felder→400; voller Round-Trip Einreichen→Berater-Liste→Übernahme (4-Augen, self_service→Onboarding-Regelset)→Stammdaten geschrieben; Idempotenz je Mandant). Client (Portal server-only; Berater-Sektion mandant-formulare build-verifiziert — kein e2e-Harness für diese Fläche). 712→717 Vitest, E2E unverändert (19). Davor MED-D-303 (Item↔Mandat-Zuordnung): +3 Server-Vitest + 1 Full-Stack-E2E / +1 Datei — api/app.test.ts +3 (Item einem Mandat zuordnen + wieder lösen; fremde/unbekannte Auftrags-ID → 404 kein Cross-Mandant-Verweis; fremdes Item → 404 + ungültige auftragRef → 400) + neue client/e2e-fullstack/onboarding-item-mandat.spec.ts 1 (Zuordnen → Persistenz-Roundtrip aus D1 → Lösen + Cross-Mandant-Guard, Migration v59 gegen den echten Worker). 709→712 Vitest, 18→19 E2E (7→8 Full-Stack, 10→11 Playwright-Dateien). Davor MED-D-302 (Onboarding-Greenfield G5 — dormante ausfuellformular-Tabelle + Repo-Methoden entfernt): +0 Tests — reiner Dead-Code-Abbau (die entfernten Methoden hatten seit G3E2 keine Test-Usages mehr); Migration v58 (DROP TABLE IF EXISTS ausfuellformular) wird von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert (7/7 grün, sauberer Boot). 709 Vitest unverändert. Davor MED-D-301 (Onboarding-Greenfield G4 — Item↔Vorgang-Link + Bedarf je Mandat): +2 Server-Vitest + 2 Full-Stack-E2E / +1 Datei — api/ausfuellformular.test.ts +1 (erstellen mit onboardingItemId setzt den Item-Link + auditiert ihn; zweites Anfordern desselben Items ist idempotent, ein Vorgang je Item), api/beratungsauftrag.test.ts +1 (liste liefert den abgeleiteten benoetigt-Bedarf aus dem Produkt-Playbook, G-2) + neue client/e2e-fullstack/onboarding-item-vorgang.spec.ts 2 (Item↔Vorgang-Roundtrip gegen echten Worker/D1: "Formular aus Item anfordern" → Link + Liste + idempotente Wiederverwendung; gebuchtes Mandat → benoetigt abgeleitet). 706→709 Vitest, 16→18 E2E (5→7 Full-Stack, 9→10 Playwright-Dateien). Davor MED-D-299 (Onboarding-Greenfield G3 Etappe 3a — Onboarding-Strecke auf die Feld-Engine): +0 Tests, verhaltensneutral — die Onboarding-Grunddaten rendern jetzt über PubFeldEngineComponent; die bestehende client/e2e/onboarding.spec.ts (Lücken-Report · Happy-Path bis "Vielen Dank!" · ungültiger Token) deckt die Strecke über dieselben Feld-IDs weiter ab und bleibt 11/11 grün. Davor MED-D-298 (Onboarding-Greenfield G3 Etappe 2 — Ausfüll-Flow auf den einen Service): −7 Vitest / −1 Datei — die G2-Lean-Service-Tests (api/formular-vorgang.test.ts) sind entfallen (der Lean-Service wurde durch den voll-ausfüll-fähigen FormularVorgangService ersetzt); die bestehende api/ausfuellformular.test.ts (Domäne + Service + HTTP-Fluss) testet jetzt den neuen FormularVorgangService gegen den formular_vorgang-Store (Import/Repo-Zugriffe umgestellt, identische Erwartungen = Contract-Netz); api/formular-vorgaenge.test.ts (SLA/Öffnungs-Tracking) ebenso auf den neuen Store repointet. Full-Stack-E2E (formular-sichtung.spec + DocuSign) verifiziert den Ausfüll-Flow gegen den echten Worker auf dem neuen Store (5/5). 713→706, 60→59 Dateien. Davor MED-D-297 (Onboarding-Greenfield G3 Etappe 1 — geteilte Feld-Engine): +2 gemockte E2E / +1 Datei — client/e2e/formular.spec.ts (die PubFeldEngineComponent rendert die Ausfüll-Formular-Felder inkl. Marker-Chips; Pflichtfeld füllen → Vorschau → rechtsverbindlich absenden → "Vielen Dank!" mit Beleg-Nr.; leeres Pflichtfeld → Vorschau blockiert mit ehrlichem Hinweis). Server-Vitest unverändert (reine Client-Konsolidierung). 14→16 E2E, 8→9 Playwright-Dateien. Davor MED-D-296 (Onboarding-Greenfield G2 — eine FormularVorgang-Entität + Service): +8 Server-Vitest / +1 Datei — api/formular-vorgang.test.ts 7 (schema-getriebener Lebenszyklus: erstellen inkl. Item-Link + Audit · kontext/Öffnungs-Tracking · einreichen mit Pflichtprüfung/Prüfsumme + Doppel-Einreichung-Konflikt · Abschnitts-Freigabe N2 idempotent · Feld-Ablehnung → Korrektur-Schleife inkl. Rücksetzen bei Re-Einreichung · Wächter · Integration mit dem G1-Adapter-Schema) + domain/feld.test.ts +1 (fehlendePflicht: Wert ODER Markierung erfüllt Pflicht, DM-9). Migration v57 (formular_vorgang) via Full-Stack-E2E gegen den echten Worker verifiziert (5/5, sauberer Boot). 705→713, 59→60 Dateien. Davor MED-D-295 (Onboarding-Greenfield G1 — kanonisches Feld-Modell): +9 Server-Vitest / +2 Dateien — domain/feld.test.ts 5 (reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt + Schema-Konsistenz-Invariante schemaVerstoesse) + domain/formular-schema.test.ts 4 (die 3 Adapter überführen die Alt-Kataloge treu: Onboarding pflicht:bool→wichtigkeit+select→auswahl, Ausfüll-KundenFeld 1:1, Fragebogen klasse→wichtigkeit+block→Abschnitt+Öffnungszeiten-strukturTyp; Feldzahl erhalten, jedes Schema konsistent). 696→705, 57→59 Dateien. Davor MED-D-293 (Onboarding-Vereinfachung N1, 1. Schritt — geteilte Bausteine der öffentlichen Kundenstrecken): +0 Tests, verhaltensneutral — die wortgleich kopierte Lade-Fehler-Auswertung + Turnstile-Callback-Bindung sind jetzt geteilte Bausteine in pub-bausteine.ts (ladeFehlerAuswerten/bindeTurnstileCallback); die bestehende gemockte E2E-Suite (client/e2e/, u. a. "ungültiger Token"/"ungültige Fragebogen-Einladung") deckt die Fehler-/Zugriffsschutz-Pfade der 4 Public-Strecken ab und bleibt 9/9 grün (Client hat keine Unit-Test-Infrastruktur). Davor MED-D-292 (Onboarding-Vereinfachung N2, Abschnitts-Freigabe): +4 Server-Vitest (domain/feld-sichtung.test.ts +1 akzeptiereOffene — akzeptiert nur offene Felder, lässt eine Ablehnung mit Grund unangetastet, idempotent; api/ausfuellformular.test.ts +3 alleAkzeptieren — 1-Klick-Freigabe akzeptiert alle offenen + Audit PII-arm, zweiter Aufruf nichts_offen, eine vorherige Ablehnung bleibt bestehen und öffnet beim Abschließen die Korrektur-Schleife, nur für eingereichte Formulare) + +1 Full-Stack-E2E (client/e2e-fullstack/formular-sichtung.spec.ts: einreichen → Abschnitt in 1 Klick freigeben → abschließen gegen den echten Worker; deckt den atomaren read-modify-write der Sichtung in D1 ab). 692→696 Vitest, 13→14 Playwright (7→8 Dateien). Davor MED-D-290 (Full-Stack-E2E gegen den echten Worker, löst MED-OP-E2E-1 ein): +4 Playwright-Tests in client/e2e-fullstack/ gegen den ECHTEN Worker (wrangler dev · workerd · lokale D1 + R2 · SIGNATUR_PROVIDER=fake) statt gemockter API — App-Shell-Smoke + Health, Mandant anlegen → persistiert → Reload (echter Browser→Worker→D1-Pfad), DocuSign-Flow (Mandant→Onboarding→Ablage provisionieren→Datei ablegen→zur Unterschrift senden→Vor-Ort-/Demo-Abschluss→Dokument "erhalten" + signierte Rückablage; echter D1/R2-Write-back + Provider-Lebenszyklus). Ein Origin bedient UI + /api (wie Produktion), Overrides nur fürs Testen (AUTH_ENFORCED=false, Fake-Signatur). CI-Job client-e2e-fullstack (gated bei Client- oder Server-Änderung, Teil des Pflicht-Checks checks). Deckt den echten Datenpfad ab (Migration, D1-SQL, R2-Ablage), den die In-Process-Vitest nicht durchläuft. Davor MED-D-289 (E2E-/UI-Test-Infrastruktur, gemockte API): +9 Playwright-Tests in client/e2e/ — App-Shell-Smoke (bootet + Marke/Cockpit + Navigation), Cockpit (Mandantenliste rendert gelieferte Mandanten / ehrlicher Leerzustand), Onboarding-Kundenstrecke (3-Schritte-Flow: Lücken-Report bei fehlender Pflichtangabe · vollständiger Weg bis "Vielen Dank!" · ungültiger Token), öffentliche Strecken (Lead-Formular rendert · ungültige Fragebogen-Einladung). Sie laufen gegen den fertigen Angular-Build (dist/browser via e2e/static-server.mjs) mit gemockter API (Playwright-route, e2e/support/mock-api.ts) — deterministisch, ohne Worker/D1/Secrets. In CI als client-e2e-Job (gated wie client-build, Teil des Pflicht-Checks checks, "immer laufen lassen"). Erste Client-Test-Infrastruktur überhaupt — löst die bisherige "nur ng build"-Lücke (MED-D-267 ff.). Volllast-E2E gegen echten Worker + D1 (wrangler) ist als Folge-Slice MED-OP-E2E-1 notiert. Server-Vitest 692 unverändert (reine Client-/CI-Ergänzung). Davor MED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem "wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Test — domain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Tests — api/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Tests — api/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → "nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte "Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistik → ng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Tests — signatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Tests — api/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Tests — auth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Tests — api/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Tests — api/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Tests — api/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Tests — api/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Tests — api/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 "keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + "Zu fakturieren"-Filter): +1 Test — api/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Tests — api/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Tests — domain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Tests — domain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Tests — Davor: MED-D-681/682 (Dexman 12-Monats-Bezug, Engpass im Kopfsatz, Meilensteine): domain/dexman-kern.test.ts +6 (abgelaufener Planungshorizont → Bezug bleibt künftig; Bezugssummen nicht hochgerechnet; Engpass auch ohne Umsatzplanung im Kopfsatz; Fall "starkes Ist-Jahr, Kosten erst ab Oktober" → nicht mehr kostendeckend neben Engpass; Kopfsatz ohne Engpass kurz; Meilensteine einmalig/sortiert/im Horizont) und Beispielpraxis-Erwartungen auf den 12-Monats-Bezug umgestellt; api/dexman.test.ts angepasst; e2e/dexman.spec.ts +1 (Anschaffung als Meilenstein mit Mouse-over). Davor: MED-D-677 (Turnstile deploy-fähig): neue portal-konfig.test.ts +5 (Portal inaktiv → keine Meldung; fehlendes EMAIL-Binding und fehlendes TURNSTILE_SECRET; Secret ohne Sitekey wird zuerst als „Login blockiert" gemeldet; vollständige Konfiguration schweigt). Davor: MED-D-674 (Portal-Login „E-Mail erneut senden"): e2e/portal.spec.ts +1 (Wartezeit-Knopf gesperrt → nach page.clock.runFor klickbar → enumeration-freie Bestätigung → „Andere E-Mail-Adresse verwenden" zurück zur Anmeldung). Davor: MED-D-672 („Nächster Schritt"-Banner entfernt): e2e-fullstack/kundenseite-einfach.spec.ts prüft jetzt die Abwesenheit des Banners statt seiner Inhalte (Titel/Kommentar angepasst), Zahl unverändert. Davor: MED-D-654 (Erinnerungs-Schalter aus der Akte): e2e-fullstack/onboarding-item-vorgang.spec.ts Erinnerungs-Test umgestellt (kein Schalter/Hinweis mehr; ausgeschaltet → leiser Stand in den Basis-Dokumenten), Zahl unverändert. Davor: MED-D-652 (Dexman-Monatsaufgaben): neue api/dexman-monatsaufgaben.test.ts +3 (Vormonat inkl. Jahreswechsel; nur laufende Dexman-Mandate, Fälligkeit 5./10./15.; Cron legt je Monat genau einmal an, gebunden an das Mandat). Davor: MED-D-649 (Folgeaufgaben für Dexman-Mandate): api/beratungsauftrag.test.ts (Playbook-Test prüft jetzt alle buchbaren Produkte + die vier Dexman-Aufgaben mit Fristen, Zahl unverändert), e2e-fullstack/dexman-flow.spec.ts prüft die Wiedervorlagen beim Buchen in echter D1. Davor: MED-D-646 (Standard-Unterlagen für Dexman-Mandate): domain/unterlagen-vorlagen.test.ts +1 (vier Unterlagen, eindeutige stabile IDs, idempotentes Seed-SQL v68), e2e-fullstack/dexman-flow.spec.ts prüft die Unterlagen beim Buchen in echter D1. Davor: MED-D-643 (Dexman-Controlling buchbar): api/beratungsauftrag.test.ts (Ablehnungs-Test durch "buchbar, an die Praxis gebunden" ersetzt, Zahl unverändert), api/lead.test.ts (Lead bietet genau die buchbaren Produkte an), e2e-fullstack/dexman-flow.spec.ts +1 (Mandat buchen → "Dexman öffnen" zeigt Dexman für die Praxis). Davor: MED-D-638 (Kunden-Erinnerung an fehlende Unterlagen): neue api/unterlagen-erinnerung.test.ts +5 (offene Unterlagen ohne Aufgaben/Dubletten; nächste Aktion; 7-Tage-Takt mit max. 3 Mails und genau einer Wiedervorlage; Zyklus-Ende/abgeschaltet/dormant ohne Portal; HTTP Stand + Schalter), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Schalter in der Akte persistiert in D1). Davor: MED-D-635/637/639 (Basis-Dokumente, Einmal-Link entfällt, 4-Stand-Schieber): api/portal.test.ts +1 (Onboarding-Einladung entsteht automatisch und idempotent über das Portal-Dashboard), e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Überschrift „Basis-Dokumente", kein Einmal-Link-Abschnitt, „Entfällt" im Schieber und zurück). Davor: CodeRabbit-Nachlese #627: api/beratungsauftrag.test.ts +3 (abgelaufene Unterlage zählt als fehlend, unterschriebene nicht; eigenes Flag unterlagenFehler; Rollen-Update ohne team lässt das Team unverändert). Davor: MED-D-630 R-13 (Standard-Mandats-Unterlagen per Migration v66): neue domain/unterlagen-vorlagen.test.ts +2 (nur buchbare Produkte, stabile IDs, idempotentes Seed-SQL); e2e-fullstack/mandats-unterlagen.spec.ts prüft zusätzlich die Standard-Vorlage in echter D1. Davor: MED-D-630 Slice 2 (Mandats-Unterlagen je Produkt): api/beratungsauftrag.test.ts +4 (Reife mit fehlenden Unterlagen, Anlage aus Vorlage idempotent + auditiert, Unterlage ergänzen mandant-scoped, HTTP: Pflege nur Admin/Validierung/Löschen), e2e/verwaltung.spec.ts +1 (Vorlagen je Produkt), neue e2e-fullstack/mandats-unterlagen.spec.ts +1 (Vorlage → Anforderung im Mandat, abhaken persistiert, ergänzen). Davor: MED-D-630 (Onboarding als Abhak-Liste): e2e-fullstack/onboarding-item-vorgang.spec.ts +1 (Kundenakte zeigt Basis-Unterlagen ohne Mandat-Auswahl und ohne "Formular anfordern"). Davor: MED-D-629 (Teams + "Nicht zugewiesen"): domain/rechte.test.ts +2 (Team-Sicht, normalisiereTeam), api/rbac.test.ts +1 (Team setzen nur Admin, Team-Kunde sehen/bearbeiten, anderes Team 404, Team entfernen), e2e/verwaltung.spec.ts +1 (Team je Benutzer, "Chef / Backoffice"), e2e/cockpit.spec.ts +1 (Filter "Nicht zugewiesen"). Davor: MED-D-623 (übrige By-ID-Routen im Kunden-Scope): api/rbac.test.ts +1 (Dokumente, Bankkonten, Onboarding-Items, Signaturen, Dokumentgenerierung, Leistungszeit: fremder Kunde → 404 ohne Wirkung, eigener Kunde → 200, unbekannte ID → Handler-404). Davor: MED-D-384 (eigene Rechtsseiten): neue rechtliches.test.ts +10 (Pflichtkern inkl. Vertretung, nur https://-Overrides, Links nur bei vollständigen Angaben bzw. externe URL mit Vorrang, nur konfigurierte Angaben im Impressum, HTML-Escaping, Datenschutz-Inhalte + Turnstile/DSB/Aufsicht nur wenn konfiguriert) + oeffentlich-site.test.ts +2 (/impressum·/datenschutz auf allen Hosts, Landing verlinkt beide nur bei vollständigem Pflichtkern). Vorher: MED-D-382 (Praxis-Routen im Kunden-Scope): api/rbac.test.ts +1 (Praxis-, Gutachten-, Befund- und Dexman-Routen für fremden Kunden → 404 inkl. PATCH/POST ohne Wirkung, eigener Kunde → 200, Backoffice sieht alles, Flag aus = keine Regression). Davor: MED-D-383 (formelle Fachsprache, keine neuen Tests): domain/dexman-kern.test.ts, api/dexman.test.ts, e2e/dexman.spec.ts, e2e-fullstack/dexman-flow.spec.ts auf die neuen Texte angepasst; Kern-Test prüft zusätzlich die Statusbegriffe je Bereich. Davor: MED-D-381 (Dexman umgesetzt, zwei Oberflächen): neue domain/dexman-kern.test.ts +25 (Rechenkern aus shared/dexman-kern/: Monat/Format, Umsatz/Personal/Material/Fixkosten, Tageslinie, Ist vor Plan, Ein-/Austritt, Gehaltsänderung + Umsatzbeteiligung, Saison/Umsatzänderung, zuflussbasiert, Darlehen fallend/Annuität/Zinsbindung/privat/vermietet/Laufzeitende, Anschaffung/AfA, Ereignisse, Kontokorrentzins, Beispielpraxis deterministisch, Sätze, Hebel, Szenario ohne Seiteneffekt, Prüfregeln) + neue api/dexman.test.ts +8 (Stand/Lage, Versionen append-only, Archivieren, Ist in praxis_jahreswert mit Vorrang, Hebel → Wiedervorlage idempotent, PII-armes Audit + Lesezugriff, HTTP inkl. Rechte je Kunde); domain/zugriff.test.ts um dexman_planung erweitert; neue gemockte e2e/dexman.spec.ts +5 (Kompass: Satz/Tageslinie/Herkunft, Was-wäre-wenn ohne Speichern, Beratungsmodus; Klassisch: Kacheln/Personal/Monate + Umschalten, Formularprüfung) + Full-Stack e2e-fullstack/dexman-flow.spec.ts +1 (Akte → Praxen → Dexman, Ist-Erfassung persistiert, Übernehmen = neue Version, Klassisch zeigt dieselben Daten). Vorher: MED-D-337 (Unterschrift über den Assistenten): api/dokumentgenerierung.test.ts +2 (inkl. wiederherstellbare Verknüpfung) + api/individualdokument.test.ts +1 (Assistenten-Weg führt Quelle nach: Status/Audit, Stufen-Vorgabe AES, zweiter Start 409); neue Full-Stack-Spec e2e-fullstack/unterschrift-generierung.spec.ts +1. Vorher: MED-D-335 (Kundenseite Slice B): domain/naechste-schritte.test.ts +1 (Klartext ohne interne IDs/Jargon); neue Full-Stack-Spec e2e-fullstack/kundenseite-einfach.spec.ts +1 (Nächster Schritt oben, ein Status, "Mehr"-Reiter, kein Unterschrift-Direktversand). Vorher: MED-D-334 (interne Oberfläche Slice A): cockpit.spec.ts +3 (Leitstand-Kopfzeile ohne Überlauf bei 1280 px, Zuweisen per Personenauswahl, Fehlermeldung mit Ursache + nächstem Schritt). Vorher: MED-D-333 (Portal-Aufgabenliste/Fragebogen seitenweise): api/portal.test.ts +3 (Aufgabenliste aus Einladung/Formular/Fragebogen/Dokument, Portal-Mail-Texte, Einladungs-Betreff); E2E portal.spec.ts +1 (Aufgabenliste-Links) + public-strecken.spec.ts +1 (Fragebogen-Assistent). Vorher: MED-D-332 (Kundenseiten Slice A): neue domain/kunden-kontakt.test.ts +3 (leer = dormant, nur http(s)-Links, Berater nur mit Anzeigename) + api/onboarding-formular.test.ts +1 (Kontext liefert Kontakt-Block, nie Roh-E-Mail der Betreuung) → 793; api/portal.test.ts/api/ausfuellformular.test.ts/api/dokumentgenerierung.test.ts an neue Labels/Config-Form angepasst; gemockt e2e/onboarding.spec.ts +2 (Kontakt-Fußzeile mit tel:-Link + fehlender Link ausgeblendet + "Zum Absenden fehlt noch"; keine leere Hilfe-Box ohne Konfig) → 33, e2e/portal.spec.ts um Upload-Button/accept/Bestätigung erweitert. Davor MED-D-331 (K-10): api/formular-vorgaenge.test.ts +2 (ohne Kunden-E-Mail keine Erinnerung/keine Markierung + Nachholen; Zustellfehler nicht-fatal, harte Eskalation läuft weiter) und bestehender Weich-Test auf Empfänger = Kontakt-E-Mail + Fortsetzungs-Link umgestellt → 789. Davor MED-D-330 (Begriffe "Kunde"/"Mandat"): keine neuen Tests, Zahl unverändert — angepasst an die neuen UI-/Fehlertexte: api/ausfuellformular.test.ts (Festwerte "Kunde"/"Kundennummer"), api/dokumentgenerierung.test.ts (Mapping "Kunde"), api/formular-vorgaenge.test.ts (Betreff "beim Kunden nachfassen"), e2e/cockpit.spec.ts + e2e/smoke.spec.ts + e2e-fullstack/smoke.spec.ts + e2e-fullstack/mandant-create.spec.ts (Überschrift "Kunden", "+ Neuer Kunde", Leerzustand). Davor MED-D-329/328 (Einfachheits-Audit): api/ausfuellformular.test.ts +2 (Korrektur-Mail an Kontakt-E-Mail mit absolutem Link, ohne E-Mail kein Versand + kundeBenachrichtigt=false, Provider-Fehler nicht-fatal) → 787; gemockt e2e/cockpit.spec.ts +3 ("Heute fällig" zeigt nur datierte überfällige/heute fällige Punkte, Leerzustand, Fokus-Kürzel Default aus) + e2e/portal.spec.ts +1 (Portal ohne Staff-Kopfzeile, data-theme=light) → 31; Full-Stack neue e2e-fullstack/timer-kein-autostart.spec.ts +1 (Öffnen startet keinen Timer, Klick startet ihn serverseitig) → 9. Davor MED-D-326 (Betriebsinfo im Footer nur für Admins): +1 gemockte E2E in client/e2e/smoke.spec.ts — Nicht-Admin (Berater) sieht keinen Betriebsinfo-Fuß (footer.mw-footer gar nicht gerendert, keine Versionsinfo). 34→35 E2E (26→27 gemockt), Server-Vitest unverändert. Davor MED-D-324 (Version/Deploy dezent in den Footer): +2 gemockte E2E in client/e2e/smoke.spec.ts — Cockpit (Route "/"): Version + Deploy stehen im footer.mw-footer, nicht mehr im header.mw-header; App-Shell (/verwaltung): Version + Deploy im footer.md-footer, nicht im header.md-header. Server-Vitest unverändert (reine Client-/CSS-Änderung). 32→34 E2E (24→26 gemockt), 785 Vitest unverändert. Davor MED-D-323 (MED-OP-MIGRATE-1: Auto-Migration restart-sicher): +3 Server-Vitest / +1 Datei — neue db/migrate.test.ts mit minimalem Fake-D1 (modelliert _meta, ALTER TABLE … ADD/DROP COLUMN-Semantik inkl. Duplikat-Erkennung, atomare batch()): Erststart erreicht die Ziel-Schema-Version; zweiter Aufruf ist ein No-op (idempotent, keine DDL erneut); Absturz mitten in der Migration → Neustart läuft sauber weiter (Version bleibt beim Vorgänger, kein duplicate column). Erste Bausteine Richtung MED-OP-TEST-1 (echtes D1-Harness). 782→785 Vitest (63→64 Dateien). Davor MED-D-322 (OP-DM-8 Mail-Selbsttest): +4 Server-Vitest in api/app.test.ts (admin→Log-Kanal zugestellt=false, keine echte Mail; echter Kanal→sende() mit der eigenen Adresse aufgerufen→zugestellt=true; Provider-Fehler→502 ok:false; Nicht-Admin (berater)→403) +3 gemockte E2E / +1 Datei (neue client/e2e/verwaltung.spec.ts: echter Kanal→Erfolgshinweis, Log-Kanal→"keine Mail zugestellt", 502→Fehlermeldung im Status-Absatz). 778→782 Vitest (Dateien unverändert 63), 29→32 E2E (21→24 gemockt, 14→15 Playwright-Dateien). Davor MED-D-320 (MED-OP-PORTAL-2 Teil 2: mehrere Kontakt-Logins je Mandant): +3 Server-Vitest in api/portal.test.ts (Staff legt mehrere Kontakte an + listet sie (sortiert), doppelte E-Mail→409 email_vergeben; Sperren beendet die laufende Session (Dashboard 200→401) + wird als gesperrt gelistet, Reaktivieren gibt Zugang zurück; Scope-Guard: fremdes Konto sperren→404, unverändert). Client (Kontakt-Liste + Sperren/Reaktivieren in mandant-einladungen) ng build-verifiziert (das Register-Panel liegt tief in mandant-detail — wie die bestehende Einladungen-UI build-only, kein eigenes E2E-Harness). 775→778 Vitest (Dateien unverändert 63), E2E unverändert (29). Davor MED-D-319 (MED-OP-UX-14: Button-CTA-Vereinheitlichung + Verlassen-Modal → md-overlay + Command-Palette-Absicherung): +3 gemockte E2E / +1 Datei — neue client/e2e/cockpit-overlays.spec.ts (Command-Palette: Öffnen über den Kopf-Button + Fokus-Pin aufs Suchfeld + Esc-Schließen; Filtern nach Eingabe + Treffer per ↓ +↵ ausführen → Theme dunkel; Backdrop-Klick schließt). Server-Vitest unverändert (reine Client-/CSS-Änderung: Primary-CTA --md-green überall, Verlassen-Modal nutzt jetzt die geteilte md-overlay-Dialogmechanik statt hand-gerollt). 26→29 E2E (18→21 gemockt, 13→14 Playwright-Dateien), 775 Vitest unverändert. Davor MED-D-318 (UI/UX-Konsistenz- & Rough-Edges-Pass): +0 Tests — reine Konsistenz-/Client-/CSS-Änderungen (Theming-Tokens, Terminologie/Labels, Ladeindikatoren, Fokus-/Doppel-Submit-Guards, labels.ts-Nachbauten zurückgeführt). Kein neues Server-Verhalten (die Landing-Farb-/Wortmarken-Änderung berührt oeffentlich-site.ts nur kosmetisch → bestehende oeffentlich-site.test.ts unverändert grün); die Client-Änderungen sind über ng build + die bestehende client/e2e-Suite (u. a. Portal-Upload) gedeckt, die Fokus-/CSS-Pfade sind ohne zusätzlichen Harness nicht sinnvoll unit-testbar. Button-/Overlay-Vereinheitlichung ist als MED-OP-UX-14 getrackt (braucht erst Visual-Regression-Deckung). 775 Vitest / 18 gemockt unverändert. Davor MED-D-317 (WIP-Landing auf medidentas.com + robots.txt/KI-Trainings-Hinweise; inkl. CodeRabbit-Nachlese #343): +11 Server-Vitest / +1 Datei — neue oeffentlich-site.test.ts (istMarketingHost Apex/www vs. App-Host; robotsTxt host-abhängig — App-Host Disallow:/ + KI-Crawler gesperrt, Marketing-Host Allow:/ + KI-Crawler gesperrt; ai.txt nennt DSM/§44b + Disallow:/; tdmrep.json reserviert /; behandleOeffentlicheSite — robots/ai/tdmrep auf allen Hosts mit X-Robots-Tag: noai/tdm-reservation:1-Headern, Apex /→WIP-Landing noindex, App-Host /→null (regulär weiter), HEAD ohne Body, nicht-GET→null, Landing verweist auf app.medidentas.com). Die CodeRabbit-Nachlese #343 (dokFarbe-Theme-Tokens --warn/--muted→--amber/--mut; per-Dokument-Upload-Set statt Einzel-Signal; Decision-Log-Backtick) ist über den bestehenden client/e2e-Portal-Upload-Test + ng build abgedeckt (reine Client-/Doku-Korrektur, kein neuer Vitest); die magic-byte-Content-Prüfung bleibt bewusst Backlog (dokumentierter Tradeoff). 764→775 Vitest (62→63 Dateien), E2E unverändert (26). Davor MED-D-316 (Portal-Dokument-Upload, MED-OP-PORTAL-2 Teil 1): +9 Server-Vitest / +1 Datei — neue domain/upload-pruefung.test.ts (+4: pruefeUpload ok · leere Datei → leer · über 15 MB → zu_gross · unerlaubte Endung inkl. Doppel-Endung script.pdf.js → typ_unzulaessig) + api/portal.test.ts (+5: Upload-Route dormant → 404 / ohne Session → 401; fremdes/unbekanntes Dokument → 404 kein Cross-Mandant-Leak; angefordertes Dokument → Upload 200 + Status erhalten + im Dashboard sichtbar; bereits geprüft → 409 nicht_offen; unzulässiger Typ → 400) + +1 gemockte E2E in client/e2e/portal.spec.ts (angefordertes Dokument im Portal hochladen → Erfolg, kein Fehler). Server-Route unter /oeffentlich/portal/dokumente/:docId/upload (Session-scoped, eigener Mandant, R2 via bestehende DokumentService.hochladen); Token-Upload-Route zusätzlich mit pruefeUpload gehärtet. 755→764 Vitest (61→62 Dateien), 25→26 E2E (17→18 gemockt, Dateien unverändert 13). Davor MED-D-315 (Portal-Zugang automatisch beim Kunden-Anlegen): +4 Server-Vitest in api/portal.test.ts (zugangSicherstellen: dormant → kein Konto/kein Send; aktiv + neue E-Mail → Konto angelegt + Invite genau einmal, zweiter Aufruf idempotent; ohne verwertbare Kontakt-E-Mail nichts; HTTP POST /api/mandanten mit E-Mail + aktivem Portal → Konto auto-angelegt + Invite). 751→755 Vitest (Dateien unverändert 61), E2E unverändert. Davor MED-D-314 (Cloudflare Email Service als einziger Mail-Provider, MailChannels raus): −6/+5 Server-Vitest (netto −1) — benachrichtigung/mailchannels.test.ts entfernt (−6); neue benachrichtigung/cloudflare-email.test.ts (+5): cloudflareEmailKonfigAusEnv-Defaults/Trim; Versand über das gemockte send_email-Binding mit From-Objekt + Magic-Link im Text-Körper; empfänger-loser Aufruf still übersprungen (kein send); Binding-Fehler wirft mit code, ohne Empfänger-PII. 752→751 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-312 (CodeRabbit-Nachlese zu #339: Login-Härtung + Mailversand-Robustheit + Rate-Limit-Retention; inkl. Round-2-Review nach "ready for review"): +6 Server-Vitest — api/staff-login.test.ts +1 (Staff-Session überschreibt keine abweichende Access-Identität: A's Session-Cookie + B's Access-Header → es gilt B/backoffice, nicht A/admin; passende Access-Identität → A/admin) + api/portal.test.ts +3 (bereinigeRateLimits löscht nur abgelaufene Fenster; Login-Mail-Zustellfehler nicht-fatal → Login-Flow läuft weiter + Audit zustellung:fehler; erfolgreiche Zustellung → zustellung:ok) + api/wiedervorlage.test.ts +2 (Erinnerungs-Item-Isolation: ein Fehlversand stoppt die übrigen nicht + bleibt retrybar; Audit-Fehler NACH erfolgreicher Zustellung → keine Wiederholung/kein Doppelversand). Übrige Fixes (MailChannels-8s-Timeout inkl. Fehler-Body-Read, PORTAL_AKTIV-Startup-Warnung, Turnstile-Token-Reset im Client) sind über die bestehenden Tests + tsc/ng build abgedeckt (die Timeout- und Client-Widget-Pfade sind ohne Netz-/Browser-Harness nicht sinnvoll unit-testbar). 746→752 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-311 (Portal-Login-Härtung: Turnstile + Rate-Limiting am öffentlichen anmelden, löst MED-OP-PORTAL-3): +3 Server-Vitest in api/portal.test.ts (GET /oeffentlich/portal/config dormant→404 / aktiv→{turnstileSitekey:null}; Rate-Limit je E-Mail — die 6. Anmeldung im 10-Min-Fenster → 429 rate_limit, eine andere E-Mail behält ihr eigenes Fenster; bei gesetztem turnstileSecret ohne Token → 400 bot_check vor dem Rate-Limit). Der neue repo.pruefeRateLimit (Tabelle rate_limit, Migration v62) ist über den Rate-Limit-Test gegen den Memory-Repo abgedeckt; Client (Turnstile-Widget auf der Portal-Login-Seite) ng build-verifiziert + portal-E2E-Login-Mock um /portal/config ergänzt. 743→746 Vitest (Dateien unverändert 61), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-310 (Portal-Go-Live: echter Mailversand MailChannels/OP-DM-8 + Kundenportal scharf): +6 Server-Vitest / +1 Datei — neue benachrichtigung/mailchannels.test.ts (gegen gemocktes fetch: mailChannelsKonfigAusEnv dormant ohne MAILCHANNELS_API_KEY; Defaults + DKIM nur bei allen drei Feldern; sende POSTet Payload + X-Api-Key, Magic-Link steht im Körper; DKIM je Personalisierung mitgesendet; Empfänger-loser Aufruf still übersprungen (kein fetch); HTTP-Fehler wirft mit Status, ohne Empfänger-PII). portal.test.ts an die korrigierte Magic-Link-Form (/portal?token= Client-Route statt API-Endpunkt) angepasst — Token wird aus dem Link gezogen und gegen den API-Endpunkt eingelöst. Client (neuer "Kundenportal"-Abschnitt in der Akte, mandant-einladungen) ng build-verifiziert; der Portal-Konto-Endpunkt ist bereits durch portal.test.ts abgedeckt. 737→743 Vitest (60→61 Dateien), E2E unverändert (17 gemockt/25 gesamt). Davor MED-D-309 (Staff-Magic-Link-Login "hinter Access", dormant): +7 Server-Vitest / +1 Datei — neue api/staff-login.test.ts (alle 3 Routen dormant→404 ohne STAFF_LOGIN_AKTIV, Golden Rule; Anmelden ohne Konto-Enumeration → unbekannte E-Mail identische ok-Antwort ohne Link; ungültige E-Mail → 400; Bootstrap-Admin m.kirchhof@drkv.com UND per Verwaltung angelegter Mitarbeiter erhalten je einen Link = zweite Nutzeranforderung; voller Magic-Link-Flow anfordern→einlösen→staff_session-Cookie + Token-Replay → 401; ungültiger Token → 401; Staff-Session etabliert die Identität hinter Access unter enforced OHNE Access-Header → /api/ich 200 Rolle=admin, ohne beides → 401, nach Logout → 401) + +3 gemockte E2E / +1 Datei — neue client/e2e/staff-login.spec.ts (ohne Token → Mitarbeiter-Anmeldeformular → Magic-Link angefordert; Token einlösen bei dormantem Login (404) → "zurzeit nicht verfügbar"; ungültiger Token (401) → "neuen Link anfordern"). Migration v61 (staff_anmelde_token/staff_session, additiv) via Memory-Repo + tsc mitverifiziert; Client staff-login.component.ts ng build-verifiziert. 730→737 Vitest (59→60 Dateien), 22→25 E2E (14→17 gemockt, 12→13 Playwright-Dateien). Davor MED-D-308 (S5-UI: Kunden-Self-Service-Portal Client-UI, dormant): +3 gemockte E2E / +1 Datei — neue client/e2e/portal.spec.ts (dormant PORTAL_AKTIV=aus → "Kundenportal zurzeit nicht verfügbar"; nicht angemeldet → Anmelde-Formular → Magic-Link angefordert; angemeldet → Dashboard zeigt Onboarding-Fortschritt/offene Items/Formular-Status + proaktive S4-Einreichung wird bestätigt), gegen die gemockten Portal-Endpunkte. Server unverändert (nur bestehende Endpunkte konsumiert); portal-public.component.ts + Portal-API-Methoden ng build-verifiziert. 19→22 E2E (11→14 gemockt, 11→12 Playwright-Dateien), 730 Vitest unverändert. Davor MED-D-307 (N5: Onboarding-Einwilligung über DocuSign statt SES-Attrappe, flag-gated/dormant, OP-SIGN-1): +3 Server-Vitest in api/onboarding-formular.test.ts (Flag an + Signatur-Dienst → Einwilligung als echte SES über den Fake-Provider angefordert/pending/PDF-Beleg, Audit einwilligung.angefordert statt .erteilt; Flag an ohne Dienst → SES-Attrappe (dormant-sicher); Provider-Fehler → consent_provider_fehler, Einladung bleibt offen — kein stiller Fallback). Client unverändert (Danke-Seite bleibt; die DocuSign-Mail ist der Folgeschritt). Der DocuSign-Pipeline-Pfad (anfordern→Envelope→Webhook→abgleich) ist bereits durch die Full-Stack-docusign-flow.spec.ts abgedeckt; der globale Fullstack-Flag bleibt aus (Onboarding-E2E prüft weiter den SES-Default). 727→730 Vitest, E2E unverändert (19). Davor MED-D-306 (kollisionssicheres Hash-Schema 3 + Ablehnung unbekannter Schema-Marker, G-4; CodeRabbit-Nachlese zu #332): +4 Server-Vitest in domain/stammdaten-version.test.ts (Schema 2 kollidiert bei =/|-Freitext, Schema 3 nicht — behebt den Trenner-Injektions-Fund; kanonischerSnapshot wirft bei unbekanntem Schema statt still zurückzufallen; pruefeVersionsKette meldet einen unbekannten hashSchema-Marker als Bruch; Umschreiben eines Bestands-Markers 2→3 wird erkannt). Server-only Härtung; Full-Stack-E2E 8/8 (schema-3-Schreibpfad gegen echten Worker) unverändert. 723→727 Vitest, E2E unverändert (19). Davor MED-D-305 (feldset-tolerante Stammdaten-Hash-Kette, G-4; die 6 Onboarding-Grunddaten jetzt versioniert): +6 Server-Vitest in domain/stammdaten-version.test.ts (neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil = Regressions-Lock der Bestands-Ketten; Schema-2 serialisiert feldset-tolerant über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation an einer Schema-1-Zeile erkannt; gemischte Kette Schema-1 + neue Schema-2-Zeile mit den 6 neuen Feldern bleibt intakt — Kern von MED-D-305; künftiges Feld-Wachstum bricht eine Schema-2-Zeile nicht, ein nachträglich eingemischtes Feld schon = Tamper-Evidenz). Client (StammdatenSnapshot/StammdatenVersion-Typen + 6 md-versioned-field-Zeilen build-verifiziert; Migration v60 von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert, 8/8 grün). 717→723 Vitest, E2E unverändert (19). Davor MED-D-304 (Kunden-Self-Service-Portal S3 Dashboard + S4 proaktive Einreichung, dormant): +5 Server-Vitest in api/portal.test.ts (S3 Dashboard dormant→404/ohne-Session→401/kompakte Sicht; S4 Vorschlag dormant→404/ohne-Session→401/keine-Whitelist-Felder→400; voller Round-Trip Einreichen→Berater-Liste→Übernahme (4-Augen, self_service→Onboarding-Regelset)→Stammdaten geschrieben; Idempotenz je Mandant). Client (Portal server-only; Berater-Sektion mandant-formulare build-verifiziert — kein e2e-Harness für diese Fläche). 712→717 Vitest, E2E unverändert (19). Davor MED-D-303 (Item↔Mandat-Zuordnung): +3 Server-Vitest + 1 Full-Stack-E2E / +1 Datei — api/app.test.ts +3 (Item einem Mandat zuordnen + wieder lösen; fremde/unbekannte Auftrags-ID → 404 kein Cross-Mandant-Verweis; fremdes Item → 404 + ungültige auftragRef → 400) + neue client/e2e-fullstack/onboarding-item-mandat.spec.ts 1 (Zuordnen → Persistenz-Roundtrip aus D1 → Lösen + Cross-Mandant-Guard, Migration v59 gegen den echten Worker). 709→712 Vitest, 18→19 E2E (7→8 Full-Stack, 10→11 Playwright-Dateien). Davor MED-D-302 (Onboarding-Greenfield G5 — dormante ausfuellformular-Tabelle + Repo-Methoden entfernt): +0 Tests — reiner Dead-Code-Abbau (die entfernten Methoden hatten seit G3E2 keine Test-Usages mehr); Migration v58 (DROP TABLE IF EXISTS ausfuellformular) wird von der bestehenden Full-Stack-E2E-Suite gegen den echten Worker mitverifiziert (7/7 grün, sauberer Boot). 709 Vitest unverändert. Davor MED-D-301 (Onboarding-Greenfield G4 — Item↔Vorgang-Link + Bedarf je Mandat): +2 Server-Vitest + 2 Full-Stack-E2E / +1 Datei — api/ausfuellformular.test.ts +1 (erstellen mit onboardingItemId setzt den Item-Link + auditiert ihn; zweites Anfordern desselben Items ist idempotent, ein Vorgang je Item), api/beratungsauftrag.test.ts +1 (liste liefert den abgeleiteten benoetigt-Bedarf aus dem Produkt-Playbook, G-2) + neue client/e2e-fullstack/onboarding-item-vorgang.spec.ts 2 (Item↔Vorgang-Roundtrip gegen echten Worker/D1: "Formular aus Item anfordern" → Link + Liste + idempotente Wiederverwendung; gebuchtes Mandat → benoetigt abgeleitet). 706→709 Vitest, 16→18 E2E (5→7 Full-Stack, 9→10 Playwright-Dateien). Davor MED-D-299 (Onboarding-Greenfield G3 Etappe 3a — Onboarding-Strecke auf die Feld-Engine): +0 Tests, verhaltensneutral — die Onboarding-Grunddaten rendern jetzt über PubFeldEngineComponent; die bestehende client/e2e/onboarding.spec.ts (Lücken-Report · Happy-Path bis "Vielen Dank!" · ungültiger Token) deckt die Strecke über dieselben Feld-IDs weiter ab und bleibt 11/11 grün. Davor MED-D-298 (Onboarding-Greenfield G3 Etappe 2 — Ausfüll-Flow auf den einen Service): −7 Vitest / −1 Datei — die G2-Lean-Service-Tests (api/formular-vorgang.test.ts) sind entfallen (der Lean-Service wurde durch den voll-ausfüll-fähigen FormularVorgangService ersetzt); die bestehende api/ausfuellformular.test.ts (Domäne + Service + HTTP-Fluss) testet jetzt den neuen FormularVorgangService gegen den formular_vorgang-Store (Import/Repo-Zugriffe umgestellt, identische Erwartungen = Contract-Netz); api/formular-vorgaenge.test.ts (SLA/Öffnungs-Tracking) ebenso auf den neuen Store repointet. Full-Stack-E2E (formular-sichtung.spec + DocuSign) verifiziert den Ausfüll-Flow gegen den echten Worker auf dem neuen Store (5/5). 713→706, 60→59 Dateien. Davor MED-D-297 (Onboarding-Greenfield G3 Etappe 1 — geteilte Feld-Engine): +2 gemockte E2E / +1 Datei — client/e2e/formular.spec.ts (die PubFeldEngineComponent rendert die Ausfüll-Formular-Felder inkl. Marker-Chips; Pflichtfeld füllen → Vorschau → rechtsverbindlich absenden → "Vielen Dank!" mit Beleg-Nr.; leeres Pflichtfeld → Vorschau blockiert mit ehrlichem Hinweis). Server-Vitest unverändert (reine Client-Konsolidierung). 14→16 E2E, 8→9 Playwright-Dateien. Davor MED-D-296 (Onboarding-Greenfield G2 — eine FormularVorgang-Entität + Service): +8 Server-Vitest / +1 Datei — api/formular-vorgang.test.ts 7 (schema-getriebener Lebenszyklus: erstellen inkl. Item-Link + Audit · kontext/Öffnungs-Tracking · einreichen mit Pflichtprüfung/Prüfsumme + Doppel-Einreichung-Konflikt · Abschnitts-Freigabe N2 idempotent · Feld-Ablehnung → Korrektur-Schleife inkl. Rücksetzen bei Re-Einreichung · Wächter · Integration mit dem G1-Adapter-Schema) + domain/feld.test.ts +1 (fehlendePflicht: Wert ODER Markierung erfüllt Pflicht, DM-9). Migration v57 (formular_vorgang) via Full-Stack-E2E gegen den echten Worker verifiziert (5/5, sauberer Boot). 705→713, 59→60 Dateien. Davor MED-D-295 (Onboarding-Greenfield G1 — kanonisches Feld-Modell): +9 Server-Vitest / +2 Dateien — domain/feld.test.ts 5 (reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt + Schema-Konsistenz-Invariante schemaVerstoesse) + domain/formular-schema.test.ts 4 (die 3 Adapter überführen die Alt-Kataloge treu: Onboarding pflicht:bool→wichtigkeit+select→auswahl, Ausfüll-KundenFeld 1:1, Fragebogen klasse→wichtigkeit+block→Abschnitt+Öffnungszeiten-strukturTyp; Feldzahl erhalten, jedes Schema konsistent). 696→705, 57→59 Dateien. Davor MED-D-293 (Onboarding-Vereinfachung N1, 1. Schritt — geteilte Bausteine der öffentlichen Kundenstrecken): +0 Tests, verhaltensneutral — die wortgleich kopierte Lade-Fehler-Auswertung + Turnstile-Callback-Bindung sind jetzt geteilte Bausteine in pub-bausteine.ts (ladeFehlerAuswerten/bindeTurnstileCallback); die bestehende gemockte E2E-Suite (client/e2e/, u. a. "ungültiger Token"/"ungültige Fragebogen-Einladung") deckt die Fehler-/Zugriffsschutz-Pfade der 4 Public-Strecken ab und bleibt 9/9 grün (Client hat keine Unit-Test-Infrastruktur). Davor MED-D-292 (Onboarding-Vereinfachung N2, Abschnitts-Freigabe): +4 Server-Vitest (domain/feld-sichtung.test.ts +1 akzeptiereOffene — akzeptiert nur offene Felder, lässt eine Ablehnung mit Grund unangetastet, idempotent; api/ausfuellformular.test.ts +3 alleAkzeptieren — 1-Klick-Freigabe akzeptiert alle offenen + Audit PII-arm, zweiter Aufruf nichts_offen, eine vorherige Ablehnung bleibt bestehen und öffnet beim Abschließen die Korrektur-Schleife, nur für eingereichte Formulare) + +1 Full-Stack-E2E (client/e2e-fullstack/formular-sichtung.spec.ts: einreichen → Abschnitt in 1 Klick freigeben → abschließen gegen den echten Worker; deckt den atomaren read-modify-write der Sichtung in D1 ab). 692→696 Vitest, 13→14 Playwright (7→8 Dateien). Davor MED-D-290 (Full-Stack-E2E gegen den echten Worker, löst MED-OP-E2E-1 ein): +4 Playwright-Tests in client/e2e-fullstack/ gegen den ECHTEN Worker (wrangler dev · workerd · lokale D1 + R2 · SIGNATUR_PROVIDER=fake) statt gemockter API — App-Shell-Smoke + Health, Mandant anlegen → persistiert → Reload (echter Browser→Worker→D1-Pfad), DocuSign-Flow (Mandant→Onboarding→Ablage provisionieren→Datei ablegen→zur Unterschrift senden→Vor-Ort-/Demo-Abschluss→Dokument "erhalten" + signierte Rückablage; echter D1/R2-Write-back + Provider-Lebenszyklus). Ein Origin bedient UI + /api (wie Produktion), Overrides nur fürs Testen (AUTH_ENFORCED=false, Fake-Signatur). CI-Job client-e2e-fullstack (gated bei Client- oder Server-Änderung, Teil des Pflicht-Checks checks). Deckt den echten Datenpfad ab (Migration, D1-SQL, R2-Ablage), den die In-Process-Vitest nicht durchläuft. Davor MED-D-289 (E2E-/UI-Test-Infrastruktur, gemockte API): +9 Playwright-Tests in client/e2e/ — App-Shell-Smoke (bootet + Marke/Cockpit + Navigation), Cockpit (Mandantenliste rendert gelieferte Mandanten / ehrlicher Leerzustand), Onboarding-Kundenstrecke (3-Schritte-Flow: Lücken-Report bei fehlender Pflichtangabe · vollständiger Weg bis "Vielen Dank!" · ungültiger Token), öffentliche Strecken (Lead-Formular rendert · ungültige Fragebogen-Einladung). Sie laufen gegen den fertigen Angular-Build (dist/browser via e2e/static-server.mjs) mit gemockter API (Playwright-route, e2e/support/mock-api.ts) — deterministisch, ohne Worker/D1/Secrets. In CI als client-e2e-Job (gated wie client-build, Teil des Pflicht-Checks checks, "immer laufen lassen"). Erste Client-Test-Infrastruktur überhaupt — löst die bisherige "nur ng build"-Lücke (MED-D-267 ff.). Volllast-E2E gegen echten Worker + D1 (wrangler) ist als Folge-Slice MED-OP-E2E-1 notiert. Server-Vitest 692 unverändert (reine Client-/CI-Ergänzung). Davor MED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem "wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Test — domain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Tests — api/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Tests — api/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → "nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte "Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistik → ng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Tests — signatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Tests — api/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Tests — auth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Tests — api/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Tests — api/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Tests — api/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Tests — api/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Tests — api/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 "keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + "Zu fakturieren"-Filter): +1 Test — api/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Tests — api/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Tests — domain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Tests — domain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Tests —
domain/kunden-vollstaendigkeit.test.ts +2 (abgelaufen = fällig/wichtig/Stufe aktiv, blockiert nicht; getrennt von
fehlt), domain/naechste-schritte.test.ts +2 (dokumente-erneuern-Schritt + Sortierung wichtig zwischen
Blocker und Wiedervorlage-Hinweis), api/dokument.test.ts +3 (Service detail/offenePunkte leiten den
Ablauf ab; keine Wiedervorlage angelegt = Backlog sauber): 603→610 (keine neue Datei).
Davor MED-D-244 (AD-006-Nachlese, Dokument-Scope-Guard auf den Signatur-Routen):
api/signatur.test.ts +1 Test (HTTP-Guard: fremder Mandant → POST /signatur + /signatur/vor-ort
liefern 404 ohne Provider-Aufruf; eigener Mandant → 201 mit Empfänger-Override): 602→603 (keine neue Datei).
Davor MED-D-243 (AD-006 editierbarer Empfänger im Unterschriften-Assistenten):
api/signatur.test.ts +2 Tests (aufzeichnender Stub-Provider: anfordern ohne Override → Mandant-Kontakt,
mit Override → gewählte Person; vor Ort → Override-Name greift, E-Mail bleibt optional): 600→602 (keine neue
Datei). Davor MED-D-241 (Akten-Cockpit-Design-System exakt übernommen): api/app.test.ts
+1 Test (direkte, berater-editierbare Stammdaten telefon/geburtsdatum/strasse/plz/ort via PATCH → persistiert
- erzeugt genau eine neue append-only Stammdaten-Version (G-4), leerer Wert löscht das Feld — weiche
Validierung): 599→600 (keine neue Datei). (MED-D-242 „Über den Link hochgeladen" war reine Client-Sicht →
keine neuen Server-Tests, 600 unverändert.) Davor MED-D-238 (DocuSign end-to-end live verifiziert + Private-Key-Parser gehärtet):
signatur/docusign.test.ts+1 Test (verstümmelter PEM-Delimiter — 4 Trailing-Hyphens, keine Umbrüche, literale\n— wird trotzdem geparst, JWT-Signatur läuft durch): 598→599 (keine neue Datei). Davor MED-D-237 (In-Person-Host = eingeloggter Berater):signatur/docusign.test.ts+1 Test (Adapter bevorzugt den übergebenen Berater-Host vor der Default-Konfig, bei Envelope-Anlage und Recipient-View): 597→598. Davor MED-D-236 (echte Vor-Ort-/In-Person-Signatur, OP-SIGN-1):signatur/docusign.test.tsum 5 Tests erweitert (Adapter:vorOrtVorbereitenerstellt captive In-Person-Envelope ohne E-Mail-Versand + verlangt Host + lässtsignerEmailohne Kunden-Mail weg,vorOrtSignaturUrlliefert die Recipient-View-URL über die Host-Identität,docuSignKonfigAusEnvliest Host;SignaturService:vorOrtVorbereiten→eingebettet:truegegen DocuSign /false+nicht_verfuegbargegen Fake): 592→597 (keine neue Datei). Davor MED-D-235 (Fremdformular-Feldmapping, AD-010/MED-OP-FORM-5, Weg A): drei neue Dateien —pdf/pdf-formular.test.ts(4 Tests: AcroForm-Felder lesen inkl. Art/Seite/Optionen, leeres PDF ohne Formular, Text/Auswahl/Ankreuz füllen ohne Flatten, unbekannte Feldnamen defensiv),domain/fremdformular.test.ts(6 Tests: Weltmodell-Katalog eindeutig + Leser,anschrift.komplett,bank.ibanKonto-vor-Alt-IBAN,bereinigeMapping,bauePrefillFelder/-Werte),api/fremdformular.test.ts(7 Tests: Service ansicht/mapping/vorbefuellen + PII-armes Audit + Fehlerpfade, plus HTTP-Route-Test übercreateApp+ FakeNextCloud): 574→591 Tests, 51→54 Dateien. Davor MED-D-231 (B10/MED-OP-UX-8, Auftrag-Playbook-Fehler sichtbar):api/beratungsauftrag.test.tsum 2 Tests erweitert (Anlage mit scheiterndem Playbook →playbookFehler:true+ Auftrag bleibt persistiert + PII-armesauftrag.playbook_fehler-Audit; Statuswechsel mit scheiterndem Status-Playbook →playbookFehler:true+ Wechsel gelingt): 572→574 (keine neue Datei). Davor MED-D-227 (Akten-Cockpit Slice B, In-Person-Signatur):api/signatur.test.tsnetto +1 — der Vor-Ort-Abschluss-Test prüft jetzt Dokument→erhalten(stattunterschrieben) + Onboarding-Item unangetastet + Auditsignatur.vor_ort_abgeschlossen, plus ein neuer Idempotenz-Test (571→572, keine neue Datei). Davor MED-D-223 (Akten-Cockpit Slice A, AD-008 DocumentRow-Zustandsmaschine):api/signatur.test.tsum 8 Tests erweitert (Service:erinnernbest-effort+Audit / unbekannt→null;signiertHochladenDokument→erhalten+Scan-Ablage+Audit / unbekannt→null; HTTP:POST .../erinnern200/404,POST .../signiert-hochladen200/404): 563→571 Tests (51 Dateien, keine neue Datei). Davor MED-D-220 (DocuSign-HTTP-Adapter): neuesignatur/docusign.test.ts(10 Tests gegen gemocktesglobalThis.fetch+ real generierten PKCS#8-Schlüssel: reine HelfermappeStatus/parseWebhook/verifyDocuSignHmac/docuSignKonfigAusEnv-Dormancy; Adapter —anfordernerstellt Envelope mitdocumentBase64+Signer+eventNotification, wirft ohne Bytes/Unterzeichner,statuslädt die kombinierte signierte Fassung, keinabschliessenDemo;SignaturService-Integration — Ablage-Bytes + Mandanten-E-Mail werden an den echten Provider durchgereicht,vorOrtAbschluss→ 409/nicht_verfuegbar): 553→571 Tests, 50→51 Dateien. Davor MED-D-204 (UX-Welle 10 Slice 2, B15 Playbook-Feedback):api/app.test.tsum 1 Test erweitert (erfolgreicher Lifecycle-Wechsel liefertplaybookFehler:false) + bestehender Playbook-Fehler-Test um dieplaybookFehler:true-Antwort-Assertion ergänzt: 552→553 Tests. Davor MED-D-198 (Akten-Cockpit S5, Unterschriften-Assistent):api/signatur.test.tsum 5 Tests erweitert (Vor-Ort-/Demo-Abschluss inkl. Write-back R4→R3→R2; unbekannter Vorgang → null; Provider ohne Demo-Seam →nicht_verfuegbar(kein Fälschen echter Vorgänge); HTTPPOST .../vor-ort-abschluss200/404): 547→552 Tests. Davor MED-D-195 (Akten-Cockpit S3a, 1:n-Bankkonten): neueapi/bankkonto.test.ts(12 Tests: HTTP-CRUD inkl. IBAN-Normalisierung + Archivieren + 400-Validierung + 404; Backfill-Idempotenz, Crash-Teillauf, Nebenläufigkeit, identische-IBAN-kein-Dedup, leere-Felder-kein-Konto, UNIQUE-quellSchluessel-Ablehnung): 535→547 Tests, 49→50 Dateien. Davor MED-D-179 (Stammdaten-Versionierung, Slice 1): neuedomain/stammdaten-version.test.ts(5 Tests:snapshotVon/snapshotGleich, Hash-Ketten-Bau + Verify, Manipulations-Erkennung,feldHistorie) + neueapi/stammdaten-versionierung.test.ts(7 Tests: v1-Seed beicreateMandant, v2 bei Feldänderung mit intakter Kette + Herkunft, kein Rausch-Bump bei reiner lifecycle-/ verantwortlich-Änderung,self_service-Herkunft, idempotenter Backfill, HTTP-Route + Berater-PATCH-Version): 523→535 Tests, 47→49 Dateien. Davor MED-D-177 (CodeRabbit-Nachlese zu MED-D-176, RBAC-Sicherheitsfund):rbac.test.ts+1 Test (By-ID-Wiedervorlage-Routen für fremden Mandanten → 404, für eigenen → 200- Regression, erledigen + wieder öffnen): 522→523 Tests. Davor MED-D-174 (CodeRabbit-Nachlese zu MED-D-172, RBAC-Sicherheitsfund):timer.test.ts+3 Tests (neue RBAC-Describe-Gruppe —/api/timer/start/buchenfür fremden Mandanten → 404, für eigenen → 201-Regression): 519→522 Tests. Davor MED-D-172 (Aktiver Timer, ein Timer je Benutzer statt je Browser-Tab, OP-TIME-3): neueapi/timer.test.tsmit 25 Tests (Service-Ebene: Start/ Konflikt/Idempotenz/Pause/Fortsetzen/Korrigieren/Buchen mit und ohneweiterlaufen/Verwerfen; HTTP-Ebene: alle/api/timer*-Routen inkl. Validierung + 409-Konfliktfälle): 494→519 Tests, 46→47 Dateien. Davor MED-D-158 (CodeRabbit-Nachlese zu MED-D-157:leistungszeit.test.ts+1 Test für missgebildetenauftragRef-Typ, z. B. Zahl/Objekt statt Text/null): 493→494 Tests. Davor MED-D-157 (Zeiterfassungs-Mandat-Zuordnung:leistungszeit.test.ts+2 Tests fürerfassen()mitauftragRef+ HTTP-Validierung unbekannter/fremder Aufträge): 491→493 Tests. Davor MED-D-152 (CodeRabbit-Nachlese zu MED-D-151:app.test.ts+1 Test für Best-effort-Playbook-Fehlerbehandlung inservice.ts#aktualisieren()): 490→491 Tests. Davor MED-D-151 (PROZESS_PHASEretiriert,domain/phasen.test.ts→ neuedomain/lifecycle.test.ts, Guard-Matrix + Playbook-Katalog;beratungsauftrag.test.ts+2 Tests für das neue Auftrags-Status-Playbook, Idempotenz + Fehler-Isolation): 488→490 Tests). Davor MED-OP-VALID-1 (kanonischer Feld-Typ + KI-Formatvorschlag, immer weich): Slice 1 (MED-D-146) +18 Tests (feld-typ.test.ts,regel-vorschlag.test.ts, erweiterteausfuellformular.test.ts); Slice 2 (MED-D-147, Praxis-PLZ im App-internen Formular) +2 Tests inpraxis.test.ts; Slice 3 (MED-D-149, PLZ↔Ort-Plausibilität via OpenPLZ) +13 Tests (openplz.test.ts,fake.test.ts, erweitertepraxis.test.ts); Slice 4 (MED-D-150, Onboarding-Formular) +5 Tests (erweiterteonboarding-formular.test.ts+stammdaten-uebernahme.test.ts). Pflege (verbindlich, MED-D-701): je PR ein Fragmenttest.d/<YYYY-MM-DD>-<branch-slug>.mdmit den neuen/entfernten Testdateien anlegen (analogchangelog.d/) und die Tabellen unten fortschreiben; die Gesamtzahlen pflegt niemand — sie werden beim Bauen aus den Test-Dateien gemessen.
E2E-/UI-Tests lokal (MED-D-289):
cd client && npm run e2e:ci(baut den Client und läuft Playwright) odernpm run e2egegen einen vorhandenendist/browser-Build;npm run e2e:reportöffnet den letzten HTML-Report. Der Browser ist auf @playwright/test 1.56.0 (Chromium-Build 1194) gepinnt; die Umgebung stellt Chromium vorinstalliert bereit (/opt/pw-browsers), in CI lädt der Jobclientden versionsgleichen Browser viaplaywright install. Neue UI-Flächen bekommen einen*.spec.tsinclient/e2e/(Selektoren gegen das reale DOM, API viainstallApiMocks).
Full-Stack-E2E lokal (MED-D-290/MED-OP-E2E-1):
cd client && npm run e2e:fullstack:ci— baut den Client und startet übere2e-fullstack/start-worker.mjsden echten Worker (wrangler dev, lokale D1 + R2,AUTH_ENFORCED=false+SIGNATUR_PROVIDER=fake), Playwright läuft gegenhttp://127.0.0.1:8787(ein Origin = UI +/api, kein Mock). Braucht server-Deps (wrangler/workerd). Neue durchgehende Fach-Flows (Persistenz, Signatur/DocuSign, Ablage) bekommen einen*.spec.tsinclient/e2e-fullstack/.
0.47.0(D-49, Live-Zeiterfassung im Kunden-Detail): UI (Timer/Guard/Register inmandant-detail.component.ts+zeiterfassung.guard.ts) ist clientseitig → Absicherung über den Angular-Produktions-Build (ng build). Die admin-konfigurierbaren Parameter (Nachfrage-Schwelle + Buchungs-Rundung) und die Buchungs-Rundungsregel (buchungsminuten) sind server-seitig indomain/einstellungen.tsgetestet: +3 Vitest ineinstellungen.test.ts(Schwelle/Rundung +buchungsminuten).
Was läuft wo?
| Ebene | Werkzeug | Umfang | Ausführen |
|---|---|---|---|
Server (server/) | Vitest + tsc | Unit-/HTTP-Tests (aktuelle Zahl s. Kopf dieser Seite) | npm test · npm run typecheck |
Client (client/) | Angular Produktions-Build | Kompiliert die gesamte UI (Typprüfung inkl.) | npm run build |
| Doku-Konsistenz | scripts/check-doc-consistency.sh | tote Links, Konflikt-Marker, Versions-Rückfall-Wächter (keine handgepflegte Versionsnummer, MED-D-363) | bash scripts/check-doc-consistency.sh |
| Doku-Site | Docusaurus-Build | rendert docs/ (Broken-Link-Check) | cd docs-site && npm run build |
| Feature-Fragmente (MED-D-362) | scripts/feature_liste_bauen.py --offline + scripts/test_feature_liste_bauen.py (unittest, 7 Tests) | feature.d/ wohlgeformt (Abschnitt, Issue-Nummer, kein Status im Fragment); Status-Ableitung aus dem Issue; Fehlerfälle brechen ab | python3 scripts/feature_liste_bauen.py --offline · python3 -m unittest discover -s scripts -p 'test_*.py' (CI-Job doc-consistency) |
| Versions-Stempel (MED-D-363) | scripts/stamp-version.test.mjs (node:test, 8 Tests) | scripts/stamp-version.mjs im Wegwerf-Git-Repo mit Fake-Tags + Label-Attrappe: Bump = höchste version:*-Stufe aller First-Parent-Merges seit dem letzten Tag bzw. seit dem Setzen des Rückfallwerts (nicht nur HEAD), höchster Tag numerisch, widersprüchliche Labels und Abfragefehler → Basisversion + ::error:: + kein Tag (Exit 0), Re-Run ohne zweiten Bump, VERSION_BUMP, --tag nur mit gültiger Stempel-Entscheidung | node --test scripts/stamp-version.test.mjs (CI-Job doc-consistency) |
| Textbaustein-Hydrate (MED-D-703) | scripts/textbausteine-hydrate.test.mjs (node:test, 4 Tests) | scripts/textbausteine-hydrate.mjs: Entwürfe lesbar mit Platzhaltern, Kopf-Pflicht, Trockenlauf ruft nichts auf, ohne Anmeldung Abbruch, 201 → angelegt / 409 → übersprungen (nie überschreiben) / sonst Fehler, Service-Token als Header | node --test scripts/textbausteine-hydrate.test.mjs (CI-Job doc-consistency) |
| Timesheet-KW-Aggregat (#365, MED-D-365) | scripts/test_timesheet_summen.py (unittest, 4 Tests) | ISO-KW-Summen aus timesheet.d/ (auch Sessions ohne Zeitangabe), Ersetzen nur zwischen den Markern, fehlende Marker brechen ab, echter Bestand baut | python3 -m unittest discover -s scripts -p 'test_*.py' (CI-Job doc-consistency) |
| Testzahlen (#701, MED-D-701) | scripts/test_testzahlen_messen.py (unittest, 8 Tests) | statisches Zählen der drei Suiten (it(/test( je Datei; Generatoren it.each und abgeschaltete Fälle werden gemeldet, nicht gezählt), Kopfzeile mit der Server-Zahl zuerst (kanonisch für den Doku-Check), Ersetzen nur zwischen den Markern, fehlende Marker brechen ab, echte Suiten werden gefunden | python3 -m unittest discover -s scripts -p 'test_*.py' (CI-Job doc-consistency) |
Der Client hat bewusst (noch) keine Unit-Tests — die Absicherung ist der strikte Produktions-Build (schlägt bei Template-/Typfehlern fehl). Kandidat für später: Component-Tests der Kern-Flows.
Server-Tests im Detail
Domäne — reine Funktionen (voll deterministisch)
| Datei | Tests | Deckt ab |
|---|---|---|
domain/progress.test.ts | 7 | Abgeleiteter Onboarding-Fortschritt + offene Pflicht-Items (G-2) |
domain/onboarding-vorlagen.test.ts | 3 | MED-D-70: Onboarding-Pflicht-Nachweise je Objektart (Einwilligung·Personalausweis·Kundenfragebogen·SEPA·Dienstleistungsauftrag + IBAN privat) + getrennte private/Entitäts-IBAN |
domain/iban.test.ts | 4 | MED-D-71: IBAN-Normalisierung + ISO-7064-Mod-97-Validierung (DACH-Längen) — erster Baustein MED-OP-VALID-1 |
adressen/openplz.test.ts | 6 | MED-D-149/MED-OP-VALID-1(b): OpenPLZ-PLZ↔Ort-Plausibilisierung (G-1, echter Adapter) — DACH-Reihenfolge DE→AT→CH, Vorschlag bei abweichendem Ort, immer weich (unbekannte PLZ/Netzwerk-/Timeout-Fehler → „nicht prüfbar", nie ein Blocker). |
adressen/fake.test.ts | 3 | MED-D-149: deterministischer PLZ↔Ort-Fake (Test/Dev, kein Netz) — plausibel/unplausibel+Vorschlag/unbekannt, erfüllt den PlzOrtProvider-Vertrag. |
domain/feld-typ.test.ts | 13 | MED-D-146 (G-8 Data Dictionary, MED-OP-VALID-1 Slice 1): kanonischer ValidierungsArt (text/email/tel/select/zahl/datum/iban/plz/betrag) — pruefeWert immer weich (leer/Freitext nie ein Blocker), normalisiereWert (IBAN/Zahl-Komma→Punkt), formatVorschlag (DE-Datum→ISO, IBAN-Normalform, PLZ/Tel-Ziffernbereinigung, null wenn kein sicherer Vorschlag ableitbar). |
api/lead.test.ts | 10 | MED-D-72/OP-LEAD-1: öffentliches Lead-Formular → Mandant als lead + Entität + Sichtungs-Wiedervorlage; Einwilligungs-/Pflichtfeld-Prüfung; Ingress-Scrubbing (G-6); öffentliche Routen trotz AUTH_ENFORCED. MED-D-106 (Slice C2a, Lead-Auto-Anlage): Kontext liefert nur buchbare Produkte (dexman ausgenommen); je gewähltem Interesse ein Auftrag (anbahnung, praxis-gebunden) + Playbook + Audit-Zählung; unbekannte/nicht-buchbare/doppelte Interesse-Werte verworfen (G-6); ohne Auftrags-Dienst keine Aufträge (Regression); Route reicht interesse durch (HTTP e2e) |
domain/lifecycle.test.ts | 8 | MED-D-151 (löst domain/phasen.test.ts ab): Lifecycle-Guard-Matrix — onboarding→aktiv blockiert bei offenem Onboarding bzw. offenen Unterschriften (einzeln + kombiniert), frei bei beidem erfüllt; lead→aktiv (Guard gilt direkt, kollabierte Zwischenstufen); lead→onboarding immer frei; →ruhend/→archiviert + Reaktivierung ruhend→aktiv immer frei (kein Re-Guard); Same-Status-No-Op; LIFECYCLE_SPINE/spineIndex; reduzierter Playbook-Katalog (nachfassen@onboarding, jahrescheck@aktiv) + Ref-Format. |
domain/audit.test.ts | 4 | SHA-256-Hash-Kette + Tamper-Erkennung (G-4) |
domain/zugriff.test.ts | 3 | D-50/RISK-27: Zugriffs-Whitelist — zugriffAktion() = <entitaet>.eingesehen, einheitliches Suffix (G-3), nur die festgelegten sensiblen Entitäten (keine Listen/Cockpit) |
domain/ablage.test.ts | 10 | NextCloud-Ablagepfad-Logik (R3/R9) + Anfangsbuchstaben-Bucket (D-31) + Upload-Ordner uploadOrdner/dokumentIdAusUploadPfad (Webhook, MED-D-55) + konfigurierbare Sandbox-Wurzel setzeBasisOrdner/basisOrdner (MED-D-56). |
domain/beratung-themen.test.ts | 4 | Vollständigkeitsprüfung der Beratungsdoku (R6) |
domain/signatur-niveau.test.ts | 4 | eIDAS-Niveau-Matrix je Dokumenttyp (R4, RISK-2) |
domain/mandant-name.test.ts | 4 | D-31: Anzeigename-Ableitung "Nachname Titel Vorname" aus R1-F08..F10; Fallback auf Einrichtungs-/Freitextname; Trim/leere Teile. |
domain/kunden-vollstaendigkeit.test.ts | 10 | D-41: Strukturierte Vollständigkeit (True North #3) — reif nur ohne blockierende Punkte, Score-Heuristik (100 − 25/10/4, Klemmung 0), Stufe/Schwere je Kategorie, offene Wiedervorlage = hinweis (blockiert nicht), jeStufe-Statusableitung + Blocker-Zählung, alle 3 Lifecycle-Spine-Stufen abgedeckt. MED-D-80: jeStufe.hinweis zählt offene Wiedervorlagen je Stufe (WV-Badge, MED-OP-REG-1). MED-D-151: phase→stufe/jePhase→jeStufe umgestellt (löst PROZESS_PHASE ab); neuer Test "ruhend/archiviert (off-spine) fallen auf die letzte Spine-Position zurück". |
domain/naechste-schritte.test.ts | 6 | D-46/OP-NEXTSTEP-1: abgeleitete "nächste Schritte" (G-2) — leer wenn vollständig, "Onboarding starten" vs. "nachfassen", ein Schritt je Kategorie (Dokument/Beratung/Unterschrift/Wiedervorlage), Sortierung blockierend vor hinweis, Wiedervorlage = reiner Hinweis. |
domain/deploy-status.test.ts | 5 | D-47 (Variante B): defensiver Parser des deploy_status-Werts — null bei fehlend/ungültig/Typfehler, gültiger Erfolgs-Status, runUrl optional, unbekannter Status → failure (fail-safe), failure/cancelled erkannt. |
pdf/pdf-dokument.test.ts | 3 | OP-DOCGEN-1: Blockmodell → PDF (pdf-lib) — Magic Bytes/Metadaten/deterministisches Datum, Mehrseiten-Umbruch, WinAnsi-Sanitizer |
domain/dexman-kern.test.ts | 42 | Dexman-Rechenkern (shared/dexman-kern/, MED-D-381, läuft identisch in Worker und Browser): Monatsrechnung (Umsatz, Personal inkl. AG-Aufschlag/Beteiligung, Material, Fixkosten, Darlehen, AfA, Ereignisse, Kontokorrent), Tageslinie, Ist vor Plan, Szenario ohne Seiteneffekt, Sätze, Hebel, Prüfregeln; Person + Anstellungs-Zeitspannen (MED-D-693): Spanne je Zeitraum, Alt-Bestand lesend überführt (identische Rechnung), abgeleiteter Rahmen, Lücke/Überlappung; Regel-Katalog DEX-6 (MED-D-683): Katalog eindeutig, geplante Regeln nennen ihre Datenquelle, jeder Hebel hat eine Katalog-Zeile mit passender Wirkungsart (gerechnet vs. Schätzwert); Plausibilitäts-Hinweise (MED-D-699): Annuitätenrate unter den Zinsen (fallende Tilgung ausgenommen), Ist vor Eintritt/nach Austritt, Ist ohne Planumsatz, Beispielpraxis plausibel |
domain/dexman-monatsbericht.test.ts | 7 | Dexman-Monatsbericht (DEX-5, MED-D-684): ein Blockmodell → PDF + Markdown; Kernaussage zuerst (Minto), Kennzahlen mit Richtwert, Liquiditätsvorschau über 12 Monate, Empfehlungen mit Schätzwert-Kennzeichnung, Leerfälle (keine Empfehlungen, Monat außerhalb des Plans), Determinismus (keine Uhr/kein Zufall), Hinweis auf eingefrorene Fassung + Abgrenzung zur Steuerberatung |
domain/dentmarking-kennzahlen.test.ts | 9 | Dentmarking T3: 18 Kennzahlen, Formel-Varianten alt/korrigiert (B-1..B-3/OP-DM-1), Benchmarks je Praxistyp, Potential/Mehrgewinn (inkl. Beispielrechnung Excel-Logik §11), Nenner-Guards |
API / HTTP — Hono-App gegen In-Memory-Repo (MemoryRepo)
| Datei | Tests | Deckt ab |
|---|---|---|
api/app.test.ts | 46 | Kern-API: Health · Mandant (UC-1, natürliche Person ohne Typ D-45; optionale E-Mail R1-F12 + Format-Validierung) · Cockpit (UC-6, inkl. auftragStatusZusammenfassung fürs Board, MED-D-151) · Betreuungsmodell (R1-F11: Default/Cockpit/PATCH+Audit/Validierung) · aggregiertes Cockpit (/offene-punkte) · Vollständigkeit (Detail + Leitstand-Aggregat /api/vollstaendigkeit, OP-AKTE-1) · Lifecycle-Guard (PATCH /api/mandanten/:id lifecycleStatus, MED-D-151, löst /phase ab: 409 bei offenem Onboarding/Unterschriften, frei bei ruhend/archiviert, Playbook-Wiedervorlage bei Eintritt) · Playbooks (admin-editierbar, auf 2 Lifecycle-Stufen reduziert) |
api/beratungsdoku.test.ts | 15 | R6 Beratungsdoku inkl. Pflichtangaben (F11–F20) + Verzicht |
api/wiedervorlage.test.ts | 23 | R5 Wiedervorlagen + Delegation/Zuweisung (OP-WV-1) + Sammelaktionen (P3b, teilfehler-tolerant) + D-42: nachträglich bearbeiten (Betreff/Fälligkeit, Erinnerung reaktiviert) + append-only Kommentare (Autor/Zeitstempel, neueste zuerst, PII-armes Audit, Anzeige an der WV im Detail); MED-OP-WV-2: createWiedervorlage idempotent bei gleichem quelleRef (Race-Simulation ohne externen Check, spiegelt den D1-Unique-Index) |
api/bankkonto.test.ts | 12 | MED-D-195 (Kunden-Cockpit S3a, R1-F33): 1:n-Bankkonto je Mandant (HTTP) — CRUD + mandantScopeGuard, Defaults (pruefstatus=neu/sepaGueltig=false/quelle=manuell), IBAN-Normalisierung, PATCH Prüfstatus+SEPA → archivieren statt löschen (append-only, G-4), Validierung (400) + unbekannter Mandant/Konto (404). |
api/praxis.test.ts | 15 | R1 Praxis/Gesellschaft (D-27/D-45): 0..n je Mandant, Objektart (praxis/mvz/gesellschaft) + Default/PATCH/Validierung, Geschäftsjahr-Beginn, Audit, HTTP-Validierung; MED-D-87: strukturierte Anschrift strasse/plz/ort (Wertobjekt, G-8) — anlegen getrimmt + PATCH (leer → null) |
api/dexman.test.ts | 13 | Dexman (MED-D-381): Planungsbausteine append-only versioniert, Archivieren, Ist-Umsätze in praxis_jahreswert (Quelle berater, Vorrang System > berater), Lage aus dem Rechenkern, Hebel → Wiedervorlage (idempotent), PII-armes Audit + Lesezugriff dexman_planung.eingesehen, HTTP mit Rechten je Kunde (fremder Berater → 404); Monatsbericht (MED-D-684): PDF in der Ablage + Dokument erhalten + Audit mit Inhalts-Hash, je Aufruf eine eigene Fassung, ohne Ablage dormant (503); Festschreibung der Ist-Monate (MED-D-694): Wasserzeichen sperrt für jede Quelle (409), nur abgeschlossene Monate und nur vorwärts, append-only Log, Aufheben nur Rolle admin + Audit |
api/dexman-monatsmeldung.test.ts | 4 | Dexman-Monatsmeldung im Kundenportal (MED-D-700, DEX-1a): Portal-Aufgabe (nur laufendes Dexman-Mandat, Vormonat, fehlende Ist-Werte), Formular (Behandler, Vormonat-Vorbelegung, Scope eigener Kunde), Einreichen/Sichten (annehmen → praxis_jahreswert Quelle portal, ablehnen mit Grund → Korrektur-Schleife + Feedback-Mail, MED-D-89), Audit ohne Beträge/Namen, Portal-Routen (dormant 404, ohne Session 401). |
api/dentmarking.test.ts | 31 | Dentmarking T2–T7: Fragenkatalog v1 (111 Felder, Alt-Spalten-Mapping, Zahl-Parser), Erhebung-Split zahl/text, Import-Idempotenz (B-5), GJ-Upsert, PII-armes Audit, Kennzahlen-Endpoint, Gutachten-Beleg (Fassung + R3-Referenz, keine-Daten-Guard/409), Befunde → idempotente Wiedervorlagen, öffentlicher Fragebogen T5 (Einladungs-Token, Einwilligungs-Pflicht, Einmal-Einreichung, Ablauf), T6/DM-9: Zwischenspeichern/Rehydrieren des Entwurfs + Score + Feld-Metadaten im Kontext (Service + HTTP-PUT), Markierungen an der Erhebung (kein Jahreswert), T7/DM-9: TTL aus Tenant-Einstellung + Cap 30, Ablauf-Hook (markiert/benachrichtigt idempotent, Entwurf bleibt), Statistik/Completion-Trichter, Einstellungen-API (GET/PUT admin, geklemmt) |
domain/einstellungen.test.ts | 6 | Tenant-Einstellungen — Fragebogen-TTL Default 30/Cap 30, Normalisierung/Klemmung, effektive Werte; Zeiterfassung (D-49): Schwelle Default 1/Klemmung 0..240, Rundung Default 0/Klemmung 0..60, buchungsminuten (Aufrunden auf Vielfache · kaufmännisch bei 0 · 0/negativ→0) |
domain/dentmarking-score.test.ts | 8 | Dentmarking T6/DM-9: Katalog-Metadaten (Wichtigkeits-Klassen, Markierbarkeit, Wertebereich Behandler 1–500, Struktur-Typ Öffnungszeiten), Ausfüll-Score (leer=0 / voll=100, Gewichtung essenziell>nice, Markierungen zählen als "offen markiert" nicht als Wert, leere Werte) |
ablage/r2.test.ts | 10 | Geschlossene Ablage V2 "R2 + App-UI" (MED-D-54/MED-D-58): R2Ablage erfüllt den NextCloudClient-Vertrag (ensureOrdner/exists/put/get, FakeR2Bucket ohne Netz), put() sichert Vorfassungen unter _versionen/… (verlustfrei), Upload-Link zeigt auf die App-Route, Token-Auflösung (unbekannt/abgelaufen → null, Pfad-Tricks abgewiesen), Upload pfad-sicher benannt, Invariante "nie löschen" (keine delete-Methode, MED-D-56); HTTP end-to-end: provisionieren → Upload-Link → Formular-GET → multipart-POST → erhalten → Datei-Abruf; hochladen/abgleich unverändert über den R2-Vertrag |
api/ausfuellformular.test.ts | 40 | MED-D-292 (Abschnitts-Freigabe N2): alleAkzeptieren gibt in 1 Klick alle offenen Felder frei (Zähler + Audit PII-arm) und lässt sich sauber abschließen; zweiter Aufruf → nichts_offen (422); eine vorherige Feld-Ablehnung bleibt unangetastet und öffnet beim Abschließen die Korrektur-Schleife; nur für eingereichte Formulare. MED-OP-VALID-1/MED-D-146: pruefeAlleWerte ist immer weich (Freitext/leer nie ein Hinweis, nur echte Format-Ausreißer); kontext() liefert hinweise/vorschlaege je Feld (gültiges-aber-nicht-kanonisches DE-Datum bekommt nur einen Vorschlag, ungültige IBAN nur einen Hinweis ohne Vorschlag, Freitext bleibt unangetastet, der Rohwert wird nie automatisch überschrieben). Ausfüll-Formulare (MED-D-63): Domäne (Festwerte aus Stammsatz, Pflichtfeld-Prüfung, Ingress-Scrubbing G-6, deterministische Beleg-Nr.) + Service (erstellen/kontext/speichern/einreichen, Beleg-PDF-Ablage, Idempotenz zweite Einreichung, ohne Ablage rein datenseitig) + HTTP-Fluss (Vorlagen-Katalog, erstellen → PUT ergänzen → einreichen, Pflichtfeld-400, unbekanntes Token-404); MED-D-67: Wichtigkeit 3-stufig, Markierungen "weiß nicht"/"nachgeliefert" (erfüllen Pflichtfeld), Nachlieferungs-Wiedervorlage idempotent + Audit; MED-D-84: Stammdaten-Rückübernahme (Vorschlag nur für eingereichte, Diff-Bildung, uebernehmen schreibt nur ausgewählte+gültige Felder + Audit PII-arm, idempotent, mandant-scoped, ungültige IBAN nicht übernommen; HTTP Vorschlag→übernehmen→idempotent; MED-D-86: Anschrift strukturiert strasse/plz/ort → verlustfreie 1:1-Übernahme); MED-D-89: Feld-Sichtung + Korrektur-Schleife (feldSichten nur eingereicht + nur gelieferte Felder, Ablehnung ohne/mit Grund + Audit PII-arm (kein Grund), sichtungAbschliessen verlangt Vollständigkeit, alles akzeptiert → bleibt eingereicht + Bestätigungs-Feedback + WV erledigt, Ablehnung → Korrektur-Schleife (nur abgelehntes Feld offen, akzeptiertes gesperrt, Re-Einreichung setzt Entscheidung zurück + reaktiviert WV), mandant-scoped) |
domain/stammdaten-version.test.ts | 15 | MED-D-179 (Stammdaten-Versionierung, G-4): snapshotVon zieht nur die versionierten Felder (kein betreuungsmodell/lifecycleStatus/verantwortlich); snapshotGleich (null≡'' , reine lifecycle-Änderung = gleich); baueNaechsteVersion (v1=GENESIS, Folgeversion kettet + zählt hoch); pruefeVersionsKette (intakt ok, manipulierter Feldwert bricht bei Position); feldHistorie (fasst unveränderte Folgeversionen zusammen, setzt gültig-bis). MED-D-305 (feldset-tolerante Hash-Kette): neue Versionen tragen AKTUELLES_HASH_SCHEMA=2; Schema-1-Kanonisierung byte-stabil (Regressions-Lock); Schema-2 über die eigenen Snapshot-Keys sortiert; Bestands-Schema-1-Kette verifiziert unverändert + Manipulation erkannt; gemischte Schema-1+Schema-2-Kette (mit den 6 neuen Feldern) intakt; künftiges Feld-Wachstum bricht keine Schema-2-Zeile, ein nachträglich eingemischtes Feld schon. MED-D-306 (kollisionssicheres Schema 3): Schema 2 kollidiert bei =/` |
domain/stammdaten-uebernahme.test.ts | 12 | MED-D-84 (Formular → Stammdaten): reine Diff-/Validierungslogik — Ausfüll-Formular-Felder → Stammdaten-Ziele, IBAN-Mod-97/Datum-Format-Gültigkeit (ungültig markiert aber vorgeschlagen), Idempotenz (kein Vorschlag bei Gleichstand), Normalisierung, leere/unbekannte Werte übersprungen, bauePatch überträgt nur Zielfelder. pruefeWert/normalisiereWert/ValidierungsArt sind seit MED-D-146 Re-Exporte aus domain/feld-typ.ts (G-8) — Importpfad hier unverändert. |
domain/feld.test.ts | 6 | MED-D-294/295 (kanonisches Feld-Modell, G1): reine Helfer pflichtFelder/markierbareFelder/felderJeAbschnitt (gruppiert in Reihenfolge, leere Abschnitte entfallen) + Konsistenz-Invariante schemaVerstoesse (unbekannter Abschnitt, doppelter Schlüssel, auswahl ohne/Nicht-auswahl mit Optionen). MED-D-296 (G2): fehlendePflicht (Wert ODER Markierung erfüllt Pflicht, DM-9). |
domain/formular-schema.test.ts | 4 | MED-D-294/295 (Alt-Katalog→Schema-Adapter, G1): onboardingSchema (pflicht:bool→wichtigkeit, select→auswahl mit Optionen, nichts markierbar), ausfuellSchema/ausfuellSchemaVon (KundenFeld 1:1, alle markierbar, unbekannte Vorlage→null), fragebogenSchema (klasse→wichtigkeit, block→Abschnitt, Öffnungszeiten-strukturTyp+markierbar); jede Überführung feldzahl-treu + konsistent (schemaVerstoesse=[]). |
domain/feld-sichtung.test.ts | 9 | MED-D-89/MED-OP-FORM-4 (Feld-Sichtung, reine Domäne): zuSichtendeFelder (nicht-leer, unmarkiert, sortiert), offene/vollständig gesichtet (leere Menge ≠ vollständig), setzeEntscheid (Ablehnung verlangt Grund, Akzeptanz verwirft Grund), abgelehnte/akzeptierte Felder, entscheidungenZuruecksetzen (Korrektur-Schleife), feedbackNachricht (Ablehnung listet Grund + Korrektur-Link / alles akzeptiert = Bestätigung ohne Link). MED-D-292 (Abschnitts-Freigabe N2): akzeptiereOffene akzeptiert nur die offenen Felder, lässt eine Ablehnung mit Grund unangetastet, ist idempotent und macht die Menge danach vollständig gesichtet. |
api/formular-vorgaenge.test.ts | 15 | Fragebogen-/Formular-Vorgänge (MED-D-67): Domäne (Katalog maxParallelOffen=1 je Typ, verallgemeinerter Ausfüllgrad Pflicht 2/optional 1 inkl. Whitespace/fremde Schlüssel/leere Felder, SLA-Zustände ok→erinnerung_faellig→ueberfaellig, quelleRefs) + Parallel-Limit idempotent (Onboarding je Mandant · Ausfüll-Formular je Mandant×Vorlage; nach Abschluss/Ablauf wieder frei) + Öffnungs-Tracking geoeffnetAm genau einmal (alle drei Strecken, Audit) + Sichtungs-Wiedervorlage (idempotent, Verantwortliche:r, Quelle formular) + SLA-Cron (weiche Erinnerung einmalig + PII-arm empfaenger:null, harte Eskalation idempotente Wiedervorlage, abgelaufener Link ohne weiche Erinnerung, abgeschlossene Vorgänge neutral) |
api/dokument.test.ts | 13 | R3 Dokumente (Provisionieren/Abgleich/Status) + Upload-Link (OCS File Drop) + Webhook (MED-D-55): uploadLinkErzeugen idempotent, webhookDateiErstellt (Pfad→Dokument, angefordert→erhalten, idempotent, fremde ignoriert), HTTP Upload-Link + Webhook (401 ohne Secret, 200 + Statuswechsel mit Secret) + Invariante "nie löschen" (Vertrag ohne delete/remove/rm, MED-D-56) |
api/dokumentgenerierung.test.ts | 9 | R12 Dokumenten-Automatisierung |
api/individualdokument.test.ts | 11 | MED-D-81: Freitext-Individualdokument — Block-/Benennungs-Engine, Service (erstellen→freigeben→erzeugen→R3-PDF; Bearbeiten nach Freigabe gesperrt; explizites eIDAS-Niveau schlägt Titel-Ableitung; zurUnterschrift lehnt bei erzeugt steckengebliebene Dokumente ab), HTTP-Vollfluss + 503/400. |
api/beratungsauftrag.test.ts | 30 | MED-D-95/99 (Slice A): Katalog (Enum↔Eintrag, laufend nur Dauer-Produkte), Service (anlegen/Status mandant-scoped + produkt-validiert; person-oder-praxis; nicht buchbar/fremde Praxis abgelehnt; Audit-Events G-4). MED-D-101 (Slice B): Reife-Regeln (Beratungsdoku ab Beratung, Unterschrift, Gutachten-Produkt); Artefakt binden/lösen aktualisiert Reife; fremdes Artefakt abgelehnt; idempotente Retro-Ableitung (je Praxis EIN Auftrag abgeschlossen, Gutachten gebunden, zweiter Lauf 0/0); Bindungs-Besitz-Guard (Lösen/Umhängen fremder Auftrag → artefakt_anders_gebunden, CodeRabbit #135). MED-D-104 (Slice C1): Produkt-Playbook — jedes buchbare Produkt hat Standard-Aufgaben (dexman leer); anlegen erzeugt die Playbook-Wiedervorlagen (quelle=playbook, an den Auftrag gebunden). MED-D-105 (Fehler-Isolation, CodeRabbit #138): ein Repo-Fehler beim Playbook lässt die bereits persistierte Auftrags-Anlage nicht scheitern (kein Retry-Doppel) und wird PII-arm als auftrag.playbook_fehler auditiert. MED-D-151 (+2, Auftrags-Status-Playbook): →beratung/→unterschrift erzeugen je genau eine idempotente Wiedervorlage (an den Auftrag gebunden, wiederholter Aufruf kein Duplikat); ein Repo-Fehler beim Status-Playbook rollt den bereits persistierten Statuswechsel nicht zurück.; MED-D-630: Mandats-Unterlagen aus Produkt-Vorlagen |
api/meeting.test.ts | 8 | MED-D-82 (R11): Gesprächsnotiz/Meeting-Doku — Domain (Bezeichnung, aufgabeQuelleRef normalisiert-stabil), Service (erstellen→Vorschlag→Übernahme legt Wiedervorlagen quelle='aufgabe' an, delegiert; idempotent; ohne Frist sofort fällig; bearbeiten lehnt leeren Titel ab), HTTP-Vollfluss (400 bei ungültiger Art; Aufgabe im WV-Register sichtbar = eine Wahrheit). |
ki/gespraechs-auswerter.test.ts | 4 | MED-D-82: Regel-Auswerter (Fake-Seam) — parst "Aufgabe:"/Checkbox-Zeilen, extrahiert @verantwortlich + ISO-/DE-Fälligkeit, ignoriert Prosa ohne Marker, liefert Haftungshinweis (RISK-14). |
ki/regel-vorschlag.test.ts | 3 | MED-D-146 (MED-OP-VALID-1 Slice 1): RegelVorschlag (Fake-Seam analog RegelPruefer/RegelAuswerter) — schlägt bei erkennbarem Format-Fehler einen korrigierten Wert vor + trägt den Haftungshinweis (RISK-14), liefert vorschlag: null wenn kein sicherer Vorschlag ableitbar ist (z. B. E-Mail-Tippfehler), name identifiziert den Fake-Adapter. |
api/leistungszeit.test.ts | 11 | R13 Leistungszeit + Übersicht (OP-TIME-1) + Mandat-Zuordnung (MED-D-157/158) |
api/timer.test.ts | 46 | MED-D-172 (Aktiver Timer, OP-TIME-3): Service-Ebene — Start (frisch/idempotent bei gleichem Mandanten/Konflikt bei anderem Mandanten, keine stille Überschreibung), Pause/Fortsetzen (Segment-Übernahme in basisMs, idempotent), Korrigieren (überschreibt + pausiert), Buchen (mit weiterlaufen true → frischer Timer läuft weiter; false → Timer beendet, Verlassen-Flow), Verwerfen, Fehlercodes (kein_timer/anderer_mandant); zwei Benutzer haben unabhängige Timer. HTTP-Ebene: alle /api/timer*-Routen inkl. Validierung (400) + 409-Konfliktfälle. MED-D-174 (CodeRabbit-Nachlese, RBAC-Sicherheitsfund): eigene RBAC-Describe-Gruppe — /api/timer/start/buchen für einen fremden (nicht sichtbaren) Mandanten → 404 kein Existenz-Leak, für den eigenen → 201-Regression. |
api/signatur.test.ts | 23 | R4 Signatur-Vorgänge (inkl. AD-006 Empfänger-Override + MED-D-244 Dokument-Scope-Guard) |
api/audit.test.ts | 10 | R8 Audit-API + Integritätsprüfung (/verify); D-50 Zugriffs-Protokollierung: Kunden-Detail öffnen → mandant.eingesehen (PII-arm details={}, Kette intakt), Beratungsdoku-Lesen → beratungsdoku.eingesehen (Gesundheitsbogen R6-F18), Datei-Abruf → dokument.eingesehen, Listen/Cockpit erzeugen kein .eingesehen (G-6) |
api/onboarding-formular.test.ts | 19 | R2 Self-Service-Onboarding (öffentlicher Formular-Flow) + MED-D-68: Zwischenspeichern/Rehydrieren des Entwurfs + Fertigstellungsgrad (Service + HTTP-PUT, Ingress-Scrubbing) + MED-D-307 (N5): Einwilligung via DocuSign flag-gated — echte SES angefordert/pending/PDF-Beleg (Fake-Provider) · Flag ohne Dienst → SES-Attrappe · Provider-Fehler → consent_provider_fehler |
Auth
| Datei | Tests | Deckt ab |
|---|---|---|
auth/auth.test.ts | 9 | Identität (Cloudflare Access), RBAC, Bootstrap-Admin, AUTH_ENFORCED (R7/OP-AUTH-1) |
domain/rechte.test.ts | 11 | Rechte-Matrix darf(rolle, faehigkeit) je Rolle + Mandant-Scope darfMandantSehen (null/eigen/fremd, whitespace-normalisiert), D-48; fragebogen_statistik_sehen admin-exklusiv (OP-DM-9/MED-D-63); Teams (MED-D-629) |
domain/unterlagen-vorlagen.test.ts | 2 | Standard-Mandats-Unterlagen (MED-D-630, Migration v66): nur buchbare Produkte, stabile eindeutige IDs, idempotentes Seed-SQL |
api/stammdaten-versionierung.test.ts | 7 | MED-D-179 (Stammdaten-Versionierung, Slice 1): createMandant seedet v1; updateMandant mit geändertem Feld erzeugt v2 (Kette intakt, Herkunft berater/actor übernommen); reine lifecycleStatus/verantwortlich-Änderung erzeugt keine neue Version (kein Rausch-Bump); self_service-Herkunft; backfillStammdatenV1 idempotent (seedet nur fehlende); HTTP GET /api/mandanten/:id/stammdaten-versionen liefert die Kette aufsteigend; Berater-PATCH erzeugt eine berater-Version mit korrektem Actor/Snapshot. |
api/rbac.test.ts | 17 | RBAC je Mandant HTTP: Flag aus/an, Scope der Aggregate (/api/mandanten, /offene-punkte, /vollstaendigkeit), Detail-Guard (Fremd→404), Zuweisung (200/400/403 + Audit), /api/benutzer/zuweisbar-Guard, Rückwärtskompatibilität, D-48; /api/fragebogen/statistik admin-gated 403/200 (OP-DM-9/MED-D-63); MED-OP-AUTH-2: By-ID-Mandant-Scope-Guard für Meetings/Beratungsdoku/Individualdokumente (fremd→404, eigen→200/201, je 1 Test — vor der Implementierung gegengeprüft: schlägt ohne den Fix fehl); Praxis-/Gutachten-/Befund-Routen im Kunden-Scope (MED-D-382); übrige By-ID-Routen im Kunden-Scope (MED-D-623); Teams (MED-D-629) |
Worker-Auslieferung (Routing-Weiche des Workers)
| Datei | Tests | Deckt ab |
|---|---|---|
serving.test.ts | 12 | Regressionsschutz zum Public-Link-Vorfall (OP-ONBOARD-1): zielIstAssets leitet /oeffentlich* (Onboarding-/Fragebogen-Daten) + /api* an die App statt an die SPA-Assets (inkl. Pfad-Grenze gegen Präfix-Kollisionen wie /apiary); öffentliche Seiten (/fragebogen/:token, /onboarding/:token) + statische Dateien an die Assets. istSpaNavigation = SPA-Fallback nur für Dokument-Navigationen (kein Datei-Suffix). spaFallback = Fallback liefert index.html als 200, nie als Redirect (Cloudflares Asset-Layer beantwortet explizites /index.html per html_handling-Default mit 307 → / → Access-Loginwand); Fakes decken beide html_handling-Modi ab. echterFaviconPfad (MED-D-78): mappt /oeffentlich/favicon.svg/.png auf die echten Asset-Pfade — Favicon läuft über den bestehenden Access-Bypass, alle anderen Pfade → null. (Die Cloudflare-Access-Konfiguration selbst ist Infrastruktur und nicht per Test abdeckbar.) |
CI-Pipeline (.github/workflows/ci.yml)
Ein changes-Vorabjob (dorny/paths-filter) erkennt die geänderten Bereiche; die schweren Jobs laufen
nur bei betroffenem Bereich (Pfad-Filter, agents.md §6.6) und werden vom aggregierten checks
zusammengefasst (Merge nur bei grünem Gate; übersprungene Bereiche zählen als bestanden):
- Server (typecheck + test) —
tsc+ Vitest. (nur wennserver/**geändert; im Merge-Queue-Lauf immer) - Client (build + e2e + e2e full-stack) — ein Job (
client, MED-D-336): 1× Deps + Chromium + Angular-Prod-Build, dann gegen denselbendist/browser(a) die gemockte Playwright-Suite (client/e2e/, MED-D-289; nur wennclient/**geändert) und (b) die Full-Stack-Suite gegen den echten Worker (wrangler dev · lokale D1 + R2 · Fake-Signatur, inkl. DocuSign-Flow; MED-D-290/MED-OP-E2E-1). (Job beiclient/**oderserver/**geändert; im Merge-Queue-Lauf immer.) Vorher drei Jobs mit dreifachem Build. - Doku-Konsistenz — mechanischer Check (warnt, blockt nicht — Job selbst ist grün; läuft immer).
- Doku-Site (build) — Docusaurus inkl. Broken-Link-Check. (nur bei
docs/**/docs-site/**/HANDOFF.md/README.md— was die Site rendert, MED-D-336; im Merge-Queue-Lauf immer)
So überspringt ein Doku-only-PR Server/Client, ein Code-only-PR die Doku-Site — schnellere PR-Läufe.
Installs laufen mit npm ci --prefer-offline --no-audit --fund=false, der Checkout ist auf PRs shallow
(fetch-depth: 1). Trigger (MED-D-327): pull_request + merge_group (verbindliche Merge-Queue) +
manuell workflow_dispatch — kein push: main mehr (der Queue-Lauf testet exakt den Stand, der auf main
landet; der frühere zweite Voll-Lauf nach dem Merge entfällt, BM-9). SBOM entsteht im App-Deploy. checks prüft, dass keiner der erforderlichen Jobs
fehlgeschlagen ist (skipped ≠ failure). CodeRabbit-Reviews laufen separat
und sind nicht-blockierend. Deploys (app-deploy.yml / docs-deploy.yml) laufen getrennt bei Push auf main.
Runner: alle CI-Jobs pinnen auf das Label hot (persistenter self-hosted Runner — warmes ~/.npm,
.angular/cache, clean: false greifen nur dort; ephemere Runner tragen ephemeral), D-38; Fork-PRs auf
ubuntu-latest (Gate). Umschalten auf
GitHub-Runner ohne Commit über die Repo-Variable CI_RUNNER (Details: Deploy.md §7b, RISK-24).
Auto-Rerun (auto-rerun.yml, D-40): ein separater workflow_run-Workflow stößt bei einem
fehlgeschlagenen CI-Lauf die fehlgeschlagenen Jobs einmalig neu an (run_attempt < 2) — heilt
flakige hot-Runner-Läufe automatisch; abgebrochene Läufe und Fork-PRs sind ausgenommen (RISK-24),
Deploys ebenfalls. Kein eigener Testlauf, sondern Selbstheilung der Pipeline.
Konventionen
- Neue Tests beiden Wegen hinzufügen: lokal (
npm test/npm run build) und damit automatisch in CI. - Domänenlogik als reine Funktionen testen; Dienste/Routen über die Hono-App gegen
MemoryRepo(kein D1 im Test). - Diese Datei ist erzeugt (MED-D-701): Verlauf als Fragment in
test.d/anlegen, Tabellen intest.d/_fuss.mdfortschreiben — die Gesamtzahlen werden gemessen, nicht gepflegt (siehe Kopf).