Commit Graph

899 Commits

Author SHA1 Message Date
schalli b6b964b69d test(ldap): WINDOWS #4 und #6 belegt und geschlossen — ohne Aenderung am AD
Am Active Directory wurde nichts veraendert; es wurde ausschliesslich gelesen.
Die beiden verbliebenen Pruefpunkte liessen sich auf der Tessera-Seite
ausloesen, weil der Sync nur vergleicht, was er gespeichert hat, mit dem, was
im Verzeichnis steht — ob eine Abweichung aus einer AD-Aenderung stammt oder
aus einem verfaelschten Group-Datensatz, kann er nicht unterscheiden.

#4 / A1 (Umbenennung bricht die Bindung nicht): Gruppe Albphone_Technik_VT neu
importiert, danach in der Datenbank Name und DN auf einen veralteten Stand
gesetzt, objectGUID echt gelassen. Der Sync fand die Gruppe allein ueber den
objectGUID und schrieb den AD-Namen zurueck — "1 umbenannt". Nicht gemessen,
weil dafuer das Verzeichnis geaendert werden muesste: dass AD den objectGUID
bei einer Umbenennung stabil haelt. Das ist zugesicherte AD-Eigenschaft und
kein Tessera-Code; der Anteil, der schiefgehen konnte, ist gemessen.

#6 / b (Amber-Zeile): dieselbe Gruppe zur Standardgruppe gemacht, dann ihren
gespeicherten objectGUID ins Leere zeigen lassen. Ergebnis: "1 geloescht" plus
die amber gefaerbte vierte Zeile "Standardgruppen-Markierung musste neu
vergeben werden (1x)". Die Markierung wanderte vor der Loeschung zurueck — die
Korrektheitszusage von D-06 haelt.

Ledger: #4 und #6 auf fixed. Offen bleiben #12 (braucht ein echtes
Exchange-Postfach), #14 und #15 (in dieser Sitzung neu gefunden).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-07 15:12:00 +02:00
schalli 1e6999071e docs: AD-Lesezugriff als bewusste Randbedingung festgehalten
Der Versuch, eine Wegwerf-Gruppe fuer den Livetest anzulegen, scheiterte mit
INSUFF_ACCESS_RIGHTS. Das Dienstkonto svc_tessera darf am Verzeichnis nur
lesen — vom User bestaetigt als gewollt, nicht als Luecke.

Folge, jetzt an drei Stellen dokumentiert (Livetest-Bericht, Deferred Items,
Ledger-Kontext): WINDOWS #4/A1 und #6b sind grundsaetzlich nicht aus Tessera
heraus belegbar. Beide brauchen eine Handlung durch einen AD-Administrator;
danach sind sie messbar. Kein Anlass, das Rechtekonzept zu aendern oder
Schreibrechte zu erbitten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-07 15:04:05 +02:00
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
schalli 98a8c93bfb chore(planning): Veraltete Checkpoints entfernt, ihr Wissen nach STATE.md ueberfuehrt
Die beiden .continue-here.md-Dateien stammten aus abgeschlossener Arbeit
(Phase 10 vom 2026-07-21, Meilenstein-Abschluss vom 2026-08-11) und waren als
Wiedereinstiegspunkt ueberholt. Ihre noch gueltigen Inhalte standen jedoch
nirgends sonst und waeren beim Loeschen verloren gegangen:

- fuenf Anti-Patterns (tautologischer Test, HTTP 200 als Funktionsbeleg,
  Image-Datum als Aktualitaets-Beleg, Server-Compose ist kein Checkout,
  stale live container maskiert Fertigstellung)
- Infrastruktur-Stand von alpha inkl. der Drift zwischen /opt/tessera und
  dem Repository
- der Blocker "kein AD-Schreibzugriff", der den noch offenen Live-Test
  WINDOWS #4 direkt betrifft

Beides ist jetzt als eigene Abschnitte unter Accumulated Context in STATE.md
festgehalten. Ausserdem den Blocker 15-06 als erledigt markiert (die
Gegenprobe wurde am 2026-09-07 nachgeholt) und .gsd/ ignoriert — das ist
Laufzeit-Scratch, das pro Sitzung neu geschrieben wird.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-07 14:36:43 +02:00
schalli cf24e978b4 docs: Sitzungsstand vom 2026-09-07 festgehalten
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 50s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s
STATE.md auf den Endstand gebracht, damit ein spaeteres /gsd-resume-work den
vollstaendigen Kontext findet: was erledigt wurde, was offen ist und warum.

Erledigt in dieser Sitzung: Phase 17 auf passed; die alten Browser-Gegenproben
#1, #2, #3 und #5 nachgeholt; drei echte Defekte gefunden und behoben (#10
Zugriffs-Guard griff nicht auf modul-eigenen Routen, #11 Fingerabdruck-Dedup war
wirkungslos, #13 Tailwind-Dunkelvariante nie an .dark gebunden); Logo und Favicon
eingebaut; Umlaute, Anrede und Ein-/Mehrzahl korrigiert; Meilenstein-Buchhaltung auf
v1.2 abgeschlossen.

Offen bleiben WINDOWS #4, #6 und #12 — alle drei brauchen ein echtes Active
Directory beziehungsweise Exchange-Postfach und sind als Deferred Items mit dem
jeweiligen Pruefauftrag im Klartext vorgemerkt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 14:23:29 +02:00
schalli 862988966f docs: Meilenstein-Buchhaltung auf den tatsaechlichen Stand gebracht
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 52s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s
Die Unterlagen fuehrten weiterhin v1.1 als laufenden Meilenstein, obwohl alle 17
Phasen und 83 Plaene ausgefuehrt sind und die Phasen 15 bis 17 zu v1.2 gehoeren. Wer
neu draufschaute, sah einen falschen Projektstand.

Korrigiert in ROADMAP.md, STATE.md und PROJECT.md:

v1.0 bleibt ausgeliefert. v1.1 traegt jetzt den ehrlichen Zwischenstand — alle Plaene
fertig, alle Phasen ausser 14 verifiziert, offen allein der Live-Test von Phase 14
gegen ein echtes Exchange-Postfach; inhaltlich abgeschlossen, formal nicht. v1.2 ist
abgeschlossen, alle drei Phasen verifiziert. Ergaenzt um den Hinweis, dass kein neuer
Meilenstein begonnen wurde und die drei verbleibenden Pruefpunkte Fremdsysteme
brauchen.

Ausserdem die Zeile zu Phase 2 praezisiert. Sie stand auf "Incomplete", was nach
liegengebliebener Arbeit klang und im Widerspruch zu v1.0 als ausgeliefertem
Meilenstein stand. Tatsaechlich sind alle vier Baupläne fertig und seit v1.0 in
Betrieb; offen ist allein die nie ausgefuehrte Browser-Abnahme, die der User am
2026-09-07 bewusst zurueckgestellt hat, weil Tessera zunaechst nur intern laeuft.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 14:11:05 +02:00
schalli bf3c082234 fix(web): Bildmarke statt Platzhalter im leeren Dashboard
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 49s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m28s
Ueber "Keine Widgets aktiv" stand noch dasselbe handgezeichnete
Vier-Quadrate-Symbol, das auf der Anmeldeseite bereits durch das Logo ersetzt wurde
— beim Logo-Einbau uebersehen, weil der leere Zustand nur ohne Widgets sichtbar ist.

