Files
tessera-ctl/.planning/quick/260925-bow-was-ist-neu-fenster-beim-ersten-anmelden/260925-bow-SUMMARY.md
T
schalli acfffa3097
Tessera CI/CD / Lint & Type Check (push) Successful in 51s
Tessera CI/CD / Tests (push) Successful in 1m20s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 18s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m13s
docs(quick-260925-bow): Was-ist-neu-Fenster
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 09:01:48 +02:00

12 KiB

phase, plan, quick_id, status, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics, actuals, plan_head_before
phase plan quick_id status subsystem tags requires provides affects tech-stack key-files decisions metrics actuals plan_head_before
quick-260925-bow 01 260925-bow complete web + api (Benutzer, Änderungsliste)
release-notice
changelog
user
prisma
a11y
rls
CHANGELOG.md als Bauzeit-Text (quick-260916-dcz)
APP_VERSION als Laufzeit-ENV der API (quick-260914-ku1)
Spalte User.lastSeenReleaseVersion (Migration 20260925120000_user_last_seen_release)
parseReleaseVersion / compareReleaseVersions / ReleaseNoticeResponse in @tessera/shared
getRunningRelease() als einzige Quelle der laufenden Version
GET /users/me/release-notice, POST /users/me/release-seen
selectReleaseNotice (Web), Server-Aktionen, ReleaseNoticeDialog, ReleaseNoticeHost in AppShell
apps/web/src/components/layout/app-shell.tsx
apps/api/src/user/user.service.ts (create)
apps/api/src/user/admin-seed.service.ts
added patterns
Server-Aktion in eigener 'use server'-Datei als einziger Web-Importeur der Änderungsliste neben page.tsx
Fenster per React.lazy nur bei vorhandener Nachricht geladen
created modified
apps/api/prisma/migrations/20260925120000_user_last_seen_release/migration.sql
apps/api/src/health/release-version.spec.ts
apps/api/src/user/dto/release-seen.dto.ts
apps/web/src/lib/release-notes.ts
apps/web/src/lib/release-notes.test.ts
apps/web/src/lib/release-notice-actions.ts
apps/web/src/lib/release-notice-actions.test.ts
apps/web/src/components/release-notice/release-notice-dialog.tsx
apps/web/src/components/release-notice/release-notice-dialog.test.tsx
apps/web/src/components/release-notice/release-notice-host.tsx
apps/web/src/components/release-notice/release-notice-host.test.tsx
packages/shared/src/index.ts
apps/api/prisma/schema.prisma
apps/api/src/health/app-version.ts
apps/api/src/user/user.controller.ts
apps/api/src/user/user.controller.spec.ts
apps/api/src/user/user.service.ts
apps/api/src/user/user.service.spec.ts
apps/api/src/user/admin-seed.service.ts
apps/api/src/user/admin-seed.service.spec.ts
apps/web/src/components/changelog/changelog-view.tsx
apps/web/src/components/layout/app-shell.tsx
apps/web/src/messages/de.json
apps/web/src/messages/en.json
apps/web/src/messages/umlaut-dictionary.ts
apps/web/src/lib/changelog.ts
apps/web/next.config.ts
docs/mandantentrennung-zugriffsklassifikation.md
docs/anleitung-anwender.md
docs/anleitung-entwicklung.md
CHANGELOG.md
Einzige Quelle der laufenden Version ist APP_VERSION der API (getRunningRelease()); das Web nimmt currentRelease aus GET /users/me/release-notice
Fehlt der Abschnitt der laufenden Version in der Änderungsliste des Web-Abbilds ganz, gibt es kein Fenster; eine laufende Version nur mit „Entfernt“ zählt ebenso als ohne Abschnitt
Scrollbereich des Fensters ist ein benannter section ohne tabIndex (keine neue Biome-Warnung, keine Biome-Ausnahme)
duration completed
16min 2026-09-25
tokens tasks commits
30400 3 9
9225ed1bf9

Quick 260925-bow: „Was ist neu“-Fenster beim ersten Anmelden nach einem Versionswechsel Summary

Nach einem Versionswechsel zeigt Tessera jedem Benutzer beim ersten Laden des Portals einmal ein Fenster „Neu in Version X.Y.Z“ mit Neu / Verbessert / Behoben aus CHANGELOG.md (höchstens drei Versionen, neueste zuerst). Der gesehene Stand liegt pro Benutzer in der neuen Spalte User.lastSeenReleaseVersion und wird erst beim Schließen über POST /users/me/release-seen gemerkt, gebunden an Benutzer und Mandanten.

Was gebaut wurde

