# Implementierungsfaden — TISAX-Onboarding-Wizard Neustruktur > Status: Entwurf · Grundlage: Konzept v2 (4 Ebenen, nach TISAX-Fachprüfung) + IST-Analyse des Task-Moduls (Branch `dev-tasks-kanban`, `ce1e847`). > Ziel: Aus dem linearen 9-Schritt-Monolithen `/onboarding` werden vier Ebenen — **Fundament → Strukturanalyse → Cockpit → Audit** — informationszentriert, prozessgeführt erhoben, bereichsbasiert umgesetzt. --- ## 0. Ausgangslage & Auswirkung der Kanban-Anpassung Das Task-Modul wurde zwischenzeitlich auf ein **Kanban-Board** umgestellt (`src/components/kanban-board.tsx`, `src/app/(app)/tasks/page.tsx`, Action `updateTaskStatus`, Status `IN_PROGRESS`). Das ist die Basis für Ebene 3 — das Bereichs-Board wird **additiv** darauf gebaut, nicht neu. **Bereits vorhanden (nutzbar):** generisches Task-Modell, Kanban mit DnD-Statuswechsel, `assigneeId` + `createdById` + Vier-Augen-Freigabe, PROPOSED→OPEN/DISCARDED-Vorschlagsmechanik, Wizard-Trigger (`task-triggers.ts`, `proposeTasksFromTriggers`), polymorphe `links`-Json + harte `entityType/entityId`-Kopplung, Teil-Sync Task→Objekt bei Policy-Freigabe (`approveTask`). **Fehlt (dieser Plan liefert es):** `domain`/Bereich, RACI/Mitwirkende, `orderIdx`, Recurrence/Wiedervorlage, Auto-Completion-Deckel, Bereichs-/Personen-Filter, harte Control-/Evidence-Verknüpfung, Information als Kernobjekt. **Altlasten (Vorarbeit M0):** - `TASK_STATUSES` in `src/lib/tasks.ts` enthält `IN_PROGRESS` nicht, obwohl Board/Actions ihn nutzen → Inkonsistenz beheben. - `src/app/(app)/tasks/page.tsx:166` referenziert `open` — im File nicht definiert (nur `myOpen`/`active`/`proposals`/`terminal`). Verifizieren & fixen. --- ## 1. Datenmodell (Prisma-Migrationen) Konventionen wie im Bestand: `cuid()`-IDs, `tenantId`, `@@map(snake_case)`, `@@unique([tenantId, …])`. Enums additiv; freie `String`-Statusfelder brauchen keine Migration. ### 1.1 Werte-Modell: Information IST ein (primärer) Asset — kein Extra-Objekt **Korrektur ggü. erstem Entwurf.** Ein Informationswert ist kein neues Objekt neben dem Asset, sondern ein **primärer Asset** (ISO 27005 / TISAX: primäre Werte = Informationen + Prozesse; sekundäre = Träger). Euer Schema bildet das bereits ab: - `AssetType.INFORMATION` / `DATA` = primäre Werte; `SYSTEM/APPLICATION/LOCATION/SUPPLIER/IT_SERVICE/SOFTWARE/PERSON` = sekundär/Träger. - `ProcessAsset.role = PRIMARY | SECONDARY` trennt primär/sekundär je Prozess-Beziehung. - `AssetRelation` bildet Träger-Abhängigkeiten ab; `confidentiality/integrity/availability` liegen schon am `Asset`. → **Kein** `InformationValue`/`ProcessInformation`/`InformationAsset`. Stattdessen kleine Ergänzungen am Bestand: ```prisma enum InfoLabel { NONE INFO_HIGH INFO_VERY_HIGH PROTOTYPE PERSONAL_DATA } model Asset { // … Bestand (type, C/I/A, ownerId, status, tags) … normalizedName String? // Dedup-Schlüssel (s. 2.1) label InfoLabel @default(NONE) // Info hoch/sehr hoch, Prototyp, personenbezogen → steuert Scope/Bereiche @@unique([tenantId, normalizedName]) // verhindert Exakt-Duplikate (v.a. type INFORMATION/DATA) } ``` **Primär/sekundär** bleibt über `ProcessAsset.role`. Optional als intrinsische Klassifikation `Asset.tier (PRIMARY|SUPPORTING)`, ableitbar aus `type` — nur einführen, falls die Rolle je Prozess nicht ausreicht (s. offene Entscheidung 4.1). **Schutzbedarf:** bleibt am `Asset`. Primäre Informations-Assets tragen den echten C/I/A-Wert; sekundäre erben per **Maximum** der von ihnen getragenen primären Assets. **„Erhebung über den Prozess":** Im Prozess-Schritt werden primäre Informations-Assets erfasst (Dedup/Autocomplete gegen bestehende Assets, 2.1), dann Träger-Assets via `ProcessAsset(SECONDARY)` + `AssetRelation` verknüpft. Anker = das primäre Informations-Asset. **Keine Datenmigration nötig** — nur additive Spalten. ### 1.2 Standard-Prozess-Katalog (Ebene 2) ```prisma model ProcessCatalogEntry { // global, kein tenantId id String @id @default(cuid()) code String @unique name String category ProcessCategory // CORE | MANAGEMENT | SUPPORT (Neben→SUPPORT) suggestedAssetTypes AssetType[] suggestedRiskCodes String[] // → RiskCatalogEntry.code suggestedInfoLabels InfoLabel[] @@map("process_catalog") } ``` ### 1.3 Team / Funktionszuordnung (Ebene 1) Heute sind ISMS-Rollen read-only Variablen. Neu: echte Zuordnung Funktion→User(n) inkl. „unbesetzt". ```prisma model ProjectFunctionAssignment { id String @id @default(cuid()) tenantId String functionKey String // ISB, PM, HR_LEAD, IT_LEAD, BCM, AUDITOR_INT, DPO … userId String? // null = unbesetzt → erzeugt Task „Funktion besetzen" domain Domain? // Default-Bereich dieser Funktion (Sichtbarkeit) invitedEmail String? // Einladung, falls Account noch nicht existiert createdAt DateTime @default(now()) @@index([tenantId, functionKey]) @@map("project_function_assignments") } ``` ### 1.4 Bereich (Domain) & Control→RACI-Mapping (Ebene 3) ```prisma enum Domain { GOVERNANCE HR PHYSICAL BCM IT PROCUREMENT COMPLIANCE DATA_PROTECTION PROTOTYPE } enum RaciKind { RESPONSIBLE ACCOUNTABLE CONSULTED INFORMED } model ControlDomainMap { // global default; tenant-Override via tenantId id String @id @default(cuid()) tenantId String? // null = globaler Default control String // "3.1.4" oder Kapitel-Präfix "3" domain Domain raci RaciKind @default(RESPONSIBLE) functionKey String? // Default-Verantwortliche Funktion @@map("control_domain_map") } ``` Seed-Defaults (Kapitel → Bereich), fachlich korrigiert: | Kapitel | Domain | Anmerkung | |---|---|---| | 1 | GOVERNANCE | ISB + Management | | 2 | HR | | | 3 (phys.) | PHYSICAL | | | 3 (BCM) | BCM | **aus K3 gelöst** — eigener Bereich | | 4 IAM | IT + **HR (CONSULTED)** | Joiner-Mover-Leaver | | 5 | IT | | | 6 | PROCUREMENT + **ISB/Recht (CONSULTED)** | | | 7 | COMPLIANCE + **DPO/IT (CONSULTED)** | | | Prototyp | PROTOTYPE | nur bei Label | | Datenschutz | DATA_PROTECTION | nur bei Label | ### 1.5 Task-Erweiterungen (Ebene 3) ```prisma model Task { // … Bestand … domain Domain? // Bereich (Default aus ControlDomainMap) orderIdx Int @default(0) // manuelle Sortierung je Bereich recurrence String? // ISO-8601-Dauer/RRULE, z.B. "P1Y" remindAt DateTime? effectiveUntil DateTime? // Wirksamkeitsintervall (Wiedervorlage) participants TaskParticipant[] @@index([tenantId, domain, status]) } model TaskParticipant { // RACI zusätzlich zu assigneeId (=primär Responsible) id String @id @default(cuid()) tenantId String taskId String userId String raci RaciKind @@unique([tenantId, taskId, userId, raci]) @@map("task_participants") } ``` ### 1.6 Nachweis-/Evidence-Register (Ebene 3+4) ```prisma model Evidence { id String @id @default(cuid()) tenantId String title String kind String // record | protocol | screenshot | export fileRef String? taskId String? control String? validFrom DateTime? validUntil DateTime? // koppelt an Task.effectiveUntil createdAt DateTime @default(now()) @@index([tenantId, control]) @@map("evidence") } ``` --- ## 2. Querschnitts-Bausteine ### 2.1 Dedup-Erkennung & Autovervollständigung für Informationswerte Kernanforderung: Menschen erfassen über den Prozess, sollen aber denselben Wert nicht doppelt anlegen (auch nicht bei abweichender Groß-/Kleinschreibung). - **Normalisierung → `normalizedName`:** `trim` → Mehrfach-Whitespace kollabieren → Unicode-NFC → `toLowerCase` (locale-aware `de`) → Umlaut-Faltung (ä→ae, ö→oe, ü→ue, ß→ss) → Satzzeichen entfernen. Ergebnis ist der `@@unique`-Schlüssel. - Beispiel: „Kundendaten", „kundendaten", „ Kunden­daten " → alle `kundendaten`. - **Exakt-Duplikate:** durch `@@unique([tenantId, normalizedName])` unmöglich; bei Kollision wird das bestehende Asset *verknüpft* (`ProcessAsset`) statt neu angelegt. - **Autovervollständigung:** As-you-type-Suche im Server (`searchAssets(query)`, gefiltert auf `type INFORMATION/DATA`) gegen (a) das Tenant-Asset-Register und (b) globale Katalog-Vorschläge. Treffer zeigen „bereits erfasst in Prozess X" → ein Klick verknüpft. - **Fuzzy-Nah-Duplikate (weiche Warnung):** Trigramm-/Levenshtein-Ähnlichkeit auf `normalizedName`; ab Schwelle „Meintest du *Kundendaten*?" **vor** dem Anlegen. Bei Postgres optional `pg_trgm` (`similarity()`), sonst in-app Levenshtein auf der Kandidatenliste. - **UI:** Combobox (bestehendes `components.json`/shadcn-Setup) mit Vorschlagsliste, Badge „neu" vs. „verknüpfen". ### 2.2 Auto-Completion-Engine — gedeckelt Zentrale Funktion `syncTaskFromObject(entityType, entityId)`, aufgerufen bei Statuswechsel von Policy/Risk/Control/Asset. Fachprüfungs-Regel: **Existenz ≠ Wirksamkeit.** | Auslöser | Task geht auf … | Deckel | |---|---|---| | `PolicyDocument.status = FREIGEGEBEN` | `DONE` (dokumentiert) | Reifegrad 3 / audit-ready nur mit verknüpftem `Evidence` + Vier-Augen | | Asset C/I/A + Owner gesetzt | `DONE` | — | | `Risk.status = ACCEPTED/CLOSED` | `DONE` | Restrisiko gesetzt | | `ControlImplementation.status = erledigt` | `DONE` (umgesetzt) | Reifegrad-Anhebung getrennt bestätigen | - Kein Auto-`DONE` ohne mindestens dokumentierten Zustand; „audit-ready" ist ein **separater, manueller** Schritt mit Nachweis. - **Wiedervorlage:** Tasks mit `recurrence` erzeugen bei `DONE` automatisch eine Folge-Task mit neuem `dueDate`/`effectiveUntil` (Serienlogik). ### 2.3 Bereichs-Sichtbarkeit & RACI - Default-Sicht: **eigene Bereiche** (aus `ProjectFunctionAssignment.domain` des Users) + eigene/zugewiesene Tasks + Pool. - **PM + ISB:** `task:read_all` (existiert bereits) → alle Bereiche. - Gezielte Mitwirkung: Eintrag in `TaskParticipant` (CONSULTED/INFORMED) macht Task für die Person sichtbar, ohne ihr den ganzen Bereich zu öffnen. - Kanban erhält einen **Bereichs-Selektor** (Lane-Filter) zusätzlich zu den Status-Spalten; Sortierung je Bereich nach `orderIdx`, dann Priorität. --- ## 3. Meilenstein-Faden (jeder Schritt einzeln lauffähig) ### M0 · Vorarbeit & Bereinigung - `TASK_STATUSES` um `IN_PROGRESS` ergänzen (`src/lib/tasks.ts`); `open`-Bug in `tasks/page.tsx:166` verifizieren/fixen. - Enums `Domain`, `RaciKind`, `InfoLabel`, `InfoProcessRole` + Task-Felder `domain`/`orderIdx` (nullable) migrieren — noch ohne UI-Wirkung. - *Risiko: minimal. Kein Verhaltensbruch.* ### M1 · Ebene 1 „Fundament" (Wizard-Umbau Teil 1) - Onboarding-Steps in `src/lib/onboarding/register-steps.ts` neu ordnen/ergänzen: `context` → `scope` (einfrieren) → `policy` (Leitlinie, verlinkt Richtlinien-Modul) → `roles`(Team) → `criteria` (Risikoakzeptanz + C/I/A-Skala). - **Team-Entität** `ProjectFunctionAssignment` + Zuweisen/Einladen-Flow (Schritt `roles`). Neue Funktionen: BCM, unabhängiger interner Auditor, DPO, Asset-/Risk-Owner-Kennzeichnung. - Leitlinie als Fundament-Artefakt (nicht als Cockpit-Task). - *Betroffen: `src/app/(app)/onboarding/steps/*`, `src/server/roles.ts`, `src/server/actions/onboarding*.ts`.* ### M2 · Ebene 2 „Strukturanalyse" (Information = primärer Asset) - Additive `Asset`-Spalten (`normalizedName`, `label`) + `ProcessCatalogEntry` migrieren. **Keine** Datenmigration — Bestand bleibt gültig. - Wizard-Fluss **prozessgeführt**: Prozesse wählen (Katalog) → je Prozess primäre Informations-Assets erfassen (Combobox mit Dedup/Autocomplete, 2.1) → Träger-Assets (`ProcessAsset SECONDARY`: System/Anwendung/Raum/Person/Dienstleister/Standort) → Schutzbedarf am Asset (Maximum/Kumulation/Verteilung) → Risiken aus Katalog. - *Betroffen: neue Steps `processes`/`assets`/`protection`/`risks`, `RiskCatalogEntry`-Matching (existiert).* ### M3 · Ebene 3 „Cockpit" auf bestehendem Kanban - `ControlDomainMap` seeden (Tabelle 1.4); Task-Erzeugung (`soa.ts`, `task-triggers.ts`, `gap.ts`) setzt `domain` + Default-RACI. - Kanban erweitern: Bereichs-Lane/-Filter + Personen-Filter; Default „meine Bereiche"; `orderIdx`-Sortierung (DnD persistiert Reihenfolge). - `TaskParticipant` (RACI) + Sichtbarkeitslogik (2.3). - **Auto-Completion-Engine** (2.2) + Recurrence/Wiedervorlage; `Evidence`-Register. - Fragebogen (`context`) auf Bereiche verteilen → Fragen erzeugen Tasks im jeweiligen Bereich. - *Betroffen: `kanban-board.tsx`, `tasks/page.tsx`, `src/server/actions/tasks.ts`, `soa.ts`, `policies.ts` (Sync-Hook).* ### M4 · Ebene 4 „Audit-Wizard" abtrennen - Neuer, aktivierbarer Wizard aus heutigen Steps `gap` + `readiness`; **internes Audit** (unabhängig, AL3-Pflicht) als eigener Schritt ≠ Readiness-Snapshot. - Reifegrad + Nachweise laufen bereits kontinuierlich (M3) → hier nur Konsolidierung + Management-Review. - *Betroffen: neue Route `/audit-readiness` o. ä., Auslagern aus `onboarding/register-steps.ts`.* --- ## 4. Offene Entscheidungen 1. **Primär/sekundär — intrinsisch oder relational?** Reicht `ProcessAsset.role` (Rolle je Prozess), oder soll ein intrinsisches `Asset.tier (PRIMARY|SUPPORTING)` als Wahrheit ergänzt werden (ableitbar aus `type`)? → Vorschlag: mit `role` starten, `tier` nur bei Bedarf. *(C/I/A-Ablageort ist entschieden: bleibt am `Asset`, sekundär erbt per Maximum.)* 2. **Fuzzy-Matching-Technik:** `pg_trgm` (DB-nah, schnell) vs. in-app Levenshtein (DB-agnostisch). → Abhängig davon, ob Postgres-Extension im Coolify-Deployment verfügbar ist. 3. **RACI-Tiefe:** volle RACI oder zunächst nur Responsible + Consulted (Sichtbarkeit)? → Vorschlag: klein starten (R + C), A = ISB/PM implizit. 4. **Audit-Wizard-Aktivierung:** manuell vs. ab Coverage-Schwelle. --- ## 5. Reihenfolge-Logik (warum dieser Faden) M0 legt risikolos das Datenfundament. M1–M2 bauen die *sequenziellen* Ebenen (Setup/Strukturanalyse), auf denen alles Weitere aufsetzt — inkl. der informationszentrierten Struktur, dem größten Modell-Hebel. M3 nutzt maximal das bereits vorhandene Kanban (additiv statt Neubau). M4 ist die saubere Abtrennung am Ende. Jeder Meilenstein ist für sich lauffähig und liefert sichtbaren Nutzen.