Jetzt die echte Bildmarke aus der Marken-Komponente, ohne den bisherigen grauen
Rahmen und ohne Kontur: die Marke steht frei auf dem Seitenhintergrund und braucht
keine eigene Flaeche. 226 Web-Tests gruen, im Browser gegengeprueft.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 14:04:55 +02:00
schalli 31a92c9b9a fix(web): Schriftzug traegt im dunklen Modus die Markenfarbe
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 50s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m36s
Die vorherige Korrektur war zu grob: sie hat das Gelb ueberall durch die
Vordergrundfarbe ersetzt, obwohl es nur im hellen Erscheinungsbild ein Problem war.
Nachgerechnet gegen die tatsaechlichen Farbwerte aus globals.css:

  Gelb auf hellem Grund   1,26 : 1   (Kopfleiste)   unlesbar
  Gelb auf heller Leiste  1,24 : 1   (Seitenleiste) unlesbar
  Gelb auf dunklem Grund 13,06 : 1                  hervorragend
  Gelb auf dunkler Leiste 14,00 : 1                 hervorragend

WCAG verlangt 3:1 fuer grossen fetten Text. Das Gelb faellt also nur auf hellem
Grund durch — auf dunklem ist es die mit Abstand beste Wahl.

Der Schriftzug erbt jetzt im hellen Erscheinungsbild die Vordergrundfarbe (19:1) und
traegt im dunklen die Markenfarbe. Umgesetzt ueber wordmarkClassName an den beiden
Aufrufstellen, nicht in der Komponente selbst: das gelbe Anmelde-Panel ist
themeunabhaengig hell und braucht dort weiterhin dunkle Schrift.

Im Browser in beide Richtungen gemessen: hell oklch(0.15 0 0), dunkel
oklch(0.91 0.19 102) — letzteres ist exakt --primary.

Ausserdem in STATE.md festgehalten, dass Tessera zunaechst nur intern eingesetzt wird
und die Abnahme der Mandantentrennung (Plan 02-05, Erfolgskriterium 4) deshalb
bewusst zurueckgestellt ist — nicht zurueckgebaut, und vor einem Einsatz bei externen
Kunden nachzuholen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 14:00:12 +02:00
schalli 594d6099dd docs(quick-260907-ipz): Abnahme dokumentiert, Sprachmaengel abgeschlossen
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 48s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m29s
Beide Korrekturen im Browser gegengeprueft. Der aussagekraeftigste Fall ist die
Gruppe "Alle Benutzer" mit zwei Mitgliedern und einer Freigabe: der Dialog schreibt
"2 Mitglieder und 1 Modul-Freigabe" — Mehrzahl und Einzahl im selben Satz, jede Zahl
einzeln richtig. Bei "Vertrieb Innendienst" mit je einem heisst es durchgaengig
Einzahl.

Die vier Du-Stellen sind ebenfalls im Browser bestaetigt: Aktivierungsdialog,
Sperrseite und Marktplatz-Hinweis siezen jetzt. Der Leerzustand der Gruppenliste ist
live nicht erreichbar, weil Gruppen existieren — er bleibt durch den Komponententest
abgedeckt.

Der UAT-Bericht zu Phase 15, in dem beide Maengel urspruenglich notiert wurden, ist
entsprechend nachgezogen. Dort war nur der Aktivierungsdialog als Du-Stelle vermerkt;
bei der Korrektur kamen drei weitere zum Vorschein.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:45:37 +02:00
schalli a21dd98924 docs(260907-ipz): Zusammenfassung fuer Anrede- und Einzahl-Korrektur
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:41:11 +02:00
schalli 3746754a5e feat(260907-ipz): ICU-Plural fuer Mitglieder- und Freigabenzahl im Loeschdialog
Beide Zahlen im Loeschdialog-Text wurden bislang in fest formulierte
Mehrzahl-Woerter eingesetzt, weshalb "1 Mitglieder und
1 Modul-Freigaben" entstand (live beobachtet an der Gruppe "Vertrieb
Innendienst"). admin.groups.deleteConfirm.body wird in de.json und
en.json auf ICU-Plural umgestellt (memberCount und grantCount je mit
eigenem plural-Argument), im selben Stil wie das bestehende
admin.ldap.…resultSummary. Die Mock-Nachricht in groups-page.test.tsx
wird auf denselben Wortlaut nachgezogen, wodurch der zuvor rote
Einzahl-Test jetzt gruen ist; die beiden bestehenden Mehrzahl-Tests
bleiben unveraendert gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:39:38 +02:00
schalli dee52a2b00 test(260907-ipz): failing test fuer Einzahl im Loeschdialog
Der Loeschdialog schreibt bei genau einem Mitglied und einer Freigabe
faelschlich "1 Mitglieder und 1 Modul-Freigaben". Der Mock-Uebersetzer
in groups-page.test.tsx wird um eine ICU-Plural-Aufloesung erweitert
(nach dem Vorbild der bestehenden select-Aufloesung in
grants-matrix.test.tsx), und ein neuer Test mit
{ memberCount: 1, grantCount: 1 } nagelt den korrekten Einzahl-Text
fest. Der Test ist an dieser Stelle rot, da der Uebersetzungstext noch
nicht auf ICU-Plural umgestellt ist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:39:09 +02:00
schalli b70161717c fix(260907-ipz): Anrede in vier deutschen Texten auf Siezen umstellen
Die Oberflaeche siezt ueberall, ausser an vier Stellen: leere
Gruppenliste, Modul-Aktivierungsdialog, Modul-Sperrseite und
Marktplatz-Hinweis duzten bislang. Alle vier Werte in de.json werden
auf die Sie-Form umgestellt, en.json bleibt unveraendert, da Englisch
die Unterscheidung nicht kennt. Die Mock-Uebersetzer und Zusicherungen
in den drei betroffenen Testdateien werden auf den neuen Wortlaut
nachgezogen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:37:12 +02:00
schalli 04827a489d docs(quick-260907-ipz): Plan fuer einheitliche Anrede und richtige Einzahl
Zwei Wortlaut-Maengel, beide am Code verifiziert: vier deutsche Texte duzen
statt zu siezen, und der Gruppen-Loeschdialog setzt zwei Zahlen in fest
formulierte Mehrzahl-Woerter ("1 Mitglieder").

