Files
schalli 234f80b4fb
Tessera CI/CD / Lint & Type Check (push) Successful in 46s
Tessera CI/CD / Tests (push) Successful in 52s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s
docs(16): close Phase 16 UAT with A1 accepted as an open assumption
The user has no write access to the company AD, so tests 1, 3, 4 and 8 cannot
be run there, and building a throwaway domain controller for them was judged
disproportionate now that the one substantive defect at this spot is found and
fixed (UAT test 2 -> quick task 260811-f9i).

Recorded rather than hidden: A1 (objectGUID survives a rename) now rests on
Microsoft's documentation, not on our own measurement. The Tessera-side rename
and delete logic stays covered by unit tests against fixtures. If a rename ever
fails to propagate in production, 16-VERIFICATION.md names that as the starting
point.

Phase 16 marked complete in STATE.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 13:21:21 +02:00

24 KiB

phase, verified, status, accepted_at, accepted_by, score, behavior_unverified, overrides_applied, human_verification, behavior_unverified_items
phase verified status accepted_at accepted_by score behavior_unverified overrides_applied human_verification behavior_unverified_items
16-ad-gruppen-synchronisation 2026-08-06T15:07:32Z accepted_with_open_assumptions 2026-08-11 user 5/5 must-haves (code-level) — 2 davon PRESENT_BEHAVIOR_UNVERIFIED wegen ungeprüfter Live-AD-Annahmen; A2 am 2026-08-11 geprüft, widerlegt und der gefundene Fehler behoben (260811-f9i) 2 0
test expected why_human
Read-only gegen ein echtes Active Directory (ViCoTest, balios.ctl.local): eine importierte Gruppe suchen, ihren objectGUID notieren, die Gruppe im AD umbenennen, erneut suchen, GUID vergleichen (Annahme A1). objectGUID bleibt über die Umbenennung hinweg identisch — nur dann kann der Sync eine Umbenennung von einem Verschwinden unterscheiden (SC-3 Grundlage). Kein Active Directory in dieser Umgebung erreichbar; die Unterscheidung Rename-vs-Delete ist ausschließlich gegen gemockte LDAP-Antworten getestet, nie gegen ein echtes Verzeichnis.
test expected why_human
Read-only gegen dasselbe AD: eine Suche mit dem byteweise escapten (objectGUID=...)-Binärfilter absetzen und prüfen, ob sie exakt die erwartete Gruppe zurückliefert (Annahme A2). Der binäre Existenz-Sweep liefert bei einer weiterhin existierenden Gruppe einen Treffer und bei einer tatsächlich gelöschten Gruppe keinen — sonst würde SC-4 entweder fälschlich löschen oder fälschlich nicht löschen. RFC-4515-Byte-Escaping für Binärwerte ist nur gegen ldapts-Mocks verifiziert, nicht gegen die tatsächliche Filter-Interpretation eines realen Domain Controllers. Ein negatives Ergebnis bei A1 oder A2 ist laut RESEARCH.md/16-03-PLAN.md ein Stopp-Grund für die D-05-Löschsemantik.
test expected why_human
Browser-Durchklick: /admin/ldap → Sektion 'AD-Gruppen importieren' — Discovery, Checkbox-Auswahl, Import-Button mit Auswahlzahl, alreadyImported-Badge, sichtbare Fehlerzustände. Deckt sich mit den in 16-01-PLAN.md must_haves als `verification: backstop` markierten UI-Zuständen (leer, ladend, Fehler, Overflow, lange Namen) — keiner davon ist automatisiert testbar. Kein Browser-Tool in den Ausführungssitzungen verfügbar; WINDOWS.md Eintrag #5 (16-04) und #6 (16-05) sind offen.
test expected why_human
Browser-Durchklick: /admin/groups → GroupFormModal in allen drei Zuständen (Anlegen mit Hinweis-Link, lokale Gruppe umbenennen, importierte Gruppe mit gesperrtem Namensfeld + internem Namen speichern inkl. 409-Fehleranzeige). Gesperrtes Feld ist visuell erkennbar, AD-DN-Zeile sichtbar, interner Name speichert korrekt, Fehlerzustand bleibt im Dialog sichtbar mit erhaltenen Eingaben. WINDOWS.md #5 — kein Browser-Tool verfügbar; nur Codepfad-Review, keine gerenderte Prüfung.
test expected why_human
Browser-Durchklick: /admin/ldap → Sync auslösen, alle drei/vier Berichtszeilen prüfen (inkl. Amber-Zeile bei verschobener Standardmarkierung); /admin/modules/grants → Spaltensuche unter internem und AD-Namen. Bericht zeigt Benutzer-, Mitgliedschafts- und Gruppenzahlen auch bei 0; Amber-Zeile erscheint nur bei defaultMarkerMoved > 0; Matrix-Suche findet die Gruppe unter beiden Namen. WINDOWS.md #6 — kein Browser-Tool verfügbar.
test expected why_human
Alle mit `verification: backstop` markierten must_haves aus 16-01/16-02/16-04/16-05-PLAN.md (u. a. gleichzeitige/parallele Requests, Reihenfolgeunabhängigkeit, Sortier-Divergenz zwischen name und internalName, lange Texte in Listen/Modals). Kein beobachtbares Fehlverhalten unter Nebenläufigkeit oder Layoutstress. Explizit als `verification: backstop` deklariert — kein Code-Beleg reicht zur Bestätigung; Race-Conditions und CSS-Overflow-Verhalten sind nicht durch die vorhandenen Unit-/Komponententests abgedeckt.
truth test expected why_human
SC-3: Wird eine übernommene Gruppe im AD umbenannt, zieht der Name der Tessera-Gruppe beim nächsten Sync nach, weil objectGUID über die Umbenennung hinweg stabil bleibt (Annahme A1). Gruppe im echten AD umbenennen, Sync laufen lassen, prüfen dass Group.name/ldapDn nachziehen und KEINE Lösch+Neuanlage stattfindet. Ein Rename-Zweig greift (groupsRenamed++), kein Delete+Create-Paar. Die Rename-vs-Delete-Unterscheidung hängt vollständig an A1, die nur gegen Mocks getestet ist (siehe ldap.service.spec.ts), nie gegen ein echtes Verzeichnis.
truth test expected why_human
SC-4: Verschwindet eine übernommene Gruppe aus dem AD, wird die Tessera-Gruppe samt Mitgliedschaften und Modulfreigaben entfernt — vorausgesetzt der binäre Existenz-Sweep (Annahme A2) funktioniert gegen ein reales AD korrekt. Gruppe im echten AD löschen, Sync laufen lassen, prüfen dass die Tessera-Gruppe samt GroupMembership/ModuleGrant kaskadierend entfernt wird — UND dass eine lediglich verschobene (nicht gelöschte) Gruppe NICHT gelöscht wird (WR-03-Fix). Löschung nur bei echtem Verschwinden; eine Verschiebung außerhalb der konfigurierten Base-DN erzeugt eine Fehlerzeile statt einer Löschung. A2 (byteweise Hex-Filter-Syntax) ist nicht gegen einen echten Domain Controller geprüft; ein negatives Ergebnis würde SC-4 in beide Richtungen falsch machen können (WINDOWS.md #4).

Phase 16: AD-Gruppen-Synchronisation Verification Report

Nachtrag 2026-08-11 — Abschluss der offenen Punkte

Die sechs human_verification-Punkte unten sind am 2026-08-11 abgearbeitet worden, mit einem substanziellen Fund und einer bewussten Entscheidung:

Geprüft und bestanden (Browser, alpha.tessera.ctl.de, Playwright): die Import-Sektion, der Gruppen-Dialog in allen drei Zuständen, der Fehlerpfad des Sync-Berichts und die Spaltensuche der Freigabe-Matrix unter internem wie AD-Namen. Details in 16-UAT.md, Tests 5 bis 7.

Geprüft und WIDERLEGT — Annahme A2: der binäre (objectGUID=...)-Filter wurde als escapter String gebaut und fand am echten AD nie etwas, auch nicht für existierende Objekte. Beide Suchen der Existenzprüfung teilten sich diesen Filter, also hätte der erste echte Sync-Lauf jede AD-gebundene Gruppe samt Mitgliedschaften und Modulfreigaben gelöscht. Behoben in Quick-Task 260811-f9i (Commit d2019dc): EqualityFilter über den rohen Buffer, gegengeprüft am echten Verzeichnis, plus zwei Regressionstests. Damit ist SC-4 nicht mehr PRESENT_BEHAVIOR_UNVERIFIED, sondern in seiner kritischen Mechanik belegt.

Bewusst nicht geprüft — Annahme A1 und die End-to-End-Läufe (Tests 1, 3, 4, 8): der User hat keine Schreibrechte im AD, und der Ersatzweg über einen eigenen Wegwerf-Domänencontroller wurde als unverhältnismäßig verworfen, nachdem der eine substanzielle Fehler bereits gefunden war. A1 (objectGUID übersteht eine Umbenennung) stützt sich damit auf die Microsoft-Dokumentation, nicht auf eine eigene Messung. Die Tessera-seitige Umbenennungs- und Löschlogik ist durch Unit-Tests gegen Testdaten belegt. Fällt im Betrieb auf, dass eine Umbenennung nicht nachzieht, ist das der Ansatzpunkt — siehe 16-UAT.md, Abschnitt "Entscheidung 2026-08-11".

Ebenfalls offen und bewusst so belassen: die Zahlenzeilen eines ERFOLGREICHEN Sync-Laufs (Test 7, zweiter Teil). Die lassen sich ohne AD-Rechte nachholen, sobald einmal ein Sync mit eng gesetztem Filter auf alpha läuft.

Phase Goal: Ausgewählte AD-Gruppen werden als Tessera-Gruppen angelegt und dauerhaft nachgeführt, statt jede Gruppe von Hand anzulegen und zu binden. Der Admin wählt aus der AD-Gruppenliste aus, welche Gruppen übernommen werden; der bestehende Benutzer-Sync hält Bestand, Namen und Mitgliedschaften danach aktuell. Verified: 2026-08-06T15:07:32Z Status: human_needed Re-verification: No — initial verification

Zusammenfassung der Prüfung

Alle fünf Erfolgskriterien sind im Code vollständig, sauber verdrahtet und durch Unit-Tests belegt — ich habe jede Behauptung aus den fünf SUMMARY.md-Dateien gegen die tatsächlichen Quelldateien gegengeprüft, nicht nur die Zusammenfassungen gelesen. Zwei Punkte verhindern trotzdem ein glattes „passed": Erstens hängen SC-3 (Umbenennung) und SC-4 (Löschung) an zwei Annahmen über das Verhalten eines echten Active Directory (A1: objectGUID übersteht eine Umbenennung; A2: die byteweise Hex-Filter-Syntax wird von einem realen Domain Controller akzeptiert), die in dieser Umgebung nicht gegen ein echtes Verzeichnis geprüft werden konnten — nur gegen Mocks. Zweitens sind mehrere manuelle Browser-Durchklicke und eine Reihe von Plan-intern als verification: backstop markierten Aussagen (Nebenläufigkeit, Sortierabweichungen, Layout-Overflow) nicht automatisiert nachweisbar. Beides ist in den Plan-Artefakten selbst bereits ehrlich als offen dokumentiert (WINDOWS.md #4/#5/#6) — diese Verifikation bestätigt das, statt es zu überschreiben.

Goal Achievement

Observable Truths (fünf Erfolgskriterien)

# Truth Status Evidence
SC-1 Admin wählt AD-Gruppen einzeln aus und importiert sie; je Auswahl entsteht eine Tessera-Gruppe mit ldapDn + ldapObjectGuid ✓ VERIFIED apps/api/src/ldap/ldap.service.ts:643 importGroupsByDn() legt pro DN genau eine Group-Zeile mit name, ldapDn, ldapObjectGuid an (tenantPrisma.group.create); POST /ldap/groups/import (ldap.controller.ts:190) unter @Roles(ADMIN, SUPER_ADMIN); UI-Sektion „AD-Gruppen importieren" in admin/ldap/page.tsx (Checkbox-Auswahl, Import-Button mit Auswahlzahl). 60 grüne Fälle in ldap.service.spec.ts, inkl. Doppel-Import (skip), Namenskollision (reject-with-report), fehlender objectGUID-Buffer.
SC-2 Nicht ausgewählte AD-Gruppen werden nicht angelegt — keine OU-weite Automatik ✓ VERIFIED importGroupsByDn(config, tenantId, dns) verarbeitet ausschließlich die im dns-Array übergebenen Einträge; kein Codepfad iteriert über ein Suchergebnis oder eine OU. ImportGroupsDto lehnt ein leeres Array ab (@ArrayNotEmpty()). Frontend postet nur selectedImportDns.
SC-3 Umbenennung im AD zieht beim nächsten Sync in Group.name/Group.ldapDn nach, Mitgliedschaften/Freigaben bleiben erhalten ⚠️ PRESENT_BEHAVIOR_UNVERIFIED Code vollständig vorhanden und korrekt verdrahtet: syncBoundGroupsForTenant() (ldap.service.ts:1223) läuft nachweislich vor syncGroupMembershipsForTenant() (Zeile 920 vor 927, Call-Order-Test vorhanden), Rename-Zweig schreibt name/ldapDn byte-exakt aus dem AD, internalName bleibt unangetastet (grep -c internalName im Methodenkörper = 0), case-insensitive Change-Detection (WR-04-Fix) verhindert Idempotenz-Verletzung durch Groß-/Kleinschreibung. Aber: die gesamte Unterscheidung Rename-vs-Delete hängt an Annahme A1 (objectGUID bleibt über eine AD-Umbenennung stabil), die nur gegen ldapts-Mocks getestet ist — nie gegen ein echtes Verzeichnis (WINDOWS.md #4). Mitgliedschaften/Freigaben werden bei einem reinen Rename nicht angefasst (kein group.delete/create im Rename-Zweig), das ist strukturell korrekt, sofern A1 hält.
SC-4 Verschwinden im AD löscht die Tessera-Gruppe samt Mitgliedschaften und Modulfreigaben ⚠️ PRESENT_BEHAVIOR_UNVERIFIED Code vollständig vorhanden: binärer Existenz-Sweep mit 32-Hex-Validierung vor jeder Filter-Interpolation, reassignDefaultBeforeDelete() läuft nachweislich vor group.delete() (Zeilenreihenfolge geprüft), Cascade auf GroupMembership/ModuleGrant existiert seit Migration 15-01, ensureDefaultGroup() läuft nach jeder Löschung. Der Review-Fund WR-03 (fälschliche Löschung bei reiner AD-Verschiebung außerhalb der Base-DN) ist behoben und mit drei neuen Tests abgesichert (Commit 00de6a6). Aber: ob der binäre (objectGUID=...)-Filter (Annahme A2) von einem echten Domain Controller tatsächlich als Treffer/Nicht-Treffer korrekt interpretiert wird, ist nicht geprüft — nur simuliert. Ein negatives Ergebnis wäre laut RESEARCH.md/16-03-PLAN.md ein Stopp-Grund für die Löschsemantik.
SC-5 Lokal angelegte Gruppen ohne AD-Bindung bleiben vom Gruppen-Sync unberührt ✓ VERIFIED Kandidaten-Query in syncBoundGroupsForTenant() filtert OR: [{ldapObjectGuid: {not: null}}, {ldapDn: {not: null}}] — eine rein lokale Gruppe (beide Felder null) erfüllt keine der beiden Bedingungen und erscheint nie im Ergebnis, löst also keine LDAP-Suche und keine Schreiboperation aus. Zusätzlich verhindert GroupFormModal.tsx (nach D-07-Rückbau) jede nachträgliche AD-Bindung einer lokalen Gruppe: kein Radio-Element (grep -c 'type="radio"' = 0), kein Discovery-Aufruf (grep -c 'ldap/groups' = 0), UpdateGroupDto besitzt kein ldapDn-Feld mehr.

Score: 5/5 Erfolgskriterien code-vollständig; 3/5 zusätzlich per Unit-Test gegen echtes Laufzeitverhalten bewiesen (SC-1, SC-2, SC-5); 2/5 (SC-3, SC-4) präsent und verdrahtet, aber deren Kernannahme (A1/A2) nicht gegen ein echtes Active Directory geprüft.

D-Entscheidungen (Auswahl der wichtigsten, code-geprüft)

Entscheidung Status Evidence
D-03 (Namensfeld importierter Gruppen serverseitig gesperrt) ✓ VERIFIED GroupsService.update() wirft BadRequestException, wenn existing.ldapObjectGuid || existing.ldapDn gesetzt ist (Zeile 84 in groups.service.ts) — das ist der Review-Fund WR-01, der ursprünglich nur ldapObjectGuid prüfte und damit Alt-Bindungen vor dem ersten Sync-Lauf umgehbar machte; Fix in Commit dd59bf5 bestätigt im Code vorhanden.
D-04 (interner Name, sync-immun, vier Anzeigestellen) ✓ VERIFIED Spalte existiert (Schema + Migration + laufende DB bestätigt), syncBoundGroupsForTenant() schreibt internalName nirgends, alle vier geplanten Anzeigestellen bestätigt: listForTenant(), module-grants.service.ts#getUserAccess (beide Projektionen), admin/groups/page.tsx Namenszelle, admin/modules/grants/page.tsx Spaltenkopf — überall Nullish-Fallback internalName ?? name, keine Truthiness-Variante gefunden.
D-05 (Löschung im AD löscht ohne Warndialog, bewusst akzeptiert) ✓ VERIFIED (Sichtbarkeitsmaßnahme) Kein Warndialog im Sync-Pfad (bewusst); stattdessen zeigt der Sync-Bericht groupsDeleted/defaultMarkerMoved in admin/ldap/page.tsx — die einzige laut D-05 vorgesehene Gegenmaßnahme ist vorhanden.
D-06 (Standardmarkierung wandert vor Löschung) ✓ VERIFIED reassignDefaultBeforeDelete() deterministisch (DEFAULT_GROUP_NAME zuerst, sonst älteste Gruppe per createdAt asc), nicht werfend, wird in syncBoundGroupsForTenant() nachweislich vor group.delete() aufgerufen; ensureDefaultGroup() läuft nach jeder Löschung. 6 gezielte Testfälle grün.
D-07 (lokale Gruppe kann nicht mehr an AD gebunden werden) ✓ VERIFIED Radio-Auswahl vollständig aus GroupFormModal.tsx entfernt; UpdateGroupDto ohne ldapDn-Feld; Kandidaten-Query des Sync berührt lokale Gruppen nie.

Requirements Coverage

Requirement Quelle Beschreibung Status Evidence
PERM-02 Alle 5 Pläne (requirements: [PERM-02]) Selektiver AD-Gruppen-Import mit Nachführung (Rename/Delete), lokale Gruppen unberührt, keine nachträgliche Bindung ✓ SATISFIED auf Code-Ebene, mit offener Live-Verifikationsschuld REQUIREMENTS.md markiert PERM-02 als [x] und „Complete" seit Plan 16-05 (2026-08-06). Diese Markierung ist auf Code-Vollständigkeits-Ebene gerechtfertigt — alle fünf Erfolgskriterien sind implementiert, verdrahtet und wo automatisiert prüfbar auch getestet. Sie verschweigt aber nicht die offene Live-AD-Prüfung: 16-03-SUMMARY.md, 16-05-SUMMARY.md und WINDOWS.md #4 halten A1/A2 ausdrücklich als unverifiziert fest und benennen ein negatives Ergebnis explizit als Stopp-Grund für die D-05-Löschsemantik. Die Requirements-Traceability-Tabelle selbst transportiert diese Nuance nicht (sie kennt nur „Complete"/„Pending") — das ist eine Grenze des Tools, keine Falschaussage der Pläne.

Keine verwaisten Requirement-IDs gefunden — PERM-02 ist die einzige der Phase zugeordnete ID und wird durchgängig in allen fünf Plan-Frontmatter-Blöcken referenziert.

Required Artifacts

Artifact Erwartet Status Details
apps/api/prisma/schema.prisma Group.internalName, Group.ldapObjectGuid, @@unique([tenantId, ldapObjectGuid]) ✓ VERIFIED Zeilen 143-153 bestätigt
apps/api/prisma/migrations/20260806133916_add_group_internal_name_and_object_guid/ Versionierte Migration ✓ VERIFIED Existiert, per migration-sql.spec.ts (14 Fälle) festgenagelt, und live gegen die lokale DB angewendet (psql \d "Group" zeigt beide Spalten + Unique-Index; _prisma_migrations bestätigt finished_at gesetzt)
apps/api/src/ldap/ldap.service.ts listGroups() mit alreadyImported, importGroupsByDn(), syncBoundGroupsForTenant(), escapeLdapFilterBuffer() ✓ VERIFIED Alle vier Symbole vorhanden und aufgerufen (kein toter Code)
apps/api/src/ldap/ldap.controller.ts POST /ldap/groups/import unter RBAC ✓ VERIFIED Zeile 190, @Roles(ADMIN, SUPER_ADMIN), statische Route vor jeder künftigen :id-Route dokumentiert
apps/api/src/groups/groups.service.ts DEFAULT_GROUP_NAME, reassignDefaultBeforeDelete(), Namenssperre, internalName-Handling ✓ VERIFIED Wie oben belegt
apps/api/src/groups/dto/update-group.dto.ts internalName statt ldapDn ✓ VERIFIED ldapDn nicht mehr vorhanden, internalName?: string | null vorhanden
apps/api/src/groups/module-grants.service.ts internalName ?? name an beiden Projektionsstellen ✓ VERIFIED Zeilen 25/34/49 bestätigt
apps/web/.../admin/ldap/page.tsx Import-Sektion, erweiterter Sync-Bericht, syncRequestError ✓ VERIFIED Alle State-Variablen, Handler und Berichtszeilen vorhanden; deactivated: 0-Fallback-Objekt entfernt
apps/web/.../admin/groups/components/GroupFormModal.tsx Drei Zustände ohne AD-Bindung ✓ VERIFIED Kein Radio, kein Discovery-Aufruf, ein einziger PATCH-Aufrufblock
apps/web/.../admin/groups/page.tsx Namenszelle mit Fallback + Tooltip ✓ VERIFIED internalName ?? group.name mit title={group.name}
apps/web/.../admin/modules/grants/page.tsx Spaltenkopf + Suchfilter mit Fallback ✓ VERIFIED Beide Stellen bestätigt, Nullish-Variante
apps/web/src/messages/{de,en}.json Neue Schlüssel, verwaiste ldapBind.* entfernt ✓ VERIFIED Node-Paritätsprüfungen aus allen Plänen liefen zuletzt grün mit; keine erneute Prüfung nötig, da tsc/vitest diese implizit mitprüfen
From To Via Status Details
GroupFormModal.tsx (importierte Gruppe) PATCH /groups/:id { internalName }, niemals name ✓ WIRED Payload-Verzweigung über isImported, serverseitig durch GroupsService.update()-Sperre abgesichert (defense-in-depth, nicht nur UI)
syncBoundGroupsForTenant() syncGroupMembershipsForTenant() Aufrufreihenfolge in syncUsersForTenant() ✓ WIRED Zeile 920 vor Zeile 927, zusätzlich durch Call-Order-Spy-Test abgesichert
syncBoundGroupsForTenant() (Löschzweig) GroupsService.reassignDefaultBeforeDelete() → group.delete() → ensureDefaultGroup() Reihenfolge im Methodenkörper ✓ WIRED Grep-Zeilenvergleich bestätigt Reihenfolge; ensureDefaultGroup läuft einmal nach der Schleife bei mind. einer Löschung
LdapService.importGroupsByDn forTenant(prisma, tenantId).group.create RLS-Scoping ✓ WIRED Bestätigt in Zeile ~700
Backend LdapSyncResult Frontend SyncResult Feldnamen-Parität ✓ WIRED groupsAdopted/groupsRenamed/groupsDeleted/defaultMarkerMoved/groupMembershipsAdded/groupMembershipsRemoved — alle sechs Felder im Frontend-Interface vorhanden, Namen identisch

Anti-Patterns

Keine Debt-Marker (TBD/FIXME/XXX/TODO/HACK/PLACEHOLDER) in den elf geprüften Kern-Dateien der Phase gefunden. Zwei vom Code-Review dokumentierte, bewusst offen gelassene Info-Findings bleiben unverändert bestehen (nicht blockierend, laut 16-REVIEW.md-Frontmatter open_findings: [IN-01, IN-02]):

  • IN-01 — veralteter Kommentar in groups.controller.ts:65 behauptet noch „AD-Bindung setzen/lösen" über PATCH /groups/:id, obwohl das seit D-07 nicht mehr möglich ist (das DTO selbst wurde korrekt aktualisiert). Kosmetisch, keine funktionale Auswirkung.
  • IN-02 — internalName trägt kein @MaxLength() in update-group.dto.ts. Kein Sicherheitsrisiko (Spalte ist TEXT, RLS greift), aber ein potenziell sehr langer Wert würde unbegrenzt persistiert.

Diese beiden Punkte sind Informationsfunde des Code-Reviews, keine neuen Beobachtungen dieser Verifikation, und wurden bewusst nicht behoben (siehe 16-REVIEW.md Frontmatter). Sie sind kein Grund für gaps_found.

Anti-Patterns explizit NICHT gefunden

  • Kein stiller catch {} in den geänderten Frontend-Handlern (Discovery, Import, Sync, GroupFormModal-Speichern) — alle vier zeigen einen sichtbaren text-*-destructive-Zustand.
  • Keine Truthiness-Fallbacks (internalName ||) an den vier Anzeigestellen — durchgängig ??.
  • Kein zweiter Schreibpfad, der eine AD-Bindung setzen könnte.

Testlage

  • cd apps/api && npx vitest run — 40 Dateien, 566 Tests, grün.
  • cd apps/web && npx vitest run — 32 Dateien, 192 Tests, grün.
  • cd apps/api && npx tsc --noEmit — fehlerfrei.
  • cd apps/web && npx tsc --noEmit — fehlerfrei.
  • Migration live gegen die lokale Datenbank angewendet und per \d "Group" gegengeprüft.
  • Gezielter Re-Run der drei phasenrelevanten Testdateien (ldap.service.spec.ts, groups.service.spec.ts, module-grants.service.spec.ts): 130 Tests, grün.
  • Kein Probe-Skript für diese Phase vorgesehen (scripts/*/tests/probe-*.sh existiert nicht für Phase 16) — Schritt 7c entfällt.

Human Verification Required

Siehe YAML-Frontmatter human_verification oben für die vollständige, strukturierte Liste. Zusammengefasst, in Prioritätsreihenfolge:

  1. A1/A2 Live-AD-Prüfung (höchste Priorität, WINDOWS.md #4) — read-only gegen ViCoTest (balios.ctl.local): objectGUID-Stabilität über eine Umbenennung, byteweise Hex-Filter-Treffer. Ein negatives Ergebnis ist laut Plan-Vorgabe selbst ein Stopp-Grund für die Löschsemantik aus D-05 und müsste zu einer neuen Planungsentscheidung führen, nicht zu einem stillen Codefix.
  2. Browser-Durchklick GroupFormModal + Gruppenliste (WINDOWS.md #5) — alle drei Dialogzustände, Fehleranzeige, Namensanzeige nach dem Speichern.
  3. Browser-Durchklick Sync-Bericht + Freigabe-Matrix (WINDOWS.md #6) — alle Berichtszeilen inkl. Amber-Zeile, Matrix-Spaltensuche unter beiden Namen.
  4. Backstop-Aussagen aus den must_haves.truths der fünf Pläne (Nebenläufigkeit bei parallelen Import-/Sync-Requests, Reihenfolgeunabhängigkeit, lange Texte in Listen/Dialogen) — laut eigener Plan-Deklaration nicht automatisiert prüfbar.

Gaps Summary

Kein Gap im Sinne von „fehlender oder kaputter Code" gefunden. Alle Artefakte existieren, sind substanziell (keine Stubs), verdrahtet und (soweit automatisiert möglich) getestet. Die Einstufung human_needed statt passed beruht ausschließlich darauf, dass zwei der fünf Erfolgskriterien an einer gegen echtes Active-Directory-Verhalten ungeprüften Annahme hängen, und dass mehrere manuelle Verifikationsschritte in dieser Umgebung nicht ausführbar waren (kein Browser-Tool, kein erreichbares AD). Das ist keine neue Erkenntnis dieser Verifikation — alle fünf SUMMARY.md-Dateien und WINDOWS.md dokumentieren diese Lücken bereits selbst und ehrlich; diese Verifikation bestätigt sie unabhängig, statt sie zu wiederholen oder zu übergehen.


Verified: 2026-08-06T15:07:32Z Verifier: Claude (gsd-verifier)