From 38c14005c6bcb800b8bd6a48148646a4fdddafe0 Mon Sep 17 00:00:00 2001 From: Schalli Date: Thu, 17 Sep 2026 13:49:03 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20Aktenstand=20=E2=80=94=20Quick-Task-Zei?= =?UTF-8?q?le=20Freigabe=201.2.0,=20Tabellenzeile=20260914-ebg=20repariert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- .planning/STATE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.planning/STATE.md b/.planning/STATE.md index 902d193..1b7a8f4 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,10 +5,10 @@ current_phase: 18 current_phase_name: desktop-client-fertigstellen status: verified stopped_at: Completed 18-06-PLAN.md — Windows-Bedienprobe des Nutzers steht aus -last_updated: "2026-09-16T15:27:16.943Z" +last_updated: "2026-09-17T11:49:03.528Z" last_activity: 2026-09-17 last_activity_desc: Version 1.2.0 freigegeben; publish-release.sh nimmt nie mehr die oeffentliche Gitea-Adresse (Host-Gateway/localhost) -state_head: a777814034ee5907cb4d151669447f015841082d +state_head: 29565831d11e2749ebf0fb65c1bd18099f72e5b3 progress: total_phases: 18 completed_phases: 15 @@ -417,7 +417,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests. | 260911-nke | **Etappe 3b — Benutzerdimension in den Datenbankregeln.** Migration `20260911120000_rls_user_dimension_personal_tables`: `current_user_id()` (liest `app.current_user`, `NULLIF` fuer den Leerstring), `forTenant(prisma, tenantId, userId?)` mit optionalem drittem Parameter (kein Schwesterhelfer — der Inventar-Detektor haette ihn nicht gesehen), beide `set_config` in EINER Anweisung, `$transaction` behaelt zwei Eintraege. Regeln der ZEHN persoenlichen Tabellen in der Form `tenantId = current_tenant_id() AND (current_user_id() IS NULL OR userId = current_user_id())` — ein Aufruf ohne Benutzer (Admin, Hintergrunddienst) sieht weiter den ganzen Mandanten. `SearchProvider`/`TenderRssFeedSource` mit vier befehlsgetrennten Regeln (jab-Praezedenz), Mandantenhaelften unveraendert; GroupMembership/ModuleGrant/PasswordResetToken/TenderMatch bewusst ohne Benutzerdimension (Verwaltungs-/Anmelde-/Hintergrundobjekte). 34 Nutzer-CRUD-Aufrufstellen in 8 Diensten reichen den Benutzer durch, Scheduler und Verwaltungswege bleiben zweistellig. **SECHS loch-behauptende Pruefungen statt drei** — und die Umkehrung war nicht trivial: die alten massen OHNE Benutzer, eine naive Umkehrung waere nach der Migration rot geworden, weil der Aufruf ohne Benutzer per Absicht beide sieht; jede wurde zu ZWEI (alte Messung unter neuem Namen als gewollte Eigenschaft, Umkehrung MIT Benutzer). 13 Extraktionsstellen im Werkzeug auf die neue Migration umgeleitet. **Wirkungslos mit ausgeschaltetem Schalter** (Rolle `tessera` hat BYPASSRLS, live bestaetigt) — blockiert das Live-Gehen am Dienstag nicht. Angenommene offene Flanke, festgehalten statt verschwiegen: ein Aufrufer, der den Benutzer vergisst, sieht den ganzen Mandanten (heutiger Stand, keine Verschlechterung) — WINDOWS #34; die dreistelligen Spec-Zusicherungen sind je Datei, nicht je Methode, das Gate 'keine zweistellige Form' ist ein Shell-Check, nicht CI — vom Verifizierer als Bewusstseinspunkt vermerkt. **Verifiziert 13/13** (1020/1020 Tests, Typpruefung sauber, 203/203 Live-Pruefungen; `NULLIF` durch Rueckbau falsifiziert, 33 Pruefungen rot; alle zehn Regeln live in `pg_policies` gelesen) | 2026-09-11 | f0b531b,07fc653,b62a905 | [260911-nke-mandantentrennung-etappe-3b-benutzerdime](./quick/260911-nke-mandantentrennung-etappe-3b-benutzerdime/) | | 260909-eor | Etappe 1 der Mandantentrennung: Anmeldeweg mandantenfaehig gemacht und alle Zugriffe klassifiziert. **Kernfund (#20):** `forTenant()` setzte den Mandantenkontext per set_config auf der Transaktionsverbindung, dispatchte die Abfrage aber ueber den aeusseren Client — empirisch reproduziert (set_config auf Backend-PID 254999, Abfrage auf 255000, Kontext dort NULL). Die Trennung hat damit nie funktioniert, auch nicht an den Stellen, die sie scheinbar nutzten; nach dem Scharfschalten haetten diese Abfragen NULL Zeilen geliefert, was der LDAP-Loeschzweig als 'Gruppe im Verzeichnis verschwunden' gedeutet und geloescht haette. Behoben und live nachgewiesen. Der Anmeldeweg bekam drei SECURITY-DEFINER-Funktionen als schmale Ausnahme (feste Spaltenliste, Gleichheitsbedingung, LIMIT 1) — eine Policy haette nicht gereicht, weil sie zwangslaeufig die ganze Tabelle freigibt. Browser-Gegenprobe lokal bestanden: Anmeldung laedt das Portal, falsches Kennwort verraet weiterhin nicht welches Feld, Kennwort-vergessen laeuft durch (der einzige Protokollfehler war ein lokal fehlender Mailserver, also NACH dem Datenbankzugriff). Klassifikation aller 227 Zugriffe in 59 Einheiten, maschinell gegen Abdriften abgesichert: 31 muessen mandantengebunden werden, 9 teilweise, 16 betreffen keine mandantengebundene Tabelle, 3 bleiben bewusst uebergreifend. 701 Tests gruen | 2026-09-09 | da0ac04 | [260909-eor-anmeldeweg-mandantenfaehig-machen-und-al](./quick/260909-eor-anmeldeweg-mandantenfaehig-machen-und-al/) | | 260910-jab | Die drei zu kurz greifenden Datenbankregeln geschlossen — T-JTS-02, T-JTS-03, WINDOWS #19 (bewusste Reihenfolge-Abweichung, vorgezogen auf Nutzerwunsch, statt wie geplant nach Etappe 2). Neue, handgeschriebene, lokal angewandte Migration `20260910120000_rls_widen_membership_grant_and_platform_read`: `GroupMembership` prueft jetzt beide Seiten der Beziehung (Gruppe UND Benutzer), `ModuleGrant` prueft zusaetzlich beide moeglichen Ziele mit Leer-Zulassung (D-04), `TenderRssFeedSource` bekommt vier nach Befehl getrennte Regeln (Lesen schliesst plattformweite Zeilen ein, Schreiben verlangt weiterhin einen Mandanten — die Trennung ist noetig, weil ein einzelner USING-Ausdruck sonst auch UPDATE/DELETE mitregelt). `SearchProvider` bewusst NICHT angefasst: die WINDOWS-#19-Praemisse ist fuer dieses Modell widerlegt (kein Codeweg erzeugt eine mandantenlose Zeile). Drei loch-behauptende Pruefungen im Wegwerf-Werkzeug UMGEKEHRT statt geloescht (66→74 Pruefungen), mit Verweis auf die alten Pruefungsnamen und Befundkennungen im Meldetext. Genau EIN Anwendungspfad musste mitgebunden werden (`TenderRssFeedSourceService.listForUser`) — sonst haette die Reparatur ihn still von 'liefert nach dem Scharfschalten nichts' auf 'liefert nur die plattformweiten Zeilen, taeuscht Vollstaendigkeit vor' verschlechtert; Falsifizierungsnachweis gefuehrt (Bindung zurueckgenommen, genau ein Test rot, zurueckgesetzt). WINDOWS #19 geschlossen mit Beleg, WINDOWS #24 neu angelegt (Verwaltungsweg fuer plattformweite Zeilen unter der Anwendungsrolle fehlt weiterhin — verschwindet nicht mit #19). Aktenstand kohaerent: Klassifikation, Kritikschrift (neuer Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19" mit Signaltabelle beider Fehlerrichtungen je Regel), Betriebsanleitung, WINDOWS.md — fuenf ueberholte Bestandsstellen mit Nachtraegen versehen, alte Messprotokolle bleiben woertlich stehen. Selbst gemessen statt uebernommen: Baseline 833/56 Tests, 66/66 Live-Pruefungen; Endstand 839/56, 74/74; keine zweite Sitzungsvariable fuer den Benutzer gefunden (nur `app.current_tenant`). Rule-1-Fix: implizites `any` in `tenders.controller.ts` nach der Bindung behoben. `npx prisma` versuchte ungefragt Prisma 8 herunterzuladen — abgebrochen, lokale gepinnte 6.19.3 verwendet | 2026-09-10 | f4f3115,6b23735,03fb3bf | [260910-jab-mandantentrennung-die-drei-zu-kurz-greif](./quick/260910-jab-mandantentrennung-die-drei-zu-kurz-greif/) | -| 260914-ebg | **WINDOWS #29 geschlossen — Zielrollen-Riegel in `UserController.update()`/`remove()`.** Ein ADMIN kann den SUPER_ADMIN seines Mandanten nicht mehr aendern (Kennwort, isActive, Rolle, Anmeldename, E-Mail) oder loeschen; Riegel nach der Mandantengrenze, vor der Rollenzuweisungs-Pruefung (Vorlage `AuthService.adminResetPassword`, T-FH9-04). Acht neue Spec-Tests (8 -> 16), Baseline 1020 -> 1028 Tests / 62 Dateien, Falsifizierung durch Rueckbau `Tests 2 failed | 14 passed (16)` (Test 9/13), unabhaengig vom Verifizierer wiederholt. Kopfkommentar `adminResetPassword` nachgezogen (T-FH9-05 nicht mehr offen). Ledger 16 offen / 1 zurueckgestellt / 19 geschlossen / 36 gesamt: #29 fixed, NEU #35 (Biome-Konfiguration im Bestand nicht lauffaehig, `pnpm lint` Leerlauf) und #36 (Admin-Frontend verschluckt 403 still). Verifiziert 6/6, gepusht. | 2026-09-14 | 759ea3b,63f9df0,70d007b | [260914-ebg-windows-29-schliessen-rechteausweitung-a](./quick/260914-ebg-windows-29-schliessen-rechteausweitung-a/) | +| 260914-ebg | **WINDOWS #29 geschlossen — Zielrollen-Riegel in `UserController.update()`/`remove()`.** Ein ADMIN kann den SUPER_ADMIN seines Mandanten nicht mehr aendern (Kennwort, isActive, Rolle, Anmeldename, E-Mail) oder loeschen; Riegel nach der Mandantengrenze, vor der Rollenzuweisungs-Pruefung (Vorlage `AuthService.adminResetPassword`, T-FH9-04). Acht neue Spec-Tests (8 -> 16), Baseline 1020 -> 1028 Tests / 62 Dateien, Falsifizierung durch Rueckbau `Tests 2 failed / 14 passed (16)` (Test 9/13), unabhaengig vom Verifizierer wiederholt. Kopfkommentar `adminResetPassword` nachgezogen (T-FH9-05 nicht mehr offen). Ledger 16 offen / 1 zurueckgestellt / 19 geschlossen / 36 gesamt: #29 fixed, NEU #35 (Biome-Konfiguration im Bestand nicht lauffaehig, `pnpm lint` Leerlauf) und #36 (Admin-Frontend verschluckt 403 still). Verifiziert 6/6, gepusht. | 2026-09-14 | 759ea3b,63f9df0,70d007b | [260914-ebg-windows-29-schliessen-rechteausweitung-a](./quick/260914-ebg-windows-29-schliessen-rechteausweitung-a/) | | 260914-eym | **Etappe 3c — Systemkontext fuer die Hintergrunddienste.** Migration `20260914120000_rls_system_context_read`: `is_system_context()`, fuenf permissive `system_read_policy ... FOR SELECT` (DkvModuleConfig, LdapConfig, LdapFieldMapping, TenderMatch, TenderSavedSearch); `forSystem(prisma)` in Array-Form mit ausdruecklichem Zuruecksetzen von Mandant/Benutzer, `forTenant()` setzt `app.system_context` zurueck (kein Erben, gemessen). Sechs Faelle: DKV-Planer einmal-abfragen-viele-bedienen (Auftrag je Mandant, WINDOWS #21 fixed); Mail-Transport je Versand aus der SmtpConfig des Empfaenger-Mandanten mit unveraenderter Umgebungs-Rueckfallkette, Startpfad und Mailer-Fabrik entfallen (WINDOWS #30 fixed, SmtpConfig ohne Systemregel); ldap `getAllActiveConfigs()` und Boot-Nachverschluesselung lesen ueber Systemkontext, schreiben je Mandant gebunden; tender-digest Kandidaten und tender-matching Suchprofile ueber Systemkontext, Schleifen gebunden; admin-seed nur dokumentiert (Tenant ohne Regel). Detektor mit fuenfter Erkennungsform `forSystem(` und exakter Erlaubnisliste (falsifiziert: Fremddatei 1 rot, Zweitaufruf 2 rot). Werkzeug 203 -> 253 (`runSystemContextChecks`: ungebunden 0 / System beide Mandanten / Schreiben abgewiesen 42501 bzw. count 0 / kein Erben / pg_policies 34, 5x SELECT). Rueckbau (a) 5 rot mit gelungenem Insert, (b) 1 rot, (c1) 253 gruen + (c2) 5 rot, (d) 2/3 rot. Tests 1028 -> 1054 / 62 -> 64 Dateien, tsc 0, 29 Dateien gegen 5e0e408, Schalter AUS (Compose/.env/Schema/Lockfile unveraendert). Klassifikation 61/179/5, 72 Paare, sechs Zeilen `system-gebunden`; Kritikschrift (y1)-(y5); Auftrag 3c Erledigt. Ledger 15 offen / 1 zurueckgestellt / 21 geschlossen / 37 gesamt; NEU #37 (prozessweiter Single-Flight-Riegel `processInbox`). Verifiziert 9/9, gepusht. | 2026-09-14 | 3d64567,6e2a641,939c812 | [260914-eym-mandantentrennung-etappe-3c-systemkontex](./quick/260914-eym-mandantentrennung-etappe-3c-systemkontex/) | | 260914-ku1 | **Zwei Auslieferungskanaele und Versionsstempel.** `main` = Beta (Etiketten `beta` + `latest`), Tag `vX.Y.Z` = Live (Etiketten `live` + `vX.Y.Z`), Zweig `live` ohne Tag nur geprueft — Entscheidung in `.gitea/scripts/publish-images.sh` (`--print-plan`), CI-Trigger `branches: [main, live]` + `tags: [v*]`, `fetch-depth: 0`. Versionsstempel `APP_VERSION/APP_CHANNEL/APP_COMMIT/APP_BUILD_TIME` als Build-Args in beide Dockerfiles (web zur Bauzeit als `NEXT_PUBLIC_APP_*`, api als Laufzeit-ENV; Vorgabe `dev`). `GET /health/version` liefert name/version/channel/commit/buildTime, Startlog `Tessera API vX (channel) commit`. Web: `app-version.ts`, `AppVersionBadge` in `sidebar.tsx` (sidebar-footer.tsx ist seit ba02b25 toter Code). `docker-compose.prod.yml`: `image: ...:${IMAGE_TAG:-beta}`. Betriebshandbuch Kapitel 9 (Zwei Kanaele, Freigabe, Hotfix ohne Datenbankaenderung, neuer Live-Server), ci-cd-setup.md auf gemessenen Stand. Falsifiziert: Build mit `v9.9.9-test live` -> Stempel in dist und Web-Bundle, ohne Args `dev`. Echter CI-Lauf 297 gruen (5:18 min), Abbilder `beta`/`latest` tragen `ea6aa99 beta`. Tests API 1054 -> 1060 / 64 -> 65 Dateien, Web 233 -> 243 / 38 -> 40, tsc 0, 20 Dateien gegen 6c19451. Offen: Zweig `live` + Tag `v1.0.0` nach dem Fehler-melden-Knopf anlegen; Handgriffe fuer den User (IMAGE_TAG je Server) im SUMMARY. Verifiziert 8/8, gepusht. | 2026-09-14 | cdb571c,9731501,ea6aa99 | [260914-ku1-zwei-auslieferungskanaele-beta-auf-main-](./quick/260914-ku1-zwei-auslieferungskanaele-beta-auf-main-/) | | 260914-m97 | **Fehler-melden-Knopf.** Kaefer-Knopf in der Kopfzeile: Bildschirmfoto VOR dem Dialog (`html-to-image` 1.11.13, laengste Kante 1600 px, `computeCaptureSize`), Dialog mit Vorschau, Haekchen und "Was ist passiert?"; Fehlerpuffer (Ringpuffer 20: window.onerror, unhandledrejection, console.error, fehlgeschlagene fetch-Antworten — keine Ruempfe/Cookies/Tokens); `POST /bug-reports` als Multipart (FileInterceptor 4 MiB -> 413, PNG-Signatur -> 400, kein Empfaenger -> 409, Drossel 5/10 min -> 429, Versandfehler -> 502; Mandant/Benutzer nur aus der Sitzung); E-Mail mit PNG-Anhang und Kontext (URL, Web-/API-Version+Kanal+Commit, Browser, Fenster, Zeitpunkt, Benutzer, letzte Fehler) ueber `MailService.sendBugReport` (Anhaenge; Kennwort-Reset bleibt verschluckend). Empfaenger: neue nullable Spalte `SmtpConfig.bugReportRecipient` (Migration `20260914170000`), Feld "Fehlermeldungen an" unter Administrator -> SMTP, Rueckfall `TESSERA_BUGREPORT_TO` (docker-compose.prod.yml). Handbuecher Anwender/Administration/Betrieb. Tests API 1060 -> 1076 / 67 Dateien, Web 243 -> 260 / 43 Dateien, tsc 0, `--frozen-lockfile` 0, 35 Dateien gegen 5c42c55, vier Commits. CI-Lauf 299 gruen (zweiter Versuch, erster scheiterte an Gitea-DB). Browser-Beweis durch den Orchestrator: E-Mail mit 85-KB-PNG (ohne Dialog, OKLCH korrekt) in mailhog, 409-Pfad im Dialog. Ledger #38 (Rule-1-Fix `@Expose()`) als fixed. Verifiziert 9/9 + Browser, gepusht. | 2026-09-14 | 54121c1,60b0ee8,b41be21,77117de | [260914-m97-fehler-melden-knopf-bildschirmfoto-der-a](./quick/260914-m97-fehler-melden-knopf-bildschirmfoto-der-a/) | @@ -436,6 +436,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests. | 260917-gsh | **Akzentfarbe als Hex-Code eingebbar; Bildmarke uebernimmt die Akzentfarbe.** `normalizeHexColor()` in `lib/color.ts` (optionales `#`, 3-stellige Kurzform, Kleinschreibung; 9 Tests), Textfeld neben dem Farbwaehler mit Zwei-Wege-Sync, `aria-invalid` + Fehlertext + gesperrtes Speichern bei ungueltigem Wert (7 Komponententests), i18n `settings.account.accentColorHex/-Invalid`. Gedrehte Kachel in `LogoMark` per Inline-Style `fill: var(--primary, #ffed00)` — folgt `applyAccentColor`, Anmeldeseite bleibt gelb. Tests Web 365 -> 381 / 57 Dateien, tsc 0. | 2026-09-17 | 795c6a4,1601d97,db478e0 | [260917-gsh-akzentfarbe-in-einstellungen-konto-zusae](./quick/260917-gsh-akzentfarbe-in-einstellungen-konto-zusae/) | | 260917-gyd | **Web-Robustheit: Ruecksprung nach Anmeldung, Sitzungswaechter, Widgets-Seite uebersetzt.** Middleware leitet auf `/login?next=` (Helfer `lib/safe-next.ts`: nur relative Pfade, kein `//`, kein `\\`, kein `/login`; Tests), Anmeldeseite springt nach Erfolg dorthin. Neue Server Action `fetchSessionState()` (authenticated/unauthenticated/unavailable): bei 401/403 oder 200 ohne Benutzer wird das Sitzungscookie geloescht und der Header leitet auf `/login?next=…` — 5xx/Netzwerkfehler bleiben still (kein Redirect bei API-Ausfall). Befund vom Testserver-DB-Reset: `/auth/me` liefert bei geloeschtem Benutzer 200 mit leerem Body. `settings/dashboard`: `common.loading` + `settings.widgets.empty` statt englischer Hartkodierung. Tests Web gruen, tsc 0. | 2026-09-17 | 4b279ea,474d170,2868ffe | [260917-gyd-web-nach-anmeldung-zurueck-zur-ursprueng](./quick/260917-gyd-web-nach-anmeldung-zurueck-zur-ursprueng/) | | 260917-h2s | **Desktop-Client-Erkennung, Beta-Hinweis, deutscher Installer.** Rust: `with_desktop_marker()` haengt `desktop=1` an beide Navigationen zur Server-Adresse (Store bleibt sauber); `update_labels()` — bei gleicher Version nennt Tray/Benachrichtigung „Neuen Beta-Stand {commit}“ statt „Version X“ (5 Rust-Tests). Web: Middleware setzt Cookie `tessera_desktop=1` (`withDesktopCookie` um jede Rueckgabe), `lib/desktop-client.ts` (`isDesktopClient`/`useIsDesktopClient`), `DesktopDownloadLinks` rendert im Client nichts, `DesktopContextMenuGuard` im RootLayout blockt Rechtsklick ausser in Eingabefeldern. Installer: `bundle.windows.nsis` languages German, kein Sprachwahldialog, installerIcon icon.ico, Header/Sidebar-BMP (Markengelb + Tessera-Zeichen, resvg-Quelle), installMode currentUser; Handbuecher ergaenzt. Web-Tests 417 / 63 Dateien, tsc 0. Browser: Cookie, Link-Ausblendung, Kontextmenue, Ruecksprung, 401-Waechter lokal bestaetigt; Installer/Client-Cookie nach CI auf der Windows-VM. | 2026-09-17 | 5bdabf5,d9b94bd,2cd4adc | [260917-h2s-desktop-client-web-erkennt-den-client-do](./quick/260917-h2s-desktop-client-web-erkennt-den-client-do/) | +| 62 | **Freigabe 1.2.0** (CHANGELOG umbenannt f7f406a, live ff auf main, Tag v1.2.0; Abbilder live/v1.2.0 gebaut). Release-Anhaenge schlugen im CI fehl: publish-release.sh nahm GITHUB_API_URL (git.vicolab.de, Proxy bricht 82-MB-Upload ab, curl 92). Anhaenge vom Host ueber localhost:3002 nachgetragen; Skript nimmt jetzt NIE die oeffentliche Adresse — im CI Host-Gateway aus /proc/net/route:3002, lokal localhost:3002 (507556f, docs/ci-cd-setup.md). | 2026-09-17 | 2956583 | — | ## Deferred Items