Beim Planen nachgeprueft: der Mock-Uebersetzer in groups-page.test.tsx kann
kein ICU-Plural — die Umstellung wuerde die beiden bestehenden
Loeschdialog-Tests brechen. Der Plan loest das ueber dieselbe Regex-Technik,
mit der grants-matrix.test.tsx bereits ICU-select aufloest, und nagelt den
Einzahl-Fall mit einem neuen Test fest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:35:58 +02:00
schalli bf7f250c81 docs(quick-260907-hyi): Abnahme dokumentiert, Umlaut-Korrektur abgeschlossen
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 49s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m51s
Die Browser-Abnahme hat drei uebersehene Stellen gefunden — "Aenderungen speichern",
"Oeffnen" und einen LDAP-Hinweis — und damit die Luecke im Waechter aufgedeckt, durch
die sie geschluepft sind: sein Verdachtsmuster war case-sensitiv. Beides mit 85d2d77
behoben.

In der SUMMARY festgehalten, wie geprueft wurde: eine unabhaengige Analyse aller
Tokens in de.json findet keine Ersatzschreibung mehr, der Schluesselsatz ist
unveraendert bei 787, und die Modulbeschreibung in der bestehenden Datenbank ist beim
API-Neustart per Seed-Upsert mitgewandert — ohne Migration, wie geplant.

Ausserdem vermerkt, dass eine erste Sichtpruefung per fetch aus der laufenden Seite
faelschlich Entwarnung gab. Dieselbe Methode hatte in dieser Sitzung schon einmal ein
falsches Ergebnis geliefert; die berichteten Zahlen stammen aus echten
Seitenaufrufen.

Backlog-Punkt zu den Umlauten nach completed verschoben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:20:36 +02:00
schalli 85d2d772b6 fix(i18n): drei uebersehene Umlaute und die Luecke, durch die sie schluepften
Die Gegenprobe im Browser hat drei Stellen gefunden, die der erste Durchgang nicht
erwischt hat — darunter zwei gut sichtbare Schaltflaechen:

  Aenderungen speichern  ->  Änderungen speichern
  Oeffnen                ->  Öffnen
  Eine Aenderung ...     ->  Eine Änderung ...

Die Ursache ist dieselbe fuer alle drei und steckte im Waechter selbst: sein
Verdachtsmuster /(ae|oe|ue|ss)/ war case-sensitiv. "Aenderungen" beginnt mit "Ae",
nicht mit "ae", und ist deshalb durchgerutscht — der Waechter konnte gar nicht
anschlagen. Muster jetzt case-insensitiv; damit erfasst es auch die
grossgeschriebenen Formen.

Durch die schaerfere Pruefung melden sich neu die Abkuerzungen RSS, RSSGenerator und
SSL. Sie tragen ein doppeltes S ohne Umlaut-Bezug und stehen jetzt auf der
Positivliste.

Ausserdem zwei Meldungen des LDAP-Abgleichs korrigiert, die dem Administrator in der
Oberflaeche angezeigt werden (result.errors landet in der Fehlerliste der
LDAP-Seite): "ungueltiger ldapObjectGuid-Wert" und "Base-DN-Konfiguration pruefen.
Nicht geloescht." Drei Tests pinnen diese Texte bewusst und wurden mitgezogen.

Bewusst NICHT angefasst: die Warnung in crypto.service.ts. Sie geht ueber
logger.warn ins Protokoll und nicht an einen Nutzer.

Unabhaengig gegengeprueft: von allen Tokens in de.json, die ae/oe/ue tragen, ist
keines mehr eine Ersatzschreibung. 642 API-Tests und 225 Web-Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:16:57 +02:00
schalli cb6ba7188a docs(quick-260907-hyi): SUMMARY fuer Tasks 1-3, Task 4 (Browser-Abnahme) offen
Dokumentiert die drei ausgefuehrten Tasks (Woerterbuch + de.json,
Vitest-Waechter, API-Texte), die zwei waehrend Task 2 selbst
entdeckten und behobenen Woerterbuch-Luecken sowie die realen
Test-/Build-Ergebnisse fuer beide Apps. Task 4 (Sichtpruefung nach
Docker-Rebuild) bleibt dem Orchestrator ueberlassen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:08:53 +02:00
schalli d9f1422d6f fix(api): Umlaute in Passwort-Mail und Modulbeschreibung
Korrigiert die deutschen Zeichenketten in beiden Mails aus
mail.service.ts (Betreff, Passwort-Reset-Fliesstext,
Willkommens-Mail) sowie die domaincheck-Modulbeschreibung in
domaincheck.seed.ts auf echte Umlaute. Wortlaut identisch, nur
Zeichen korrigiert; die englischen Zweige bleiben unveraendert.

seedModule() ist ein upsert mit update-Zweig und laeuft in
onModuleInit — ein API-Neustart schreibt die korrigierte
Beschreibung ueber bestehende Datenbankzeilen, ohne Migration oder
Backfill.

pnpm --filter @tessera/api run build: sauber.
pnpm --filter @tessera/api run type-check: sauber.
pnpm --filter @tessera/api run test: 46 Testdateien, 642 Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:07:38 +02:00
schalli caa4827360 test(i18n): Waechter gegen neue Umlaut-Ersatzschreibweisen
Legt umlaut-guard.spec.ts an, das ausschliesslich das geparste JSON
von de.json/en.json liest (nie repo-weit ueber Quelldateien greift,
sonst schluege es am Woerterbuch selbst an) und drei Dinge prueft:
kein Token aus UMLAUT_REPLACEMENTS mehr in de.json, jedes verbliebene
ae/oe/ue/ss-Token steht auf UMLAUT_ALLOWLIST, und de.json/en.json
haben denselben Schluesselsatz. Manuell mit einem probeweise
eingefuegten "fuer" verifiziert: Meldung nennt "für" und den
Schluesselpfad, danach zurueckgenommen.

Beim Aufbau des Waechters kamen zwei Luecken der Task-1-Wortliste ans
Licht: "Bestaetigen" (Grossschreibung, common.confirm) fehlte in
UMLAUT_REPLACEMENTS und blieb faelschlich falsch geschrieben; die
Ersetzungen zu Passwoerter/vertrauenswuerdigen/ausschliessen/
entschluesselt ergeben nach der Korrektur korrektes Deutsch, das aber
weiterhin ae/oe/ue/ss enthaelt und deshalb auf UMLAUT_ALLOWLIST
ergaenzt werden musste. Beide Luecken behoben (Rule 1 - Bug).

