Files
schalli ff70b4efba
Tessera CI/CD / Lint & Type Check (push) Successful in 48s
Tessera CI/CD / Tests (push) Successful in 49s
Tessera CI/CD / Build & Publish Images (push) Successful in 28s
docs(quick-260805-fok): Standardgruppe bei Mandanten-Anlage + Startup-Reparatur
2026-08-05 11:37:43 +02:00

9.5 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions requirements-completed coverage duration completed status
quick-260805-fok 01 api
nestjs
prisma
groups
multi-tenancy
onapplicationbootstrap
phase provides
15-modul-berechtigungen-gruppen-user-grants Group/GroupMembership/ModuleGrant-Schema, partieller Unique-Index Group_one_default_per_tenant, GroupsService.addUserToDefaultGroup
GroupsService.ensureDefaultGroup(tenantId) — race-sichere, idempotente Standardgruppen-Anlage mit Null-Gruppen-Wächter (D-13)
TenantService.create verdrahtet mit ensureDefaultGroup
AdminSeedService.ensureDefaultGroupsForAllTenants() — Startup-Reparatur über alle Mandanten
groups
tenant
user
admin-seed
added patterns
Sequenzielle await-Kette in onApplicationBootstrap statt Verlass auf Nest-Hook-Reihenfolge zwischen Modulen (Muster aus tender-scheduler.service.ts übernommen)
Interaktive Prisma-Transaktion ($transaction mit async-Callback) statt Array-Form, wenn spätere Schritte die id eines vorherigen Schritts brauchen
created modified
apps/api/src/tenant/tenant.service.spec.ts
apps/api/src/user/admin-seed.service.spec.ts
apps/api/src/groups/groups.service.ts
apps/api/src/groups/groups.service.spec.ts
apps/api/src/tenant/tenant.service.ts
apps/api/src/tenant/tenant.module.ts
apps/api/src/user/admin-seed.service.ts
Wächter prüft ausschließlich group.count === 0, nie das Fehlen der isDefault-Markierung — ein Mandant mit Gruppen ohne Markierung ist eine bewusste Admin-Entscheidung (D-13) und wird nie überschrieben
Reparatur läuft als zweiter sequenzieller await-Schritt in AdminSeedService.onApplicationBootstrap, NICHT als eigener Hook in GroupsModule — GroupsModule ist Dependency von UserModule, seine Hooks liefen sonst vor der Tenant-Anlage
P2002 aus dem partiellen Index Group_one_default_per_tenant wird in ensureDefaultGroup abgefangen und liefert null, propagiert nicht — der Index bleibt der eigentliche Durchsetzungspunkt
260805-fok
id description requirement verification human_judgment
D1 GroupsService.ensureDefaultGroup(tenantId) legt für einen Mandanten ohne jede Gruppe die Standardgruppe 'Alle Benutzer' an, mit Mitgliedern und Modul-Grants, race-sicher und idempotent 260805-fok
kind ref status
unit apps/api/src/groups/groups.service.spec.ts#ensureDefaultGroup() pass
false
id description requirement verification human_judgment
D2 TenantService.create ruft ensureDefaultGroup auf; Fehler werden protokolliert, nicht propagiert 260805-fok
kind ref status
unit apps/api/src/tenant/tenant.service.spec.ts pass
false
id description requirement verification human_judgment
D3 AdminSeedService führt die Standardgruppen-Anlage vor dem Super-Admin-Anlegen aus und repariert am Ende von onApplicationBootstrap alle Mandanten ohne Gruppe, unabhängig von den frühen Rückkehrpfaden 260805-fok
kind ref status
unit apps/api/src/user/admin-seed.service.spec.ts pass
false
id description verification human_judgment
D4 Kein Schemawechsel, keine neue Migration — 24 Migrationsverzeichnisse unverändert, schema.prisma unverändert
kind ref status
other test "$(ls -1d apps/api/prisma/migrations/*/ | wc -l)" -eq 24 && git diff --quiet -- apps/api/prisma/schema.prisma pass
false
6min 2026-08-05 complete

Quick Task 260805-fok: Standardgruppe bei Mandanten-Anlage und Startup-Reparatur Summary

GroupsService.ensureDefaultGroup(tenantId) verlegt die "jeder Mandant hat eine Standardgruppe"-Invariante von der einmaligen Migration in beide Mandanten-Entstehungspfade (TenantService.create, AdminSeedService) plus eine Startup-Reparatur über alle Bestandsmandanten.

Performance

  • Duration: 6 min
  • Started: 2026-08-05T11:25:00+02:00 (circa)
  • Completed: 2026-08-05T11:30:49+02:00
  • Tasks: 2
  • Files modified: 7 (2 neu, 5 geändert)

Accomplishments

  • GroupsService.ensureDefaultGroup(tenantId): legt für einen Mandanten ohne jede Gruppe "Alle Benutzer" (isDefault:true) an, nimmt alle Bestandsbenutzer als MANUAL-Mitglieder auf und erzeugt Grants für alle aktiven Module — derselbe Endzustand wie die drei Backfill-INSERTs der Migration 20260804130130_add_groups_and_module_grants. Wächter prüft ausschließlich group.count === 0 (D-13), fängt P2002 aus dem partiellen Index Group_one_default_per_tenant ab und liefert null statt zu werfen.
  • TenantService.create ruft ensureDefaultGroup nach der Mandanten-Anlage auf; ein Fehler wird protokolliert, die Mandanten-Anlage schlägt trotzdem nicht fehl.
  • AdminSeedService.onApplicationBootstrap läuft jetzt als zwei sequenzielle await-Schritte: seedAdmin() (bisheriger Rumpf, jetzt mit ensureDefaultGroup nach dem Tenant-Upsert und VOR user.create) und danach ensureDefaultGroupsForAllTenants() — die einmalige Reparatur über jeden Mandanten aus prisma.tenant.findMany, unabhängig von seedAdmin()s beiden frühen Rückkehrpfaden (fehlende ENV-Variablen, Admin existiert bereits — genau der Fall auf dem Testserver).

