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

14 KiB
Raw Permalink Blame History

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:

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)

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".

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)

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)

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)

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.