Files
tessera-ctl/.planning/.continue-here.md
T
schalli 3b08d8e0d6
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Successful in 52s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s
docs(quick-260911-nke): Etappe 3b Benutzerdimension abgeschlossen und verifiziert; Handoff fuer 3c/3a
2026-09-11 17:58:48 +02:00

9.5 KiB

context, phase, task, total_tasks, status, last_updated
context phase task total_tasks status last_updated
default mandantentrennung-etappe-3 null 5 paused 2026-09-11T16:30:00.000Z

Wiedereinstieg — Mandantentrennung, Etappe 2 + #27 + 3b ABGESCHLOSSEN, vor 3c und 3a

Critical Anti-Patterns

Alle vier stammen aus tatsaechlichen Fehlschlaegen dieser Sitzung, nicht aus Vorsicht.

Muster Beschreibung Schwere Vermeidung
Auf ein ungeprueftes Fundament bauen forTenant() — der Helfer, auf dem die ganze Mandantentrennung ruht — setzte den Kontext per set_config auf der Transaktionsverbindung, dispatchte die Abfrage aber ueber den aeusseren Client. Gemessen: set_config auf Backend-PID 254999, Abfrage auf 255000, Kontext dort NULL. Die Trennung hat nie funktioniert, auch nicht an den Stellen, die sie scheinbar nutzten. Waere vor dem Scharfschalten nicht geprueft worden, haetten die Abfragen danach NULL Zeilen geliefert und der LDAP-Loeschzweig haette das als "Gruppe im Verzeichnis verschwunden" gedeutet und geloescht. blocking Vor jedem Umbau, der auf einem Helfer aufsetzt, dessen Wirkung EMPIRISCH nachweisen — gegen eine Wegwerf-Datenbank, mit zwei Mandanten und einer echten Abfrage. Nicht den Code lesen und schliessen, dass er stimmt.
Zaehlung ohne Ansehen der Treffer Eine grep-Zaehlung ergab 36 mandantengebundene Stellen. Tatsaechlich waren die meisten Treffer Kommentare, die erklaeren, warum forTenant() dort FEHLT. Echte Aufrufstellen: 6. blocking Bei jeder Zahl, die eine Planung traegt, in die Treffer hineinsehen. Eine Zahl aus grep -c ist eine Behauptung, kein Befund.
Falsche Datei auf dem Server bearbeitet Die Volume-Zeile wurde in /opt/tessera/docker-compose.yml eingetragen — der Server nutzt aber docker-compose.prod.yml, weil die .env COMPOSE_FILE=docker-compose.prod.yml setzt. Die Aenderung waere wirkungslos geblieben und haette wie erledigt ausgesehen. blocking Nach JEDER Aenderung an einer Compose-Datei docker compose config rendern und pruefen, ob die Aenderung im Ergebnis auftaucht. Vorher docker compose ls --format json lesen, um zu sehen, welche Datei ueberhaupt gilt.
Zeichensatz beim Veroeffentlichen angenommen Die Handbuch-Webseite ging mit zerlegten Umlauten live ("Für" statt "Fuer"), weil im lokalen Test der Zeichensatz fehlte und ich annahm, das Veroeffentlichen setze ihn schon richtig. advisory Seiten mit deutschem Text als reines ASCII ausliefern (Sonderzeichen als \uXXXX in den Daten). Dann kann kein Zeichensatz sie falsch auslegen. Die fertige Datei mit all(ord(c)<128 ...) pruefen.

<current_state> Etappe 2 ist am 2026-09-11 abgeschlossen. Alle zwoelf Bereiche sind gebunden und einzeln verifiziert (ldap, groups, tenders, dkv, user, module-registry, dashboard, calendar, tenant, auth, favorites+settings); die drei Datenbankregeln wurden auf Anweisung des Users vorgezogen (260910-jab). Endstand: 994 Tests in 62 Dateien (Ausgang 701/53), 137 Live-Pruefungen (Ausgang 8), 65 Paare / 68 ungebunden / 178 gebunden — jeder ungebundene Zugriff liegt auf einer plattformglobalen Tabelle oder einem benannten Startpfad. Alles gepusht, Arbeitsbaum sauber.