pnpm --filter @tessera/web test: 38 Testdateien, 225 Tests gruen.
pnpm --filter @tessera/web run type-check: sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:06:25 +02:00
schalli 3d351c6a99 fix(i18n): Umlaute in de.json korrigieren
Die deutsche Oberflaeche schrieb Umlaute als Buchstabenpaare aus
("fuer" statt "für", "Loeschen" statt "Löschen"). Legt
apps/web/src/messages/umlaut-dictionary.ts als einzige Wahrheitsquelle
fuer eine explizite Token-Ersetzungsliste sowie eine Positivliste
bereits korrekter Woerter (Passwort, Quelle, manuell, neue, ...) an
und korrigiert damit 67 falsche Tokens (140 Fundstellen) in de.json.
Ersetzt wird ausschliesslich per Ganzwort-Treffer, nie per
Zeichenregel, damit Woerter wie "Quelle" oder "manuell" unangetastet
bleiben. Der Platzhalterwert "empfaenger@example.com" bleibt
unveraendert, da er eine Beispiel-E-Mail-Adresse ist. Schluessel und
Reihenfolge in de.json sind unveraendert, de.json und en.json tragen
weiterhin denselben Satz von 787 Schluesseln.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:03:42 +02:00
schalli bec0530a2b docs(quick-260907-fgv): Abnahme dokumentiert, Quick-Task abgeschlossen
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 50s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m51s
Task 3 (Browser-Abnahme) ist durchgefuehrt. Von den beiden zur Freigabe gestellten
Gestaltungsfragen wurde eine zurueckgewiesen und eine blieb offen:

Die Anordnung auf dem Anmelde-Panel hat der User weder als "Marke oben" noch als
"Marke unten" abgenommen, sondern eine dritte Loesung verlangt — Logo links, Text
rechts daneben, wie in der gelieferten Schriftzug-Datei. Nachgezogen mit 11c0ec6.

Zur Schriftzugfarbe hat er sich nicht geaeussert; sie bleibt vorerst dunkel, die
Rueckkehr zu Gelb wurde als jederzeit moeglich angeboten.