Aufgabe 1 (Tracer, DB → API → Server-Aktion → Fenster)

  • packages/shared: parseReleaseVersion (optionales v, drei Zifferngruppen zu je 1 bis 6 Ziffern, optional Describe-Anhang, verankert, ≤ 64 Zeichen, kanonisch ohne führende Nullen), compareReleaseVersions (numerisch, wirft bei Unparsebarem), ReleaseNoticeResponse. Warnkommentar über WIDGET_TYPES ergänzt.
  • getRunningRelease() in app-version.ts mit Begründung, warum die API die Quelle ist.
  • Spalte + Migration (ALTER TABLE "User" ADD COLUMN "lastSeenReleaseVersion" TEXT;), kein Standardwert, kein Backfill. Lokal auf die DB angewendet (prisma migrate deploy über 172.19.0.2); tessera_app hat Rechte auf Tabellenebene und damit auch auf die neue Spalte.
  • GET me/release-notice und POST me/release-seen in user.controller.ts vor der Kennungs-Route, ohne @Roles, beide über forTenant() + where: { id: currentUser.id }. ReleaseSeenDto mit @IsString, @MaxLength(32), @Matches auf kanonisches X.Y.Z; im Rumpf zusätzlich Format-, dev- und „nicht über laufend“-Prüfung; nie absenken, unparsebaren Altwert überschreiben.
  • Web: release-notes.ts (reine Auswahl, kein Import der Änderungsliste), release-notice-actions.ts ('use server'), ReleaseNoticeDialog (role=dialog, aria-modal, aria-labelledby, Anfangsfokus Überschrift, Escape, Fokusfalle mit dynamischer Elementliste, Fokus-Rückgabe, keine Animation, nur Tailwind-Tokens), ReleaseNoticeHost (Ref-Sperre, nicht auf /change-password, React.lazy, merkt erst beim Schließen), eingebunden nur in AppShell. ChangelogView mit variant="plain". Texte releaseNotice in de.json/en.json.

Aufgabe 2 (Anlagewege)

  • UserService.create() (Admin-Anlage und beide LDAP-Wege) und AdminSeedService setzen lastSeenReleaseVersion: getRunningRelease(); kein neuer Parameter. grep-Nachweis: kein eigener user.create in apps/api/src/ldap.
  • RLS-Buchführung nachgemessen mit der Gate-Schleife: user 8/17/0 (vorher 8/14/0, +3 gebunden in user.controller.ts), Summe 61/216/6 (vorher 61/213/6). Übersichts-, Summen- und Fundstellenzeile mit Vermerk quick-260925-bow fortgeschrieben. rls-access-inventory.spec.ts grün (Paar user.controller.ts | user | gebunden unverändert).

Aufgabe 3 (Doku und volle Prüfungen)

  • CHANGELOG „Unveröffentlicht → Neu“, Abschnitt „Was ist neu“ im Anwenderhandbuch, Entwicklerdoku (Importregel erweitert, Versionsquelle, Endpunkte, Spalte, Folge für die Freigabe, Ausprobieren mit APP_VERSION), Kopfkommentare changelog.ts und next.config.ts.

Gemessene Ergebnisse

Prüfung Ergebnis
API-Suite (voll) 85 Dateien, 1435 Tests grün
Web-Suite (voll) 95 Dateien, 924 Tests grün
pnpm turbo run type-check lint 9/9 Tasks erfolgreich
Biome-Warnungen Web 53 (Grenze 53), API 82 (Grenze 82)
Stil-Gate (Versal/Sperrschrift, ·, →, rohes HTML) leer
i18n-Gate releaseNotice de/en vollständig
RLS user / Summe 8/17/0 / 61/216/6
next build erfolgreich
Chunk-Nachweis Satz „hochgeladene Bilder liegen jetzt im Dateibereich des Servers“: 2 Dateien unter .next/server (changelog/page.js und der Server-Chunk der Aktion), 0 unter .next/static; ebenso der neue CHANGELOG-Eintrag (2 / 0). next-env.d.ts unverändert.

Abgleich mit der echten CHANGELOG.md: gemerkt 1.0.0, laufend 1.4.0 ergibt 1.4.0 (new/changed/fixed), 1.3.1 (changed/fixed), 1.3.0 (new/changed/fixed) und omittedCount 2, also genau das, was die Browserprüfung des Orchestrators erwartet; gemerkt null ergibt nur 1.4.0; gemerkt 1.4.0 ergibt kein Fenster.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] StrictMode-Doppeleffekt verwarf das Abrufergebnis im Host

  • Found during: Aufgabe 1 (Host-Test „StrictMode-Doppeleffekt führt nicht zu zwei Abrufen“)
  • Issue: Ein Abbruch-Flag im Aufräumen des Effekts wurde beim simulierten Unmount gesetzt, der zweite Effektlauf fragte wegen der Ref-Sperre nicht erneut, das einzige Ergebnis ging verloren, kein Fenster.
  • Fix: Abbruch-Flag entfernt (setState nach Unmount ist in React 19 folgenlos), Kommentar dazu.
  • Commit: 187fb76