Der Umstellungsschalter ist AUS. DATABASE_URL zeigt weiter auf die Rolle tessera mit BYPASSRLS. Der User hat ausdruecklich verlangt, beim Scharfschalten angehalten und gefragt zu werden.

Die maschinelle Bestandsaufnahme hat eine bekannte Blindstelle (WINDOWS #27): include:/_count: in fremd geschuetzte Tabellen sieht sie nicht. Alle heutigen Instanzen sind einzeln geprueft; der Mechanismus muss VOR Etappe 4 geschlossen werden. </current_state>

<completed_work>

Diese Sitzung, in Reihenfolge:

  1. Aufraeumen (98a8c93) — zwei veraltete Checkpoint-Dateien entfernt, ihr noch gueltiges Wissen (5 Anti-Patterns, Infrastruktur-Stand) nach STATE.md gerettet.
  2. Live-Test AD (a1a8b4f, b6b964b) — WINDOWS #4 und #6 belegt und geschlossen, ohne jede Aenderung am Verzeichnis: Umbenennung und Verschwinden wurden ueber den in Tessera gespeicherten Stand nachgestellt.
  3. Verbindungstest Postfach (c4db3b2, abgenommen) — WINDOWS #16.
  4. Zwei Defekte behoben (e8c2411, 1222951, 2167046) — WINDOWS #14 (Matrix-Suche) und #15 (Sync-Meldungen); dabei den Sicherheitsfund T-Q3-01 mitgeschlossen.
  5. Anleitungen (3501eb4) — vier Handbuecher plus Einstieg unter docs/, gegen den Quelltext geschrieben und unabhaengig gegengeprueft. Zusaetzlich als Webseite veroeffentlicht: https://claude.ai/code/artifact/67372b7f-4d7c-49c7-9642-1c5ef576f245
  6. Dateisicherung (dab72eb) — user-files als benanntes Volume; auf dem Server nachgetragen und am laufenden System belegt, WINDOWS #17 geschlossen.
  7. Versionsangaben (c807049) — CLAUDE.md auf den installierten Stand; sechs nie eingebaute Empfehlungen benannt (u.a. Keycloak, Redis, shadcn/ui).
  8. Mandantentrennung Etappe 1 (bbf1795, de50297, 5f3a39c, da0ac04). </completed_work>

<remaining_work>

Etappe 2 — die eigentliche Umstellung. 31 Einheiten vollstaendig, 9 teilweise. Grundlage: docs/mandantentrennung-zugriffsklassifikation.md, maschinell gegen Abdriften abgesichert durch apps/api/src/prisma/rls-access-inventory.spec.ts. Geschaetzt 5-8 Durchlaeufe, nach Bereichen gebuendelt.

Groessen je Bereich: tenders 62, groups 37, ldap 21, dkv 21, user 17, module-registry 17, dashboard 13, calendar 12, tenant 8, favorites 7, settings 4.

Etappe 3 — Systemkontext fuer Hintergrundlaeufe (ein Cron-Job liest bewusst ueber alle Mandanten, muss aber INNERHALB der Schleife je Mandant binden) plus WINDOWS #19.

Etappe 4 — Scharfschalten mit rls-preflight.mjs davor und dokumentiertem Rueckweg. </remaining_work>

<decisions_made>

  • Anmeldeweg ueber SECURITY-DEFINER-Funktionen, nicht ueber eine Policy und nicht ueber eine zweite Rolle. Eine Policy ist ein Zeilenpraedikat: jede Regel, die eine Suche nach Benutzername erlaubt, erlaubt zwangslaeufig das Lesen der ganzen Tabelle. Die Funktion pinnt die Ausnahme auf feste Spaltenliste, Gleichheit und LIMIT 1.
  • Benanntes Volume statt Bind-Mount fuer user-files — Eigentuemerschaft, nicht Sicherungskomfort, gab den Ausschlag.
  • Am Active Directory wird nichts veraendert (User, 2026-09-09, mit Nachdruck). Pruefungen, die nach einer Verzeichnis-Aenderung aussehen, werden ueber den in Tessera gespeicherten Stand nachgestellt — so wurden #4 und #6 geschlossen.
  • Datenverlust in der Datenbank ist derzeit hinnehmbar (User, 2026-09-09): nichts laeuft produktiv. Erlaubt beim Scharfschalten den direkten Weg statt aufwendiger Absicherung. Gilt nur, solange das so bleibt — vor einem Produktivbetrieb neu bewerten. </decisions_made>

Etappe 4 darf nicht vorgezogen werden. Wird scharf geschaltet, bevor Etappe 2 und 3 durch sind, liefern die noch nicht umgestellten Abfragen null Zeilen statt zu vieler. Der gefaehrlichste Fall ist der Loeschzweig in ldap.service.ts (~Zeile 1559), der Leere als "Gruppe im Verzeichnis verschwunden" deutet und samt Mitgliedschaften und Modulfreigaben loescht.

Keine offenen Handgriffe des Users. Der Server ist auf dem aktuellen Stand.

Required Reading (in order)

  1. .planning/STATE.md — Position, Quick-Task-Tabelle mit allen Ergebnissen dieser Sitzung
  2. docs/mandantentrennung-zugriffsklassifikation.md — die Arbeitsgrundlage fuer Etappe 2
  3. docs/mandantentrennung-datenbankrolle.md — Befund, Sperrgrund, Handgriffe, Rueckweg
  4. .planning/WINDOWS.md — offen sind #18, #19, #20
  5. apps/api/src/prisma/prisma-tenant.extension.ts — der reparierte Helfer

Infrastructure State

  • alpha (192.168.13.12, https://alpha.tessera.ctl.de): auf dem Stand von ea003d4, Container am 2026-09-09 neu erstellt. user-files haengt als Volume tessera_user-files am api-Container, nachgewiesen.
  • Die Serverdatei ist docker-compose.prod.yml, nicht docker-compose.yml — die .env setzt COMPOSE_FILE. Sicherungen liegen als .bak.20260909-0818 daneben.
  • git push geht ausschliesslich ueber localhost:3002; die Push-URL des Remotes ist seit dieser Sitzung dauerhaft darauf gesetzt, ein schlichtes git push genuegt.
  • Lokal: api, db und web laufen; es gibt KEINEN mailhog-Container, deshalb scheitert der Mailversand lokal mit ENOTFOUND mailhog — das ist umgebungsbedingt und kein Defekt.
  • Worktree-Isolation ist abgeschaltet (workflow.use_worktrees=false), weil origin/HEAD in diesem Repo nicht aufloesbar ist und ein isolierter Baum von einem veralteten Stand abzweigen wuerde.

Pre-Execution Critique Required

Bevor Etappe 2 beginnt, ist die Antwort auf diese Frage schriftlich festzuhalten:

Woran wuerde ich merken, dass eine umgestellte Abfrage jetzt zu WENIG liefert statt zu viel? Der Umbau dreht die Fehlerrichtung um. Bis heute war der Fehlerfall "sieht zu viel"; nach der Umstellung ist er "sieht nichts" — und der still gefaehrlichste Ort dafuer ist jeder Code, der Leere als Abwesenheit deutet und daraufhin loescht. Vor der Umstellung eines Bereichs ist zu pruefen, ob er solchen Code enthaelt.

<next_action>

  1. docs/mandantentrennung-etappe3-auftrag.md lesen — 3b ist dort als Erledigt vermerkt.
  2. Etappe 3c (Systemkontext fuer die sechs Hintergrunddienst-Faelle) als /gsd-quick --validate — braucht KEINE Entscheidung des Users.
  3. Etappe 3a (Anmeldenamen pro Mandant) — enthaelt die eine offene Produktfrage.
  4. Etappe 4 — Scharfschalten. NUR nach Rueckfrage beim User.

WINDOWS #27 ist geschlossen (260911-mkj). Frische Sitzung, dann /gsd-resume-work. </next_action>