Task Commits

Each task was committed atomically:

  1. Task 1: GroupsService.ensureDefaultGroup(tenantId) + Spec - 9d1254c (feat)
  2. Task 2: Beide Mandanten-Entstehungspfade verdrahten + Startup-Reparatur + Specs - 0d7d8a5 (feat)

Plan metadata: wird vom Orchestrator committet (SUMMARY.md, STATE.md)

Files Created/Modified

  • apps/api/src/groups/groups.service.ts - neue Methode ensureDefaultGroup(tenantId) vor addUserToDefaultGroup
  • apps/api/src/groups/groups.service.spec.ts - Fake erweitert (group.count, tenantModuleActivation, moduleGrant.findMany/createMany, $transaction-Callback-Form) plus voller ensureDefaultGroup-Testblock
  • apps/api/src/tenant/tenant.service.ts - GroupsService injiziert, create() ruft ensureDefaultGroup in try/catch mit Logger auf
  • apps/api/src/tenant/tenant.module.ts - GroupsModule importiert
  • apps/api/src/tenant/tenant.service.spec.ts (neu) - beweist Aufruf und nicht-fatalen Fehlerpfad
  • apps/api/src/user/admin-seed.service.ts - seedAdmin() extrahiert, ensureDefaultGroup nach dem Tenant-Upsert eingefügt, ensureDefaultGroupsForAllTenants() als abschließende Reparatur
  • apps/api/src/user/admin-seed.service.spec.ts (neu) - Reihenfolge, Reparatur über alle Mandanten, Idempotenz über zwei Bootstrap-Läufe, Fehlerisolation je Mandant

Decisions Made

  • Wächter prüft ausschließlich group.count === 0, nie das Fehlen der isDefault-Markierung — D-13 erlaubt dem Admin ausdrücklich, die Markierung abzuhängen oder umzuhängen; die Reparatur darf diese Entscheidung nie überschreiben.
  • Die Reparatur läuft als zweiter sequenzieller await-Schritt in AdminSeedService.onApplicationBootstrap, nicht als eigener Hook in GroupsModule: GroupsModule ist Dependency von UserModule, seine onApplicationBootstrap-Hooks liefen deshalb vor dem Admin-Seed und würden auf einer frischen Installation den noch nicht existierenden Default-Mandanten übergehen — dieselbe Falle, die tender-scheduler.service.ts für den DÖE-Poll-Config-Seed dokumentiert.
  • ensureDefaultGroup nutzt eine interaktive Transaktion ($transaction mit async-Callback) statt der Array-Form, weil Schritt 2 (Mitgliedschaften) und Schritt 3 (Grants) die id der in Schritt 1 erzeugten Gruppe brauchen.
  • P2002 aus group.create wird ausschließlich anhand err.code === 'P2002' abgefangen und in null übersetzt; jeder andere Fehler wird weitergeworfen. Der partielle Unique-Index Group_one_default_per_tenant (15-01) bleibt der eigentliche Durchsetzungspunkt.

Deviations from Plan

None — Plan exakt wie geschrieben umgesetzt.

Issues Encountered

Keine funktionalen Probleme. Beim ersten Testlauf für admin-seed.service.spec.ts waren zwei Testerwartungen selbst fehlerhaft formuliert (Reihenfolge-Filter schloss user.findUnique nicht aus; Aufrufzähler für den Neustart-Test berücksichtigte nicht, dass seedAdmin() beim ersten Lauf zusätzlich zur Reparatur einen eigenen ensureDefaultGroup-Aufruf auslöst) — beide Assertions korrigiert, die Implementierung war in beiden Fällen bereits korrekt.

User Setup Required

None - keine externe Konfiguration nötig.

Kein Deploy auf den Testserver aus diesem Plan heraus — das übernimmt der Benutzer laut Vorgabe. Erwarteter Effekt dort nach dem nächsten Container-Start (unbeaufsichtigter prisma migrate deploy + API-Start): der Mandant „Default" bekommt die Gruppe „Alle Benutzer" mit isDefault:true, die vier vorhandenen Benutzer werden Mitglieder, und für jedes aktive Modul entsteht ein Gruppen-Grant.

Next Phase Readiness

  • Lokaler Stack geprüft: der einzige lokale Mandant hat bereits genau eine Gruppe (docker exec tessera-ctl-db-1 psql bestätigt groups=1 für den Tenant default) — die Startup-Reparatur lässt ihn beim nächsten API-Neustart unangetastet, genau wie im behavior-Block gefordert.
  • Live-Testserver (192.168.13.12, tenants=1 users=4 groups=0) profitiert vom nächsten Container-Neustart automatisch — kein manueller Eingriff nötig, keine neue Migration.
  • Keine offenen Punkte.

Phase: quick-260805-fok Completed: 2026-08-05

Self-Check: PASSED

Alle 7 in files_modified gelisteten Dateien vorhanden, beide Task-Commits (9d1254c, 0d7d8a5) im Git-Log gefunden.