Ausserdem in der SUMMARY festgehalten, dass die Abnahme einen Defekt ausserhalb dieses
Plans aufgedeckt hat: die Tailwind-Dunkelvariante war nie an die .dark-Klasse gebunden
(WINDOWS #13, behoben mit f8d7da0). Der Logo-Einbau hat ihn nur sichtbar gemacht,
verursacht hat er ihn nicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 12:50:43 +02:00
schalli b8bd4456df docs: zwei Punkte aus dem Logo-Einbau festgehalten
Beim Einbau des Logos im Browser aufgefallen und als Backlog-Punkte abgelegt, nicht
mitrepariert:

Mandanten-Branding — der Wunsch des Users, dass ein Administrator das Aussehen spaeter
selbst anpassen kann. Der Logo-Einbau hat dafuer schon vorgearbeitet: die Oberflaeche
zieht die Marke ausschliesslich aus einer zentralen Komponente, ein spaeterer Austausch
setzt an genau einer Stelle an. Was noch fehlt (Ablage, Mandantenfeld, Rueckfall,
Verwaltungsseite, Zusammenspiel mit hell/dunkel) steht im Punkt.

Umlaute — die deutschen Oberflaechentexte schreiben Umlaute in 29 Zeilen als
Buchstabenpaare aus: "fuer", "Loeschen", "Zurueck zum Dashboard", "Verfuegbar". Das
sieht jeder Nutzer auf jeder Seite. Mit Hinweis auf die Stolpersteine beim Ersetzen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:41:59 +02:00
schalli 11c0ec63cb feat(quick-260907-fgv): Anmeldeseite zeigt Logo links, Text rechts daneben
Auf Wunsch des Users stehen Bildmarke und Text auf dem gelben Anmelde-Panel jetzt
nebeneinander statt untereinander — dieselbe Anordnung wie in der gelieferten Datei
tessera-logo-schriftzug.svg: links die Marke, rechts der Name und darunter der
Untertitel.

Dafuer nimmt die horizontale Variante der Marken-Komponente einen optionalen
Untertitel entgegen. Die gestapelte Variante entfaellt — sie war nach dieser
Umstellung nirgends mehr im Einsatz, alle uebrigen fuenf Stellen standen schon vorher
nebeneinander.

Der Test auf die entfallene Variante wurde nicht geloescht, sondern durch zwei Tests
auf das neue Verhalten ersetzt: einer prueft, dass ohne Angabe kein Untertitel
erscheint, der andere die tatsaechliche Reihenfolge im Dokument (Marke vor Name, Name
vor Untertitel) statt nur deren Vorhandensein. Die Erwartungstexte sind von Hand
geschrieben und nicht aus derselben Quelle gebildet, aus der die Komponente sie zieht.

222/222 Web-Tests gruen, Typpruefung sauber, im Browser gegengeprueft.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:41:07 +02:00
schalli f8d7da073b fix(web): Dunkelvariante an die .dark-Klasse binden — 100+ dark:-Angaben waren wirkungslos
Beim Einbau des Logos fiel auf, dass dessen Plattenkontur dark:stroke-white/25 im
dunklen Modus nicht griff. Die Ursache reicht weit ueber das Logo hinaus.

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, also an der Einstellung des Betriebssystems. Die Anwendung
schaltet den Modus jedoch ueber next-themes mit attribute=class. Beides lief damit
auseinander: sobald ein Nutzer im Portal auf dunkel stellte, waehrend sein System hell
stand, blieb JEDE dark:-Utility im Quellcode wirkungslos — ueber 100 Vorkommen in mehr
als zehn Dateien, darunter Status-, Warn- und Fehlerfarben, Hinweisboxen und Badges in
der Administration.

Unentdeckt geblieben ist es, weil Hintergrund und Textfarbe NICHT ueber dark: laufen,
sondern ueber die CSS-Variablen unter .dark. Die Oberflaeche wurde also grundsaetzlich
dunkel und nur die Feinheiten fehlten.

Am laufenden System in beide Richtungen gemessen. Vorher, bei html.dark und hellem
System: dark:bg-gray-800 ergab transparent, dark:text-green-400 blieb ohne Wirkung.
Nachher greifen beide korrekt, und im hellen Modus greifen sie weiterhin nicht — die
Logo-Kontur ist dort unsichtbar, im dunklen Modus weiss mit 25 Prozent Deckkraft.
221/221 Web-Tests gruen, Typpruefung sauber.

Erfasst als WINDOWS.md #13 und mit diesem Commit geschlossen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:35:49 +02:00
schalli 83e8cedcd4 docs(quick-260907-fgv): SUMMARY fuer Tasks 1+2, Task 3 (Browser-Abnahme) offen
Dokumentiert die uebernommenen/ausgelassenen Lieferdateien, die Begruendung
fuer Text- statt Bild-Schriftzug, die Konturloesung der dunklen Platte, die
Zusammenfuehrung des Gelbwerts sowie zwei waehrend Task 2 automatisch
behobene Deviationen. Status halted, weil Task 3
(checkpoint:human-verify) absichtlich nicht ausgefuehrt wurde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:27:35 +02:00
schalli c4cc05a942 feat(quick-260907-fgv-02): sechs Marken-Stellen auf TesseraLogo umgestellt, Gelbwert zusammengefuehrt
- header.tsx, sidebar.tsx (Schublade), login/page.tsx (breit + schmal),
  reset-password/page.tsx, reset-password/[token]/page.tsx ziehen jetzt
  alle TesseraLogo statt eines von Hand gesetzten Schriftzugs
- login/page.tsx: handgezeichneter Vier-Quadrate-Platzhalter entfernt,
  gelbes Panel nutzt BRAND_YELLOW per Inline-Style statt eigener
  Tailwind-Klasse, Kopfkommentar ohne Farbwert neu formuliert
- account-settings-form.tsx: DEFAULT_ACCENT liest jetzt BRAND_YELLOW statt
  eines eigenen Literals
- tessera-logo.tsx: Schriftzug setzt keine eigene Textfarbe mehr, sondern
  erbt sie vom umgebenden Container (Rule 1 -- ohne diese Korrektur waere
  der Schriftzug auf dem immer gelben Anmelde-Panel im dunklen
  Erscheinungsbild unlesbar hell auf hell gewesen)
- sidebar.tsx: nicht mehr benoetigtes tCommon entfernt; sidebar.test.tsx
  um common.brand.logoAlt in der Uebersetzungs-Attrappe ergaenzt
- vollstaendige Web-Testsuite (221 Tests) und Typpruefung gruen

Docker-Neubau und Browser-Abnahme (curl-Health-Check gegen den lokalen
Container) bewusst ausgelassen -- der Orchestrator uebernimmt beides in
Task 3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:25:14 +02:00
schalli 49d3df4485 feat(quick-260907-fgv-01): Markenbausteine — Icons, Farbkonstanten, TesseraLogo-Komponente
- favicon.svg und apple-touch-icon.png byteidentisch nach src/app/ kopiert
  (dateibasierte Next.js-Metadaten fuer den Browser-Tab)
- brand.ts: einzige Stelle im Web-Quellcode fuer die drei Markenfarben
  (BRAND_YELLOW, BRAND_OLIVE, BRAND_PLATE), aus favicon.svg uebernommen
- TesseraLogo-Komponente mit den Varianten mark/horizontal/stacked, als
  JSX-SVG von Hand gesetzt (kein dangerouslySetInnerHTML), Kontur-Klasse
  fuer die dunkle Platte per plateOutline-Eigenschaft schaltbar
- common.brand.logoAlt in de.json und en.json ergaenzt, Schluesselparitaet
  per Test abgesichert

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:21:40 +02:00
schalli 17a8355f27 docs(quick-260907-fgv): Plan fuer festen Einbau von Tessera-Logo und Favicon
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:19:16 +02:00
schalli 2b4e58799e docs: AD- und Exchange-Pruefungen fuer den Live-Test vorgemerkt
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 50s
Tessera CI/CD / Build & Publish Images (push) Successful in 8s
Entscheidung des Users vom 2026-09-07: die drei verbleibenden Ledger-Punkte werden
spaeter am Live-System geprueft, nicht in der Entwicklungsumgebung. Sie brauchen ein
erreichbares Active Directory (#4, #6) beziehungsweise ein echtes Exchange-Postfach
(#12) und sind hier nicht ersetzbar.

Als Deferred Items in STATE.md eingetragen, mit dem jeweiligen Pruefauftrag im
Klartext, damit beim naechsten Aufsetzen niemand neu recherchieren muss, was genau
zu tun ist.

Im Ledger bleiben sie ausdruecklich `open` und wurden NICHT auf `waived` gesetzt —
sie sollen nachgeholt werden, nur eben dort, wo die Fremdsysteme erreichbar sind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:01:01 +02:00
schalli dba261de38 docs(13): Dedup-Fix dokumentiert und am laufenden System gegengeprueft
Tessera CI/CD / Lint & Type Check (push) Successful in 49s
Tessera CI/CD / Tests (push) Successful in 48s
Tessera CI/CD / Build & Publish Images (push) Successful in 4m1s
Die Fingerabdruck-Stufe legte vor dem Fix bei 16.255 Zeilen null Duplikate
zusammen — nicht zu wenige, sondern gar keine. Nach der Umstellung auf die
quellstabilen Felder sind es 26 Gruppen mit 52 Zeilen, davon 2 durch das neue
Wert-Veto getrennt. 374/374 API-Tests unabhaengig nachgelaufen.

Die automatische Nachrechnung wurde scharf geprueft statt nur beobachtet: 500
Zeilen wurden kuenstlich auf ein altes Fingerabdruck-Format zurueckgesetzt, dann
die API neu gebaut und gestartet. Der Start um 08:46:25 meldete "500 Zeile(n) auf
die neue Formel gebracht" und hinterliess null veraltete Zeilen; der Neustart um
08:46:49 erzeugte keine Meldung mehr. Damit ist beides belegt — dass sie repariert
und dass sie nicht bei jedem Start erneut laeuft.

Der Bericht haelt die drei bewusst getragenen Grenzen fest (zweites Los gleicher
Groessenordnung wird falsch zusammengelegt; RSS/E-Mail matchen nur ueber den Titel;
der eine bereits messbare Fall LSA242) und begruendet, warum ein Riegel dagegen
verworfen wurde: er wuerde RSS dauerhaft von der Dublettenerkennung ausschliessen.
Ausserdem festgehalten, dass die Stufe nur beim Einlesen wirkt — der Altbestand
wird nicht rueckwirkend zusammengefuehrt.

Phase 13 steht damit auf Complete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:48:29 +02:00
schalli 389a1fd717 docs(260907-efh): complete Cross-Source-Dedup-Fingerabdruck-Fix
WINDOWS.md #11 als fixed markiert — Fingerprint-Stufe hasht nur noch
[buyerName, title, deadlineAt], Wert kehrt als exakter Widerspruchs-Veto
zurueck, 16.255 Bestandszeilen wurden gegen die lokale DB nachgerechnet
(0 -> 26 Duplikat-Gruppen, davon 2 durch den Veto geschuetzt).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:44:39 +02:00
schalli ee3b28e505 feat(260907-efh): automatische Fingerabdruck-Nachrechnung beim Start
Nach der Formelaenderung (v1 -> v2) traegt jede Bestandszeile einen Hash
der alten Formel und wuerde nie wieder auf einen neuen treffen. Die
Produktionsdatenbank wird von Hand nicht angefasst, deshalb rechnet ein
neuer Dienst beim Start alle veralteten Zeilen automatisch nach — an der
Versionsmarke aus tender-fingerprint.ts erkannt, gleiche Bauform wie die
LDAP-Bind-Passwort-Nachverschluesselung.

- TenderFingerprintBackfillService: OnApplicationBootstrap, seitenweise
  (500 Zeilen), pro Seite eine Transaktion, Fehler werden geloggt und
  geschluckt statt geworfen
- In tenders.module.ts VOR TenderSchedulerService eingetragen, damit die
  Nachrechnung vor der Cron-Registrierung laeuft
- Vier Tests: leere DB, alter+leerer Hash werden beide erfasst, zweiter
  Lauf schreibt nichts mehr, Datenbankfehler wirft den Haken nicht

Gemessen an der lokalen Datenbank (16.255 Zeilen): Fingerabdruck-Gruppen
mit mehr als einer Zeile vorher=0, nachher=26, veraltete Zeilen
danach=0, davon Gruppen mit widersprechenden Wert-Groessenordnungen=2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:41:58 +02:00
schalli 0645cac618 docs(15): WINDOWS #10 geschlossen — Browser-Gegenprobe nach dem Fix bestanden
Der am Vormittag gefundene Zugriffs-Guard-Defekt ist behoben (260907-e8k) und im
echten Browser gegen ein frisch gebautes Web-Image nachgeprueft.

nutzer2 ohne Freigabe bekommt jetzt auf /modules/tender-radar, dessen Unterseiten
my-sources und settings sowie auf /modules/dkv-fleet die 403-Seite als
Serverantwort. Auf /modules/cert-manager, wo er eine Freigabe hat, kommt er
weiterhin durch — das ist die Gegenprobe dagegen, dass die Sperre zu weit greift.
admin sieht alle vier Adressen unveraendert, nutzer1 sieht Meine Quellen mit allen
drei Abschnitten; die Phase-17-Funktion ist unbeschaedigt.

Im Bericht zusaetzlich festgehalten, dass ein erster Messversuch per fetch() aus der
laufenden Seite heraus faelschlich alle Seiten als gesperrt meldete, auch fuer den
Administrator. Der Aufruf fuehrt die Sitzung nicht wie eine echte Navigation mit.
Alle berichteten Zahlen stammen aus echten Seitenaufrufen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:40:48 +02:00
schalli 9e5445e6b7 fix(260907-efh): Wert-Veto in Stufe 3 und Fingerabdruck bei Update mitschreiben
Zwei Schaeden aus derselben Wurzel behoben (WINDOWS.md #11, beim Messen
gefunden): der Aktualisierungszweig aendert Titel/Vergabestelle/Frist
einer bestehenden Zeile, schrieb den Fingerabdruck dabei aber nie mit —
der gespeicherte Wert driftete vom Inhalt der Zeile weg und Stufe 3 fand
die Zeile nie wieder (an der Live-DB an drei Gruppen gemessen).

- fingerprint wird einmal in resolve() berechnet und sowohl im
  Anlegezweig als auch im Aktualisierungszweig geschrieben
- Neue Stufe-3-Pruefung: valueBucketsContradict() als Veto auf einen
  Fingerabdruck-Treffer, exakter Groessenordnungsvergleich, keine
  Aehnlichkeitssuche
- toNumberOrNull() haendelt Prisma Decimal und den einfachen
  Zahlenwert der Test-Fakes gleichermassen
- Vier neue Tests fuer die Wert-Faelle, bestehender Update-Test um die
  Fingerabdruck-Erwartung erweitert, alle vorherigen Tests bleiben gruen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:38:37 +02:00
schalli cb23e3bc78 fix(260907-efh): Fingerabdruck auf Vergabestelle/Titel/Frist verengen
Die dritte Dedup-Stufe hashte bisher fuenf Segmente, darunter CPV-Division
und geschaetzten Wert — beide werden von Nicht-DOE-Quellen systematisch
nie geliefert, wodurch dieselbe Ausschreibung aus DOE und aus einem
Scraper nie denselben Fingerabdruck ergab (WINDOWS.md #11).

- tenderFingerprint() nimmt nur noch buyerName, title, deadlineAt entgegen
- Versionsmarke FINGERPRINT_VERSION_PREFIX ("v2:") vor dem Hash, damit
  Task 3 veraltete Zeilen erkennen kann
- valueBucketsContradict() als eigene, exportierte Funktion fuer den
  Wert-Veto in Task 2 — kein Hash-Bestandteil mehr
- backfill-tender-source.ts an die neue Signatur angepasst
- Testsuite auf das neue Verhalten umgeschrieben inkl. Goldwert-Test

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:36:21 +02:00
schalli 4502664fb2 docs(quick-260907-efh): Plan fuer Cross-Source-Dedup-Fix (WINDOWS #11)
Fingerprint hasht kuenftig nur noch Vergabestelle/Titel/Frist, der Wert
kehrt als Widerspruchspruefung am Treffer zurueck, Versionsmarke plus
Bootstrap-Nachrechnung stellt Bestand und frische Installation gleich.

Beim Erden gemessen und mit aufgenommen: der Aktualisierungszweig in
TenderDedupService schreibt den Fingerabdruck nicht mit, wodurch drei
Gruppen echter Duplikate schon heute allein durch Drift auseinanderstehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:34:18 +02:00
schalli 5504931af3 docs(quick-260907-e8k): complete Zugriffs-Guard-Fix fuer modul-eigene Routen
Beide Tasks des Plans ausgefuehrt, verifiziert und committed
(4a23e13, 69b6418). Automatisierte Verifikation vollstaendig gruen:
Gate-Tests, Layout-Tests, volle Web-Suite (213/213), Typpruefung und
Produktionsbau. Browser-Gegenprobe (A/B/C) bleibt laut Plan Aufgabe
des Orchestrators nach Docker-Rebuild — WINDOWS #10 bleibt bis dahin
offen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:29:19 +02:00
schalli 69b641861c feat(quick-260907-e8k-02): Layout-Gate vor jedes Modulverzeichnis, generische Route umgestellt
Legt je ein layout.tsx fuer cert-manager, dkv-fleet, domaincheck und
tender-radar an, das ModuleAccessGate mit dem fest eingetragenen
Verzeichnis-Slug umschliesst. Ein Layout im App Router deckt alle
verschachtelten Unterrouten automatisch mit ab — my-sources und
settings unter tender-radar sowie settings und vehicles unter
dkv-fleet schliessen sich ohne eigene Datei (WINDOWS #10, PERM-04).

Die generische Route [category]/[moduleSlug]/page.tsx nutzt jetzt
ebenfalls ModuleAccessGate statt des bisherigen Inline-403-Markups —
das 403-Markup existiert damit nur noch einmal im Code
(module-access-denied.tsx).

module-layouts.test.tsx deckt zwei Threats ab: falscher Slug in einem
der vier Layouts (T-e8k-03) und ein kuenftig hinzugefuegtes
Modulverzeichnis ohne Layout (T-e8k-04, liest das Verzeichnis per
node:fs aus). module-access.test.tsx ist auf die Weitergabe an das
Gate umgeschrieben, die 403-vs-Rendern-Entscheidung ist bereits durch
module-access-gate.test.tsx abgedeckt.

Volle Web-Testsuite (213 Tests), Typpruefung und Produktionsbau sind
gruen. Browser-Gegenprobe folgt durch den Orchestrator.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:27:47 +02:00
schalli 4a23e13731 feat(quick-260907-e8k-01): gemeinsame 403-Komponente und ModuleAccessGate
Zieht das bisher inline in [category]/[moduleSlug]/page.tsx stehende
403-Markup in ModuleAccessDenied (uebersetzungsfrei, nimmt fertige Texte
als Props) und legt mit ModuleAccessGate eine wiederverwendbare
Server-Component-Pruefung an, die checkModuleAccess aufruft und bei
jeder Ausnahme ebenfalls als "kein Zugriff" wertet (zweite
Verteidigungslinie ueber das bereits geschlossen ausfallende
checkModuleAccess, T-15-29). Vier Testfaelle decken Durchlassen,
Verweigern, Ausnahme und Slug-Weitergabe ab.

Bereitet Task 2 vor: die vier Modul-Layouts und die generische Route
werden auf dieses Gate umgestellt (WINDOWS #10, PERM-04).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:24:30 +02:00
schalli 74a30fb8ef docs(quick-260907-e8k): plan module access gate for module-owned routes
WINDOWS #10: der Zugriffs-Guard sitzt nur in der generischen Route
modules/[category]/[moduleSlug]/page.tsx. Die vier fest verdrahteten
Modulverzeichnisse laufen daran vorbei.

Plan: gemeinsame 403-Komponente + ModuleAccessGate (Server Component),
je ein layout.tsx pro Modulverzeichnis (deckt Unterrouten mit ab),
generische Route auf dieselben Bausteine umgestellt. Zwei Tasks,
Browser-Gegenprobe als human-check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:22:44 +02:00
schalli 5aca441bb3 docs: Roadmap-Status ehrlich gemacht, zwei unsichtbare Befunde ins Ledger
Die Phasen 11 bis 14 standen in der Roadmap-Tabelle auf "In Progress", obwohl an
keiner davon noch gearbeitet wird. Der Status wurde gegen die Verifikationsberichte
gehalten und dabei zeigte sich, dass es kein reiner Buchhaltungsrueckstand war:

11 und 12 stehen auf passed und sind jetzt Complete.

13 traegt einen offenen Befund. tenderFingerprint() haengt buyerName, title,
cpvDivisionKey, deadlineKey und valueBucket zu einem String und hasht ihn. Die
Scraper liefern cpvDivisions immer leer, DOE meist gefuellt — dieselbe
Ausschreibung ergibt aus beiden Quellen verschiedene Fingerabdruecke. Die
Fingerprint-Stufe greift also gerade dort nicht, wo sie greifen soll. Das stand
seit dem 2026-07-23 in 13-VERIFICATION.md, aber nicht im Ledger und war damit
praktisch unsichtbar. Jetzt WINDOWS.md #11.

14 wartet auf den Live-Test gegen ein echtes Exchange-Postfach; der
NTLM/SOAP-Weg ist nur gegen Mocks geprueft. Vom User am 2026-07-24 bewusst
zurueckgestellt, stand ebenfalls nur im Verifikationsbericht. Jetzt WINDOWS.md #12.

Beide Phasen tragen den wahren Status statt "In Progress", mit Verweis auf den
jeweiligen Ledger-Eintrag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:09:28 +02:00
schalli 2c4558164e test(15,16): alte Browser-Gegenproben nachgeholt — ein Defekt gefunden
Die sechs seit Anfang August offenen Browser-Durchlaeufe aus Phase 15 und 16
(WINDOWS.md #1-#6) waren liegengeblieben, weil in den damaligen Sitzungen kein
Browser-Tool verfuegbar war. Der Stack lief fuer die Phase-17-Gegenproben ohnehin,
also wurden alle nachgeholt, die ohne echtes Active Directory pruefbar sind.

Geschlossen:

#1 (15-06) Gruppenverwaltung: anlegen, umbenennen, Standardmarkierung exklusiv
setzen und zuruecknehmen, Mitglied hinzufuegen und entfernen, Loeschdialog nennt
beide Zahlen konkret. Bei gestoppter API meldet der Loeschvorgang deutschen
Klartext und laesst die Gruppe stehen. Die AD-Bindung ist an dieser Stelle
gegenstandslos geworden — Phase 16 hat sie in den LDAP-Bereich verlagert.

#2 (15-07) Freigaben-Matrix: setzen und entziehen, Aktivierungsdialog auf beiden
Wegen (Sofort-Freigabe legt den Grant an, "Spaeter konfigurieren" nicht),
Direkt-Grant neben Gruppen-Grant mit sichtbarer Gruppenspalte, langer Gruppenname
bleibt in seinen Massen. Bei gestoppter API springt die Checkbox zurueck und die
Datenbank bleibt unveraendert — kein optimistischer Zustand ueberlebt.

#5 (16-04) Gruppen-Dialog in allen drei Zustaenden, inklusive importierter Gruppe
mit gesperrtem AD-Namen und freiem internen Namen; die Liste zeigt danach den
internen Namen und traegt den AD-Namen im Tooltip. Die AD-Bindung wurde dafuer in
der Datenbank gesetzt, nicht importiert — im Bericht ausdruecklich vermerkt.

#3 (15-08) wurde durchgefuehrt und hat einen Defekt aufgedeckt:

WINDOWS #10 (neu, offen): PERM-04 greift nicht auf den modul-eigenen Routen. Der
Zugriffs-Guard sitzt allein in modules/[category]/[moduleSlug]/page.tsx. Die vier
fest verdrahteten Modulrouten (tender-radar, dkv-fleet, cert-manager, domaincheck)
samt Unterseiten laufen daran vorbei. Ein USER ohne Freigabe bekommt unter
/modules/procurement/tender-radar korrekt die 403-Seite, unter /modules/tender-radar,
/modules/tender-radar/my-sources und /modules/dkv-fleet dagegen die volle Seite mit
bedienbaren Knoepfen. Keine Datenpreisgabe — die API antwortet durchgehend 403 —
aber der Nutzer sieht Bedienelemente, die er nicht benutzen darf, und rohe
englische Techniktexte statt einer verstaendlichen Meldung. Sidebar, Marketplace
und API verhalten sich dagegen korrekt.

Offen bleiben #4 und #6: beide messen den Sync-Vorgang selbst und brauchen ein
erreichbares Active Directory.

Berichte mit Screenshots unter 15-UAT-2026-09-07.md und 16-UAT-2026-09-07.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:08:04 +02:00
schalli 72d0ba7e48 test(17): Browser-Gegenproben #7/#8/#9 nachgeholt — Phase 17 auf passed
Die drei human-check-Punkte aus Phase 17 waren am 2026-08-12 offen geblieben,
weil in der Ausfuehrungssitzung kein Browser-Tool verfuegbar war. Sie wurden
jetzt gegen frisch gebaute Images aus main (79015dd) mit echtem Chrome und
leerer Datenbank durchgeklickt.

#7 (17-01 Task 2): nutzer1 speichert sein Postfach, die Werte ueberleben den
Reload; nutzer2 sieht ein leeres Formular statt der Werte von nutzer1. Danach
zwei TenderEmailConfig-Zeilen mit je eigenem userId.

#8 (17-03 Task 1): Meine Quellen zeigt alle drei Abschnitte, ein eigener
RSS-Feed erscheint sofort mit Entfernen-Knopf, der plattformweite service-bund
steht darunter ohne, und das Digest-Intervall "Woechentlich" ueberlebt den
Reload.

#9 (17-03 Task 2): USER sieht auf /settings keine Bedienelemente, nur Hinweis
und Verweis; SUPER_ADMIN sieht Abrufintervall und plattformweite Feeds, aber
kein Postfach und keine Benachrichtigung mehr. Das Zahnrad fuehrt in beiden
Faellen nach Meine Quellen.

Zusaetzlich am laufenden Server gemessen, weil eine ausgeblendete Schaltflaeche
kein Beweis fuer eine serverseitige Sperre ist: als nutzer2 liefert GET
/rss-feeds nur den plattformweiten Feed, DELETE auf den plattformweiten wie auf
den fremden Feed antwortet 404 ohne die Zeile anzufassen, und POST mit
scope=platform wird mit 403 abgewiesen.

WINDOWS.md #7/#8/#9 auf fixed (open_count 9 -> 6), Verifikationsbericht mit
Nachtrag und Screenshots auf passed, ROADMAP auf Complete, STATE nachgezogen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 09:53:02 +02:00
schalli 79015dd3f4 docs(17): mark phase human_needed in the roadmap
Tessera CI/CD / Lint & Type Check (push) Successful in 46s
Tessera CI/CD / Tests (push) Successful in 47s
Tessera CI/CD / Build & Publish Images (push) Successful in 6s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 15:08:59 +02:00
schalli a46006eada docs(17): verify phase against the codebase — human_needed
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 51s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s
Every mechanism checked against live schema, migrations and code rather
than the summaries. Three browser click-throughs remain unrun (no browser
tool this session), so the phase lands human_needed, not passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 15:08:45 +02:00
schalli 4453e86626 docs(17-03): complete UI-Aufteilung Meine Quellen vs. Administration plan
Tessera CI/CD / Lint & Type Check (push) Successful in 46s
Tessera CI/CD / Tests (push) Successful in 53s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m58s
2026-08-12 12:06:31 +02:00
schalli 809afbce13 test(17-03): Tests fuer beide Seiten, Backlog-Punkt abgeschlossen
- RssFeedListForm.test.tsx auf scope umgestellt: personal zeigt eigene Feeds
  editierbar + plattformweite als schlichte, nicht bedienbare Liste darunter;
  platform zeigt nur plattformweite Feeds, eigene Feeds tauchen dort gar
  nicht auf; beide Faelle pruefen den scope-Parameter von createRssFeed
- settings-roles.test.tsx (neu): USER sieht Hinweis+Verweis statt
  Bedienelemente, ADMIN/SUPER_ADMIN sehen Abrufintervall+RSS-Feeds, unbekannte
  Rolle zeigt weder-noch, entfernte Abschnittsueberschriften kommen nirgends
  mehr vor
- my-sources.test.tsx (neu): alle drei Abschnittsueberschriften vorhanden,
  Verweis auf die Administrationsseite nur bei ADMIN/SUPER_ADMIN
- Erwartete Texte in allen drei Testdateien von Hand geschrieben, nicht aus
  der gleichen next-intl-Zuordnung abgeleitet, die die Komponenten benutzen
- Backlog-Punkt 2026-08-11-tender-radar-einstellungen-mischen-rollen.md nach
  todos/completed/ verschoben, mit Resolution-Abschnitt: beide Nutzer-
  Entscheidungen (eigene Quellen je Nutzer, Trefferliste bleibt
  plattform-global) und die Antwort auf offenen Punkt 4 (eigene
  nutzerseitige Modulseite als Vorbild fuer kuenftige Module) dokumentiert

Verifikation: Web 205/205, API src/tenders 362/362, beide Typpruefungen
fehlerfrei.
2026-08-12 12:02:36 +02:00
schalli 150046e14f feat(17-03): Administrationsseite schrumpft auf das, was Administration ist
- settings/page.tsx zeigt nur noch Abrufintervall + plattformweite Feeds;
  Postfach- und Benachrichtigungsabschnitt entfernt (ziehen auf my-sources um)
- Anzeigepruefung der Rolle aus dem Anmelde-Speicher: unbekannt -> Platzhalter,
  ADMIN/SUPER_ADMIN -> Inhalt, sonst Hinweistext + Verweis auf "Meine Quellen"
  (verbindliche Pruefung bleibt serverseitig, T-17-08/@UseModule, siehe
  Dateikommentar)
- Zahnrad auf der Modulseite fuehrt jetzt nach /my-sources statt /settings;
  neuer Schluessel page.mySourcesTitle, alter page.settingsTitle bleibt als
  Verweistext auf der Nutzerseite (Task 1) in Gebrauch
- settings.emailSectionTitle/emailSectionBody/notificationsSectionTitle aus
  beiden Sprachdateien entfernt (gegengeprueft: nirgends mehr referenziert);
  neue Schluessel settings.accessDeniedText, settings.rssSectionUserNote
2026-08-12 11:58:29 +02:00
schalli 4100bb575e feat(17-03): "Meine Quellen" wird vollstaendig — Postfach, eigene Feeds, Benachrichtigung
- RssFeedSource bekommt isPlatformWide (server-derived), createRssFeed nimmt
  einen scope-Parameter (personal/platform, Vorgabe personal)
- RssFeedListForm bekommt scope-Prop: personal zeigt eigene Feeds editierbar +
  plattformweite als schlichte Aufzaehlung ohne Knoepfe darunter; platform
  zeigt nur plattformweite Feeds editierbar
- DigestIntervalForm aus settings/page.tsx unveraendert herausgeloest (keine
  neuen Beschriftungen, gleiche settings.*-Schluessel)
- my-sources/page.tsx um "Meine Feeds" und "Benachrichtigung" erweitert,
  Verweis auf die Administrationsseite nur fuer ADMIN/SUPER_ADMIN
- settings/page.tsx vorgezogen auf RssFeedListForm scope="platform" (Rule 3,
  eigener Type-Check-Verify sonst rot) — volle Rollenpruesung folgt Task 2

Rule 1: createRssFeed's Antwort traegt kein isPlatformWide (nur GET mappt es
serverseitig) — RssFeedListForm setzt es nach dem Anlegen lokal aus dem
verwendeten scope, sonst wuerde ein frisch angelegter plattformweiter Feed
bis zum naechsten Neuladen aus seiner eigenen Liste verschwinden.
2026-08-12 11:56:42 +02:00
schalli e45d7f2068 docs(17-02): complete RSS-Feed-Besitzer plan 2026-08-12 11:49:07 +02:00