Files
tessera-ctl/.planning/.continue-here.md
T
schalli 6fb32754d5
Tessera CI/CD / Lint & Type Check (push) Successful in 49s
Tessera CI/CD / Tests (push) Failing after 11m21s
Tessera CI/CD / Build & Publish Images (push) Has been skipped
docs: Arbeitsstand pausiert — Etappe 3b zu, offen 3c/#29/3a/Etappe 4
Handoff neu aufgenommen und gemessen statt erinnert: Arbeitsbaum leer,
main == origin/main, keine Datei juenger als der letzte Commit vom
2026-09-11 — es lag keine angefangene Arbeit herum.

Gegenueber dem alten Handoff ergaenzt:
- WINDOWS #29 (PATCH /users/:id ohne Rollenausweitungs-Pruefung) als
  eigener Punkt; einziger offener Sicherheitsbefund ohne Bezug zum
  Scharfschalten.
- Phase 17 ist VERIFIED, /gsd-ship lief nie — v1.2 formal nicht
  geschlossen, windows_enforce blockiert bei open_count 15.
- Einordnung der 15 offenen WINDOWS-Eintraege: welche mit 3a/3c fallen,
  welche erst mit #18 akut werden, welche Netz-Luecken sind.
- Placeholder-Suche ueber .planning/phases/ geprueft: 53 Treffer, alle
  Prosa ueber Debt-Marker, keine unfertigen Zusammenfassungen.

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

12 KiB

context, phase, task, total_tasks, status, last_updated
context phase task total_tasks status last_updated
default mandantentrennung-etappe-3 null 4 paused 2026-09-14T08:02:29.277Z

Wiedereinstieg — Etappe 3b abgeschlossen, offen sind 3c, 3a, Etappe 4

Critical Anti-Patterns

Alle stammen aus tatsaechlichen Fehlschlaegen der Etappen 1-3b, nicht aus Vorsicht.

Muster Beschreibung Schwere Vermeidung
Auf ein ungeprueftes Fundament bauen forTenant() 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. 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 ging nach /opt/tessera/docker-compose.yml — der Server nutzt docker-compose.prod.yml, weil die .env COMPOSE_FILE setzt. 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.
Bericht statt Arbeitsbaum geglaubt Zwei Agenten brachen am Sitzungslimit NACH getaner Arbeit ab; die Arbeit lag vollstaendig auf der Platte, der Bericht fehlte. blocking git status ist die Wahrheit, nicht der Agentenbericht.
Zeichensatz beim Veroeffentlichen angenommen Die Handbuch-Webseite ging mit zerlegten Umlauten live ("Für" statt "Fuer"). advisory Seiten mit deutschem Text als reines ASCII ausliefern (Sonderzeichen als \uXXXX), mit all(ord(c)<128 ...) pruefen.

