Files
craftvia/docs/IMPL-tisax-wizard-neustruktur.md
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

239 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.