Files
tessera-ctl/.planning/WINDOWS.md
T
schalli a1a8b4fa7b test(ldap): Live-Test gegen echtes AD — A2 belegt, zwei neue Defekte gefunden
Auf alpha gegen das echte Active Directory balios.ctl.local geprueft.

Belegt:
- WINDOWS #4 / Annahme A2 (byteweise Filter-Syntax): read-only gemessen.
  EqualityFilter ueber rohen Buffer findet Claude_VT (DN und objectGUID
  stimmen mit der Tessera-DB ueberein), der frueher verwendete escapte
  Hex-String findet nichts. Der erwartete DN stammt aus der Datenbank,
  nicht aus der Filter-Hilfsfunktion — sonst waere die Messung tautologisch.
- WINDOWS #6 Teil (a): Sync ausgeloest, alle drei Zahlenzeilen erscheinen.
- WINDOWS #6 Teil (c): die Spalte ist in der Freigaben-Matrix sowohl unter
  dem internen Namen als auch unter dem AD-Namen auffindbar.

Weiterhin offen, weil Schreibzugriff im Verzeichnis noetig:
- #4 / A1 (objectGUID uebersteht Umbenennung)
- #6 Teil (b) (Amber-Zeile bei verschobener Standardmarkierung). Ueber den
  konfigurierten Suchbereich nicht nachstellbar — die WR-03-Weitsuche
  verhindert das zu Recht.

Neu im Ledger:
- #14: Die Suche in der Freigaben-Matrix filtert beide Achsen mit demselben
  Begriff. Ein Begriff, der nur eine Achse trifft, leert die andere ganz —
  es bleibt nie ein Kaestchen zum Klicken. Damit scheitert genau der Zweck
  der Suche.
- #15: Der Sync reicht rohe Prisma-Fehler und englische Techniktexte an den
  Administrator durch. Vier AD-Konten aus OU=CTL_PWS_Gruppen werden wegen
  einer geteilten E-Mail-Adresse nie importiert, ohne verstaendlichen Hinweis.

Der Testzustand wurde zurueckgesetzt: Cert Manager wieder deaktiviert,
interner Name wieder geleert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-07 14:49:07 +02:00

23 KiB

schema_version, open_count, waived_count, fixed_count, total_count, last_updated
schema_version open_count waived_count fixed_count total_count last_updated
1 5 0 10 15 2026-09-07T12:47:32.058Z

Broken Windows Ledger

Cross-phase defect register. With workflow.windows_enforce enabled, /gsd-ship blocks while open_count > 0. Waive with gsd-tools windows waive <id> "<reason>" (reason required). Mark fixed with gsd-tools windows fixed <id>.