2. [Rule 3 - Blocking] Umlaut-Wächter kannte „Verbessert“ nicht

  • Found during: Aufgabe 3 (volle Web-Suite)
  • Issue: umlaut-guard.spec.ts meldete das korrekt geschriebene Wort „Verbessert“ (Gruppe releaseNotice.section.changed) als unbekanntes „ss“-Wort.
  • Fix: „Verbessert“ in UMLAUT_ALLOWLIST (umlaut-dictionary.ts) mit Vermerk aufgenommen.
  • Commit: 2aeb3e8

3. [Rule 3 - Blocking] Scrollbereich ohne tabIndex={0}

  • Issue: Der geplante div tabIndex={0} aria-label erzeugte zwei neue Biome-Warnungen (noNoninteractiveTabindex, useAriaPropsSupportedByRole) und hätte die Grenze Web ≤ 53 gerissen; Biome-Ausnahmen in neuen Dateien sind laut Plan verboten.
  • Fix: Benannter <section aria-label={contentLabel}> ohne tabIndex. Heutige Chromium- und WebKit-Versionen (Browser und Desktop-App) machen einen Scroll-Container ohne fokussierbaren Inhalt selbst per Tastatur erreichbar; enthält er Links, scrollt der Tab-Fokus mit. Innere Versionsblöcke sind div statt section, damit keine verschachtelten Landmarken entstehen.
  • Commit: 187fb76

4. [Kleinigkeit] Überschriften-Hierarchie

  • Bei einer Version sind die Gruppen h3 (wie geplant); bei mehreren Versionen trägt die Version h3 und die Gruppen h4 (statt einer Versionszeile ohne Überschriftenrolle). Test entsprechend.

5. [Hinweis] prisma validate braucht DATABASE_URL

  • Das Aufgabe-1-Gate ruft prisma validate ohne Umgebung auf; hier scheitert das nur an der fehlenden DATABASE_URL (P1012). Mit gesetzter URL (echte Container-IP bzw. Platzhalter) ist das Schema gültig. prisma format wurde bewusst NICHT verwendet, weil es die ganze Schemadatei umformatiert hätte; die neue Zeile ist von Hand eingetragen.

6. [Hinweis] Bestehende Format-Befunde nicht angefasst

  • biome check meldet in app-shell.tsx und changelog-view.tsx Format-/Importreihenfolge-Befunde, die schon vorher bestanden; das Lint-Skript ist biome lint, die Dateien wurden deshalb nicht umformatiert. Neue Dateien sind mit biome check --write formatiert, ohne Ausnahmen, any nur in Test-Attrappen wie im Bestand.

Hinweise für den Orchestrator (Browserprüfung, D-11)

  • Die Migration ist auf der lokalen DB bereits angewendet; der laufende API-Container (altes Abbild) stört sich an der zusätzlichen nullbaren Spalte nicht. Für die Prüfung muss wie im Plan beschrieben mit --build-arg APP_VERSION=1.4.0-1-g0000000 neu gebaut werden (der Describe-Anhang braucht mindestens 4 Hex-Zeichen nach g, g0000000 passt).
  • Auf /change-password wird nicht abgefragt; die nächste Seite danach fragt einmal.

Threat Flags

Keine neuen Angriffsflächen außerhalb des Bedrohungsmodells: die zwei Endpunkte (T-BOW-01/02/05/06), die Migration (T-BOW-07), die Server-Aktion/Chunks (T-BOW-03) und das Markdown-Rendering über ChangelogView mit rehype-sanitize (T-BOW-04) sind dort erfasst und umgesetzt.

Commits

Hash Nachricht
b35edd5 test(260925-bow): Versionsvergleich und Was-ist-neu-Endpunkte (rot)
59db32a feat(260925-bow): gesehene Version pro Benutzer merken - Spalte, Versionsfunktionen, API
cf7784e test(260925-bow): Auswahl, Server-Aktionen, Fenster und Host des Was-ist-neu-Fensters
187fb76 feat(260925-bow): Was-ist-neu-Fenster im Portal-Rahmen
25c8db7 test(260925-bow): Anlagewege tragen die laufende Version ein (rot)
5ae9aaa feat(260925-bow): neue Benutzer bekommen die laufende Version eingetragen
4fa5aaf docs(260925-bow): RLS-Buchfuehrung nachgemessen (user 8/17/0, Summe 61/216/6)
2aeb3e8 fix(260925-bow): Umlaut-Waechter kennt das korrekte Wort Verbessert
b3b7b5d docs(260925-bow): Was-ist-neu-Fenster in CHANGELOG, Anwender- und Entwicklerdoku

Nicht gepusht, kein Tag, keine Freigabe. .planning/** nicht committet.

Self-Check: PASSED

Alle 11 neuen Dateien vorhanden, alle 9 Commits im Verlauf (git rev-list --count 9225ed1..HEAD = 9).