<current_state> Seit dem 2026-09-11 17:58 wurde nichts mehr angefasst. Arbeitsbaum leer (git status --porcelain liefert nichts), main == origin/main, keine Datei im Repo ist juenger als der letzte Commit. Es gibt also KEINE angefangene Arbeit, die aufzunehmen waere — die Sitzung lief nach Etappe 3b bei ~70% Kontext aus, der Handoff war geschrieben. Diese Datei ist die Neuaufnahme desselben Standes am 2026-09-14, ergaenzt um zwei Punkte, die im alten Handoff fehlten (WINDOWS #29 und der nie gelaufene Ship von Phase 17).

Stand der Mandantentrennung: Etappe 1 (Fundament repariert, 227 Zugriffe klassifiziert), Etappe 2 (alle zwoelf Bereiche gebunden, jeder einzeln verifiziert), die drei vorgezogenen Datenbankregeln (260910-jab), WINDOWS #27 (Relations-Blindstelle, 260911-mkj) und Etappe 3b (Benutzerdimension, 260911-nke) sind fertig und gepusht. Endstand 3b: Migration 20260911120000_rls_user_dimension_personal_tables, current_user_id(), forTenant(prisma, tenantId, userId?), zehn persoenliche Tabellen, 34 Aufrufstellen in acht Diensten, 1020 Tests in 62 Dateien, rls-scratch-check.mjs 203/203, sechs Loch-Pruefungen umgedreht.

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. </current_state>

<completed_work>

  • Etappe 1 — forTenant() repariert, Anmeldeweg ueber SECURITY-DEFINER-Funktionen, 227 Zugriffe klassifiziert (5228f28).
  • Etappe 2 — zwoelf Bereiche gebunden (ldap, groups, tenders, dkv, user, module-registry, dashboard, calendar, tenant, auth, favorites, settings), ueber zwoelf Quick-Tasks 260909-ipc .. 260911-gwh (1240932).
  • Drei zu kurz greifende Datenbankregeln geschlossen, auf Anweisung des Users vorgezogen (260910-jab, 03fb3bf).
  • WINDOWS #27 geschlossen — vierte Erkennungsform der Bestandsaufnahme, 72 Paare, Restmenge als #33 eingetragen (260911-mkj, 388690f).
  • Etappe 3b — Benutzerdimension in den Regeln (260911-nke, f0b531b, 07fc653, b62a905, 3b08d8e), verifiziert 13/13. </completed_work>

<remaining_work>

  1. Etappe 3c — Systemkontext fuer die Hintergrunddienste. Sechs Faelle: dkv-scheduler / loadAnyActiveConfigForScheduler() (WINDOWS #21, heute bereits falsch — willkuerlicher Mandant), mail.module / loadAnySmtpConfigForStartupTransport() (WINDOWS #30, dieselbe Form), ldap-config.service getAllActiveConfigs(), tender-digest.scheduler, tender-matching.service, admin-seed.service ensureDefaultGroupsForAllTenants. Bauform: benannter Systemkontext forSystem(prisma) mit dritter Sitzungsvariable app.system_context, lesend erlaubt, Schreiben innerhalb der Schleife je Mandant gebunden. Braucht keine Entscheidung des Users — als naechstes empfohlen.
  2. Etappe 3a — Anmeldenamen pro Mandant. @@unique([tenantId, username]) und @@unique([tenantId, email]) statt plattformweit, die drei SECURITY-DEFINER-Funktionen bekommen p_tenant_id. Enthaelt die eine offene Produktfrage: woran der Login den Mandanten erkennt — Subdomain je Mandant (Empfehlung, weil Tessera hinter Nginx Proxy Manager laeuft) oder Mandantenwahl im Anmeldeformular. Schliesst WINDOWS #22.
  3. Etappe 4 — Scharfschalten. rls-preflight.mjs davor, dokumentierter Rueckweg. NUR nach Rueckfrage beim User. WINDOWS #18.
  4. WINDOWS #29 — offene Rechteausweitung, unabhaengig von der Mandantentrennung. adminResetPassword verhindert ADMIN -> SUPER_ADMIN im eigenen Handler (T-FH9-04), der Schwesterweg PATCH /users/:id (UserController.update, T-02-08) prueft nur, ob die Rolle existiert. Das ist der einzige offene Punkt, der nicht am Scharfschalten haengt, und der einzige mit Sicherheitsbezug vor dem Live-Gehen.
  5. Phase 17 ist VERIFIED, aber /gsd-ship lief nie — v1.2 ist formal nicht geschlossen. Mit windows_enforce blockiert der Ship, solange open_count > 0 (aktuell 15).

Von den 15 offenen WINDOWS-Eintraegen sind #25, #26, #28, #31, #32 Beschreibungen der umgedrehten Fehlerrichtung je Bereich — sie werden erst mit dem Scharfschalten (#18) akut und sind bewusst so abgelegt. #33 und #34 sind Luecken im Netz der Bestandsaufnahme, nicht im Produkt. #21, #22, #24, #30 fallen mit 3a bzw. 3c. </remaining_work>

<decisions_made>

  • Anmeldenamen pro Mandant eindeutig, nicht plattformweit (User, 2026-09-10) — m.schmidt darf es bei Firma A und Firma B geben.
  • Kollegen derselben Firma strikt getrennt (User, 2026-09-10) — jeder sieht nur seine eigenen gespeicherten Suchen, Favoriten, Dashboard-Anordnung. Mit Etappe 3b in den Datenbankregeln verankert.
  • Anmeldeweg ueber SECURITY-DEFINER-Funktionen, nicht ueber eine Policy: eine Policy ist ein Zeilenpraedikat, jede Regel die eine Suche nach Benutzername erlaubt, erlaubt das Lesen der ganzen Tabelle. Die Funktion pinnt die Ausnahme auf feste Spaltenliste, Gleichheit und LIMIT 1.
  • Helfer erweitern statt zweiten bauen — forTenant() bekam den optionalen dritten Parameter statt eines forTenantAndUser().
  • Am Active Directory wird nichts veraendert (User, 2026-09-09, mit Nachdruck).
  • Datenverlust in der Datenbank ist derzeit hinnehmbar (User, 2026-09-09): nichts laeuft produktiv. Gilt nur, solange das so bleibt.
  • Beim Scharfschalten anhalten und fragen (User, seit 2026-09-09) — die einzige Ausnahme von "nicht nachfragen". </decisions_made>

Etappe 4 darf nicht vorgezogen werden. Wird scharf geschaltet, bevor Etappe 3 durch ist, 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. Keine laufenden Hintergrundauftraege (.planning/async-jobs/ existiert nicht).

Required Reading (in order)

  1. docs/mandantentrennung-etappe3-auftrag.md — der gemessene Auftrag fuer 3a/3b/3c; 3b ist dort als erledigt vermerkt, der historische Auftragstext steht daneben
  2. .planning/STATE.md — Position, Quick-Task-Tabelle mit allen Ergebnissen
  3. docs/mandantentrennung-zugriffsklassifikation.md — die Arbeitsgrundlage
  4. docs/mandantentrennung-etappe2-fehlerrichtung.md — Abschnitt "Regelschluss Benutzerdimension (Etappe 3b, 260911-nke)"
  5. docs/mandantentrennung-datenbankrolle.md — Befund, Sperrgrund, Handgriffe, Rueckweg
  6. .planning/WINDOWS.md — 15 offen, davon #29 als einziger ohne Bezug zum Schalter
  7. apps/api/src/prisma/prisma-tenant.extension.ts — der Helfer, dreistellig

Infrastructure State

  • alpha (192.168.13.12, https://alpha.tessera.ctl.de): Stand ea003d4, Container am 2026-09-09 neu erstellt, user-files als Volume tessera_user-files am api-Container nachgewiesen. Live-Gehen am Dienstag, 2026-09-15 — braucht den Schalter NICHT (ein Mandant, BYPASSRLS-Stand laeuft heute dort).
  • Die Serverdatei ist docker-compose.prod.yml, nicht docker-compose.yml — die .env setzt COMPOSE_FILE. Sicherungen als .bak.20260909-0818 daneben.
  • git push geht ausschliesslich ueber localhost:3002; die Push-URL des Remotes ist dauerhaft darauf gesetzt, ein schlichtes git push genuegt.
  • Lokal: api, db und web laufen; KEIN mailhog-Container, deshalb scheitert der Mailversand lokal mit ENOTFOUND mailhog — umgebungsbedingt, kein Defekt.
  • Datenbank: kein Host-Port. IP per docker inspect frisch ermitteln, tessera:tessera_dev. Prisma-Binary aus apps/api/node_modules/.bin/prisma, NICHT npx prisma (zieht Prisma 8).
  • Worktree-Isolation ist abgeschaltet (workflow.use_worktrees=false), weil origin/HEAD in diesem Repo nicht aufloesbar ist.

Pre-Execution Critique Required

Vor jedem weiteren Bereich gilt die Frage aus Etappe 2 unveraendert:

Woran wuerde ich merken, dass eine umgestellte Abfrage jetzt zu WENIG liefert statt zu viel? Der Umbau dreht die Fehlerrichtung um. Der still gefaehrlichste Ort ist jeder Code, der Leere als Abwesenheit deutet und daraufhin loescht.

Etappe 2 lief ueber zwoelf Quick-Tasks plus einen Regel-Durchlauf, jeder mit Planer, Plan-Pruefer, Executor, Verifizierer. ZEHN Lieferungen wurden vom jeweils NAECHSTEN Schritt gefangen, nie vom eigenen: vier geschrumpfte Zaehlungen, zwei nicht committete Messungen, zwei Zusammenfassungen mit N statt N-1, uebersprungene handgepflegte Dokumentstellen, Falsifizierungsnachweise nur in Commit-Nachrichten, eine Wegwerf-Tabelle ohne `createdAt`/`updatedAt`, ein Selbstwiderspruch, ein Pruefer der etwas als plausibel durchwinkte, ein still fehlgeschlagener `git add`, und zwei Agenten die am Sitzungslimit NACH getaner Arbeit abbrachen. Die Kette vollstaendig zu fahren ist deshalb keine Zeremonie, sondern das, was in dieser Arbeit tatsaechlich Fehler gefangen hat.

Verifizierer-Hinweis zu 3b (WINDOWS #34, angenommenes Risiko): die dreistelligen forTenant()-Zusicherungen sind je Spec-Datei, nicht je Methode; das Gate "keine zweistellige Form in den acht Dateien" ist ein Shell-Check, nicht CI. Wer das schliessen will, baut den Check in rls-access-inventory.spec.ts ein.

USER-ANWEISUNG 2026-09-11, weiter gueltig: "mach #27 und dann Etappe 3, nicht nachfragen. du machst alles, was ohne mich geht. am dienstag geht eine voll funktionsfaehige version live." Nach jedem Durchlauf pushen.

<next_action>

  1. docs/mandantentrennung-etappe3-auftrag.md lesen — 3b ist dort als erledigt vermerkt.
  2. Etappe 3c (Systemkontext, sechs Hintergrunddienst-Faelle) als /gsd-quick --validate — braucht keine Entscheidung des Users.
  3. WINDOWS #29 (PATCH /users/:id ohne Rollenausweitungs-Pruefung) — kleiner, unabhaengiger Durchlauf, der einzige Sicherheitspunkt vor dem Live-Gehen.
  4. Etappe 3a — enthaelt die eine offene Produktfrage (Subdomain vs. Login-Wahl).
  5. Etappe 4 — Scharfschalten. NUR nach Rueckfrage beim User. </next_action>