id phase kind file line description status reason recorded_at resolved_at
1 15 unrun-verify .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-06-PLAN.md 15-06 : manueller Browser-Durchklick (Anlegen/Umbenennen/Standardmarkierung/AD-Bindung/Mitglieder/Loeschdialog + Fehlerpfade bei abgeschalteter API) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar fixed 2026-08-04T17:05:42.026Z 2026-09-07T08:03:50.832Z
2 15 unrun-verify .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-PLAN.md Manueller Browser-Durchklick aus dem Plan-Verification-Block (Matrix-Freigabe setzen/entziehen, Aktivierungsdialog beide Wege, Direkt-Grant neben Gruppen-Grant, Fehlerfall bei gestoppter API, lange Namen) nicht ausgefuehrt -- kein Browser-Tool in dieser Session (15-07-SUMMARY.md D4) fixed 2026-08-04T17:27:42.267Z 2026-09-07T08:03:51.067Z
3 15 unrun-verify .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-08-PLAN.md Manueller Browser-Durchklick aus dem Plan-Verification-Block (USER ohne Freigabe: Sidebar/403-Seite/Marketplace-Badge+Toast/API-403 identisch; ADMIN: alle vier Ebenen zugaenglich) nicht ausgefuehrt - kein Browser-Tool in dieser Session verfuegbar. fixed 2026-08-04T17:50:13.320Z 2026-09-07T08:03:51.288Z
4 16 unrun-verify .planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md 16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05). open 2026-08-06T14:21:59.987Z
5 16 unrun-verify .planning/phases/16-ad-gruppen-synchronisation/16-04-PLAN.md Manueller Browser-Durchklick aus dem Plan-Verification-Block (drei GroupFormModal-Zustaende: Anlegen mit Hinweis-Link, lokale Gruppe umbenennen, importierte Gruppe mit gesperrtem Namen + internem Namen speichern; Namensanzeige mit Tooltip in der Liste nach dem Speichern) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. fixed 2026-08-06T14:31:14.336Z 2026-09-07T08:03:51.517Z
6 16 unrun-verify .planning/phases/16-ad-gruppen-synchronisation/16-05-PLAN.md 16-05 Manuell nachzuholen (Browser): Sync ausloesen und pruefen, dass alle drei Zahlenzeilen erscheinen; Lauf mit verschobener Standardmarkierung provozieren und Amber-Zeile pruefen; Freigabe-Matrix-Spaltensuche unter beiden Namen probieren - nicht ausgefuehrt, kein Browser-Tool in dieser Session verfuegbar. open 2026-08-06T14:39:03.905Z
7 17 unrun-verify .planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-PLAN.md 17-01 Task 2 human-check: Browser-Gegenprobe (normaler Nutzer oeffnet /modules/tender-radar/my-sources, speichert Postfach, zweites Konto desselben Mandanten sieht leeres Formular) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. Automatisierte Pruefungen (prisma validate/migrate status, Index-Liste, beide Typpruefungen, 335/335 src/tenders-Tests) sind gelaufen und gruen. fixed 2026-08-12T09:24:35.289Z 2026-09-07T07:50:56.468Z
8 17 unrun-verify apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx Browser-Gegenprobe Plan 17-03 Task 1 (Tracer-Feedback-Gate): Meine Quellen mit drei Abschnitten, neu angelegter eigener Feed sofort sichtbar, plattformweiter Feed ohne Entfernen-Knopf, Digest-Intervall speichert nach Reload — kein Browser-Tool in dieser Sitzung verfuegbar fixed 2026-08-12T10:02:49.122Z 2026-09-07T07:50:56.693Z
9 17 unrun-verify apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx Browser-Gegenprobe Plan 17-03 Task 2: USER-Konto sieht keine Bedienelemente auf /settings (nur Hinweis+Verweis), ADMIN-Konto sieht Abrufintervall+plattformweite Feeds ohne Postfach/Benachrichtigung, Zahnrad fuehrt in beiden Faellen nach Meine Quellen — kein Browser-Tool in dieser Sitzung verfuegbar fixed 2026-08-12T10:02:49.291Z 2026-09-07T07:50:56.910Z
10 15 unmet-truth apps/web/src/app/(portal)/modules/tender-radar/page.tsx PERM-04 greift nicht auf modul-eigenen Routen: der Zugriffs-Guard sitzt nur in modules/[category]/[moduleSlug]/page.tsx. Die vier fest verdrahteten Modulrouten (tender-radar, dkv-fleet, cert-manager, domaincheck) samt Unterseiten (z.B. tender-radar/my-sources) laufen daran vorbei und rendern fuer einen USER OHNE Modulfreigabe vollstaendig, inklusive bedienbarer Knoepfe. Im Browser gemessen am 2026-09-07 mit nutzer2 (keine Freigabe): /modules/procurement/tender-radar zeigt korrekt die 403-Seite, /modules/tender-radar und /modules/tender-radar/my-sources und /modules/dkv-fleet zeigen die volle Seite. Keine Datenpreisgabe - die API antwortet auf allen Endpunkten mit 403 - aber der Nutzer sieht Bedienelemente, die er nicht benutzen darf, und bekommt rohe englische Techniktexte statt einer verstaendlichen Meldung. Beleg: .planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/uat-2026-09-07/befund-modul-eigene-route-ohne-freigabe.png fixed 2026-09-07T08:03:45.978Z 2026-09-07T08:40:04.951Z
11 13 unmet-truth apps/api/src/tenders/tender-fingerprint.ts Cross-Source-Dedup kollabiert die Mehrzahl echter Duplikate nicht: tenderFingerprint() haengt [buyerName, title, cpvDivisionKey, deadlineKey, valueBucket] zu einem String und hasht ihn. Die Scraper-Adapter liefern cpvDivisions immer leer, DOE dagegen meist gefuellt - dieselbe Ausschreibung ergibt aus beiden Quellen verschiedene Fingerabdruecke, die Fingerprint-Stufe greift also gerade dort nicht, wo sie greifen soll. Steht als offener Rest in 13-VERIFICATION.md (status gaps_found, 3/4 Wahrheiten), war aber bisher nicht im Ledger und damit unsichtbar. Die frueher vermutete Ursache (Normalizer-Defaults) ist mit 9881005 geschlossen und nicht gemeint. fixed 2026-09-07T08:09:04.188Z 2026-09-07T08:42:34.794Z
12 14 unrun-verify .planning/phases/14-rss-email-alert-ingestion-module-rollout/14-03-PLAN.md 14-03 Task 4 (checkpoint human-verify, gate blocking): echtes Portal-Alert-Postfach ueber den Exchange/EWS-Weg anbinden und eine echte Alarm-Mail ingestieren. Zu belegen sind (a) die Mail erzeugt eine mandantenprivate Ausschreibung, (b) ein anderer Mandant sieht sie nicht, (c) der EWS-Body-Abruf liefert nicht-leeren, nicht-verstuemmelten Inhalt gegen einen echten Exchange-Server. Der handgeschriebene NTLM/SOAP-Weg ist nur gegen gemocktes httpntlm.post geprueft, nie gegen einen echten Server. Braucht ein echtes Postfach - vom User am 2026-07-24 bewusst zurueckgestellt. Stand bisher nur in 14-VERIFICATION.md (status human_needed), nicht im Ledger. open 2026-09-07T08:09:04.406Z
13 1 unmet-truth apps/web/src/app/globals.css Tailwind-4-Dunkelvariante war nie an die .dark-Klasse gebunden: globals.css definierte die Farbtokens unter .dark, deklarierte aber kein @custom-variant dark. In Tailwind 4 haengt die dark:-Utility-Variante per Vorgabe an prefers-color-scheme, nicht an einer Klasse. Die App schaltet den Modus jedoch ueber next-themes mit attribute=class. Folge: JEDE dark:-Utility im Web-Quellcode (ueber 100 Vorkommen in 10+ Dateien - Status-, Warn- und Fehlerfarben, Hinweisboxen, Badges in der Administration) blieb wirkungslos, sobald der Nutzer im Portal auf dunkel stellte, waehrend sein Betriebssystem hell stand. Am laufenden System gemessen: html.dark gesetzt, prefers-color-scheme hell, dark:bg-gray-800 ergab transparent und dark:text-green-400 blieb ohne Wirkung. Unentdeckt geblieben, weil Hintergrund und Textfarbe ueber CSS-Variablen unter .dark laufen und deshalb korrekt umschalten. Aufgedeckt beim Logo-Einbau 260907-fgv, dessen Plattenkontur dark:stroke-white/25 aus demselben Grund nicht griff. fixed 2026-09-07T09:32:24.960Z 2026-09-07T09:35:49.019Z
14 15 unmet-truth apps/web/src/app/(portal)/admin/modules/grants/page.tsx Die Suche in der Freigaben-Matrix macht die Matrix unbenutzbar, sobald sie etwas findet: derselbe Suchbegriff filtert BEIDE Achsen unabhaengig voneinander (page.tsx:137 filtert Module ueber m.name, page.tsx:145 filtert Gruppen ueber internalName/name). Ein Begriff, der nur eine Achse trifft, leert die andere vollstaendig — es bleibt nie ein Kaestchen zum Klicken uebrig. Am 2026-09-07 im Browser auf alpha gemessen: Suche 'Claude_VT' bzw. 'Vertrieb' laesst die Gruppenspalte stehen, entfernt aber jede Modulzeile; Suche 'Cert' laesst die Modulzeile stehen, entfernt aber jede Gruppenspalte. Damit scheitert genau der Zweck der Suche — in einer grossen Matrix die Kreuzung Modul x Gruppe finden. Belege: .planning/phases/16-ad-gruppen-synchronisation/uat-2026-09-07/befund-matrix-suche-gruppenname.png und befund-matrix-suche-modulname.png open 2026-09-07T12:46:56.172Z
15 16 unmet-truth apps/api/src/ldap/ldap.service.ts Der Sync reicht rohe Techniktexte an den Administrator durch und laesst echte AD-Konten still liegen. Am 2026-09-07 auf alpha gegen das echte AD gemessen (Lauf 14:42): zehn Fehlerzeilen unter den drei Zahlenzeilen, davon zwei Sorten. (1) Vier Konten scheitern mit der woertlichen Prisma-Meldung 'Invalid prisma.user.create() invocation: Unique constraint failed on the fields: (email)' — CN=uvertrieb_ro, uvertrieb_rw, uvertrieb_ro_ss, usoftware_rw aus OU=CTL_PWS_Gruppen teilen sich offenbar eine E-Mail-Adresse. Sie werden dadurch NIE importiert, ohne dass der Administrator erfaehrt warum oder was er tun soll. (2) Sechs Eintraege melden englisch 'no username mapped (check sAMAccountName mapping)' — korrekt uebersprungene Kontakte/Ressourcen ohne sAMAccountName, aber die Meldung liest sich wie ein Fehler und ist nicht uebersetzt. Beides braucht eine verstaendliche deutsche Meldung; die E-Mail-Kollision zusaetzlich eine Entscheidung, ob solche Konten ohne E-Mail angelegt oder bewusst uebersprungen werden. Beleg: .planning/phases/16-ad-gruppen-synchronisation/uat-2026-09-07/windows6a-sync-zahlenzeilen.png open 2026-09-07T12:47:32.058Z
[
  {
    "id": 1,
    "kind": "unrun-verify",
    "phase": "15",
    "file": ".planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-06-PLAN.md",
    "line": null,
    "description": "15-06 <verification>: manueller Browser-Durchklick (Anlegen/Umbenennen/Standardmarkierung/AD-Bindung/Mitglieder/Loeschdialog + Fehlerpfade bei abgeschalteter API) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-04T17:05:42.026Z",
    "resolved_at": "2026-09-07T08:03:50.832Z"
  },
  {
    "id": 2,
    "kind": "unrun-verify",
    "phase": "15",
    "file": ".planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-PLAN.md",
    "line": null,
    "description": "Manueller Browser-Durchklick aus dem Plan-Verification-Block (Matrix-Freigabe setzen/entziehen, Aktivierungsdialog beide Wege, Direkt-Grant neben Gruppen-Grant, Fehlerfall bei gestoppter API, lange Namen) nicht ausgefuehrt -- kein Browser-Tool in dieser Session (15-07-SUMMARY.md D4)",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-04T17:27:42.267Z",
    "resolved_at": "2026-09-07T08:03:51.067Z"
  },
  {
    "id": 3,
    "kind": "unrun-verify",
    "phase": "15",
    "file": ".planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-08-PLAN.md",
    "line": null,
    "description": "Manueller Browser-Durchklick aus dem Plan-Verification-Block (USER ohne Freigabe: Sidebar/403-Seite/Marketplace-Badge+Toast/API-403 identisch; ADMIN: alle vier Ebenen zugaenglich) nicht ausgefuehrt - kein Browser-Tool in dieser Session verfuegbar.",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-04T17:50:13.320Z",
    "resolved_at": "2026-09-07T08:03:51.288Z"
  },
  {
    "id": 4,
    "kind": "unrun-verify",
    "phase": "16",
    "file": ".planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md",
    "line": null,
    "description": "16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05).",
    "status": "open",
    "reason": "",
    "recorded_at": "2026-08-06T14:21:59.987Z",
    "resolved_at": null
  },
  {
    "id": 5,
    "kind": "unrun-verify",
    "phase": "16",
    "file": ".planning/phases/16-ad-gruppen-synchronisation/16-04-PLAN.md",
    "line": null,
    "description": "Manueller Browser-Durchklick aus dem Plan-Verification-Block (drei GroupFormModal-Zustaende: Anlegen mit Hinweis-Link, lokale Gruppe umbenennen, importierte Gruppe mit gesperrtem Namen + internem Namen speichern; Namensanzeige mit Tooltip in der Liste nach dem Speichern) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar.",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-06T14:31:14.336Z",
    "resolved_at": "2026-09-07T08:03:51.517Z"
  },
  {
    "id": 6,
    "kind": "unrun-verify",
    "phase": "16",
    "file": ".planning/phases/16-ad-gruppen-synchronisation/16-05-PLAN.md",
    "line": null,
    "description": "16-05 <verification> Manuell nachzuholen (Browser): Sync ausloesen und pruefen, dass alle drei Zahlenzeilen erscheinen; Lauf mit verschobener Standardmarkierung provozieren und Amber-Zeile pruefen; Freigabe-Matrix-Spaltensuche unter beiden Namen probieren - nicht ausgefuehrt, kein Browser-Tool in dieser Session verfuegbar.",
    "status": "open",
    "reason": "",
    "recorded_at": "2026-08-06T14:39:03.905Z",
    "resolved_at": null
  },
  {
    "id": 7,
    "kind": "unrun-verify",
    "phase": "17",
    "file": ".planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-PLAN.md",
    "line": null,
    "description": "17-01 Task 2 human-check: Browser-Gegenprobe (normaler Nutzer oeffnet /modules/tender-radar/my-sources, speichert Postfach, zweites Konto desselben Mandanten sieht leeres Formular) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. Automatisierte Pruefungen (prisma validate/migrate status, Index-Liste, beide Typpruefungen, 335/335 src/tenders-Tests) sind gelaufen und gruen.",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-12T09:24:35.289Z",
    "resolved_at": "2026-09-07T07:50:56.468Z"
  },
  {
    "id": 8,
    "kind": "unrun-verify",
    "phase": "17",
    "file": "apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx",
    "line": null,
    "description": "Browser-Gegenprobe Plan 17-03 Task 1 (Tracer-Feedback-Gate): Meine Quellen mit drei Abschnitten, neu angelegter eigener Feed sofort sichtbar, plattformweiter Feed ohne Entfernen-Knopf, Digest-Intervall speichert nach Reload — kein Browser-Tool in dieser Sitzung verfuegbar",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-12T10:02:49.122Z",
    "resolved_at": "2026-09-07T07:50:56.693Z"
  },
  {
    "id": 9,
    "kind": "unrun-verify",
    "phase": "17",
    "file": "apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx",
    "line": null,
    "description": "Browser-Gegenprobe Plan 17-03 Task 2: USER-Konto sieht keine Bedienelemente auf /settings (nur Hinweis+Verweis), ADMIN-Konto sieht Abrufintervall+plattformweite Feeds ohne Postfach/Benachrichtigung, Zahnrad fuehrt in beiden Faellen nach Meine Quellen — kein Browser-Tool in dieser Sitzung verfuegbar",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-08-12T10:02:49.291Z",
    "resolved_at": "2026-09-07T07:50:56.910Z"
  },
  {
    "id": 10,
    "kind": "unmet-truth",
    "phase": "15",
    "file": "apps/web/src/app/(portal)/modules/tender-radar/page.tsx",
    "line": null,
    "description": "PERM-04 greift nicht auf modul-eigenen Routen: der Zugriffs-Guard sitzt nur in modules/[category]/[moduleSlug]/page.tsx. Die vier fest verdrahteten Modulrouten (tender-radar, dkv-fleet, cert-manager, domaincheck) samt Unterseiten (z.B. tender-radar/my-sources) laufen daran vorbei und rendern fuer einen USER OHNE Modulfreigabe vollstaendig, inklusive bedienbarer Knoepfe. Im Browser gemessen am 2026-09-07 mit nutzer2 (keine Freigabe): /modules/procurement/tender-radar zeigt korrekt die 403-Seite, /modules/tender-radar und /modules/tender-radar/my-sources und /modules/dkv-fleet zeigen die volle Seite. Keine Datenpreisgabe - die API antwortet auf allen Endpunkten mit 403 - aber der Nutzer sieht Bedienelemente, die er nicht benutzen darf, und bekommt rohe englische Techniktexte statt einer verstaendlichen Meldung. Beleg: .planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/uat-2026-09-07/befund-modul-eigene-route-ohne-freigabe.png",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-09-07T08:03:45.978Z",
    "resolved_at": "2026-09-07T08:40:04.951Z"
  },
  {
    "id": 11,
    "kind": "unmet-truth",
    "phase": "13",
    "file": "apps/api/src/tenders/tender-fingerprint.ts",
    "line": null,
    "description": "Cross-Source-Dedup kollabiert die Mehrzahl echter Duplikate nicht: tenderFingerprint() haengt [buyerName, title, cpvDivisionKey, deadlineKey, valueBucket] zu einem String und hasht ihn. Die Scraper-Adapter liefern cpvDivisions immer leer, DOE dagegen meist gefuellt - dieselbe Ausschreibung ergibt aus beiden Quellen verschiedene Fingerabdruecke, die Fingerprint-Stufe greift also gerade dort nicht, wo sie greifen soll. Steht als offener Rest in 13-VERIFICATION.md (status gaps_found, 3/4 Wahrheiten), war aber bisher nicht im Ledger und damit unsichtbar. Die frueher vermutete Ursache (Normalizer-Defaults) ist mit 9881005 geschlossen und nicht gemeint.",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-09-07T08:09:04.188Z",
    "resolved_at": "2026-09-07T08:42:34.794Z"
  },
  {
    "id": 12,
    "kind": "unrun-verify",
    "phase": "14",
    "file": ".planning/phases/14-rss-email-alert-ingestion-module-rollout/14-03-PLAN.md",
    "line": null,
    "description": "14-03 Task 4 (checkpoint human-verify, gate blocking): echtes Portal-Alert-Postfach ueber den Exchange/EWS-Weg anbinden und eine echte Alarm-Mail ingestieren. Zu belegen sind (a) die Mail erzeugt eine mandantenprivate Ausschreibung, (b) ein anderer Mandant sieht sie nicht, (c) der EWS-Body-Abruf liefert nicht-leeren, nicht-verstuemmelten Inhalt gegen einen echten Exchange-Server. Der handgeschriebene NTLM/SOAP-Weg ist nur gegen gemocktes httpntlm.post geprueft, nie gegen einen echten Server. Braucht ein echtes Postfach - vom User am 2026-07-24 bewusst zurueckgestellt. Stand bisher nur in 14-VERIFICATION.md (status human_needed), nicht im Ledger.",
    "status": "open",
    "reason": "",
    "recorded_at": "2026-09-07T08:09:04.406Z",
    "resolved_at": null
  },
  {
    "id": 13,
    "kind": "unmet-truth",
    "phase": "1",
    "file": "apps/web/src/app/globals.css",
    "line": null,
    "description": "Tailwind-4-Dunkelvariante war nie an die .dark-Klasse gebunden: globals.css definierte die Farbtokens unter .dark, deklarierte aber kein @custom-variant dark. In Tailwind 4 haengt die dark:-Utility-Variante per Vorgabe an prefers-color-scheme, nicht an einer Klasse. Die App schaltet den Modus jedoch ueber next-themes mit attribute=class. Folge: JEDE dark:-Utility im Web-Quellcode (ueber 100 Vorkommen in 10+ Dateien - Status-, Warn- und Fehlerfarben, Hinweisboxen, Badges in der Administration) blieb wirkungslos, sobald der Nutzer im Portal auf dunkel stellte, waehrend sein Betriebssystem hell stand. Am laufenden System gemessen: html.dark gesetzt, prefers-color-scheme hell, dark:bg-gray-800 ergab transparent und dark:text-green-400 blieb ohne Wirkung. Unentdeckt geblieben, weil Hintergrund und Textfarbe ueber CSS-Variablen unter .dark laufen und deshalb korrekt umschalten. Aufgedeckt beim Logo-Einbau 260907-fgv, dessen Plattenkontur dark:stroke-white/25 aus demselben Grund nicht griff.",
    "status": "fixed",
    "reason": "",
    "recorded_at": "2026-09-07T09:32:24.960Z",
    "resolved_at": "2026-09-07T09:35:49.019Z"
  },
  {
    "id": 14,
    "kind": "unmet-truth",
    "phase": "15",
    "file": "apps/web/src/app/(portal)/admin/modules/grants/page.tsx",
    "line": null,
    "description": "Die Suche in der Freigaben-Matrix macht die Matrix unbenutzbar, sobald sie etwas findet: derselbe Suchbegriff filtert BEIDE Achsen unabhaengig voneinander (page.tsx:137 filtert Module ueber m.name, page.tsx:145 filtert Gruppen ueber internalName/name). Ein Begriff, der nur eine Achse trifft, leert die andere vollstaendig — es bleibt nie ein Kaestchen zum Klicken uebrig. Am 2026-09-07 im Browser auf alpha gemessen: Suche 'Claude_VT' bzw. 'Vertrieb' laesst die Gruppenspalte stehen, entfernt aber jede Modulzeile; Suche 'Cert' laesst die Modulzeile stehen, entfernt aber jede Gruppenspalte. Damit scheitert genau der Zweck der Suche — in einer grossen Matrix die Kreuzung Modul x Gruppe finden. Belege: .planning/phases/16-ad-gruppen-synchronisation/uat-2026-09-07/befund-matrix-suche-gruppenname.png und befund-matrix-suche-modulname.png",
    "status": "open",
    "reason": "",
    "recorded_at": "2026-09-07T12:46:56.172Z",
    "resolved_at": null
  },
  {
    "id": 15,
    "kind": "unmet-truth",
    "phase": "16",
    "file": "apps/api/src/ldap/ldap.service.ts",
    "line": null,
    "description": "Der Sync reicht rohe Techniktexte an den Administrator durch und laesst echte AD-Konten still liegen. Am 2026-09-07 auf alpha gegen das echte AD gemessen (Lauf 14:42): zehn Fehlerzeilen unter den drei Zahlenzeilen, davon zwei Sorten. (1) Vier Konten scheitern mit der woertlichen Prisma-Meldung 'Invalid prisma.user.create() invocation: Unique constraint failed on the fields: (email)' — CN=uvertrieb_ro, uvertrieb_rw, uvertrieb_ro_ss, usoftware_rw aus OU=CTL_PWS_Gruppen teilen sich offenbar eine E-Mail-Adresse. Sie werden dadurch NIE importiert, ohne dass der Administrator erfaehrt warum oder was er tun soll. (2) Sechs Eintraege melden englisch 'no username mapped (check sAMAccountName mapping)' — korrekt uebersprungene Kontakte/Ressourcen ohne sAMAccountName, aber die Meldung liest sich wie ein Fehler und ist nicht uebersetzt. Beides braucht eine verstaendliche deutsche Meldung; die E-Mail-Kollision zusaetzlich eine Entscheidung, ob solche Konten ohne E-Mail angelegt oder bewusst uebersprungen werden. Beleg: .planning/phases/16-ad-gruppen-synchronisation/uat-2026-09-07/windows6a-sync-zahlenzeilen.png",
    "status": "open",
    "reason": "",
    "recorded_at": "2026-09-07T12:47:32.058Z",
    "resolved_at": null
  }
]