# Übergabe: Anwendung auf zwei Frameworks umbauen (ISO 27001 neben TISAX) **Stand:** 2026-08-21 · **Zielgruppe:** Entwickler:innen · **Vorarbeit:** Branch `feature/iso27001-framework-mapping`, Commit `492d315` > **Ausgangslage:** Die Inhaltsseite ist fertig. `seed/isms-vorlagenpaket-v2` trägt jetzt **zwei > Framework-Mappings** auf **einem** Dokumentensatz — `mapping.json` (VDA ISA, 321 Anforderungen) und > `mapping-iso.json` (ISO/IEC 27001:2022, 120 Anforderungen). Die Anwendung kennt das zweite Mapping > noch nicht: `parsePackageFiles` liest `mapping.json` fest verdrahtet. > > **Auftrag:** Framework als erste Klasse im Datenmodell und in der Paketauflösung, damit ein Mandant > ISO, TISAX oder beides führen kann. Danach die drei ISO-Artefakte, die es im Tool noch nicht gibt: > SoA, Kennzahlen, Managementbewertung/Korrekturmaßnahmen. Fachlicher Hintergrund und Begründung der Entscheidung: `docs/FRAMEWORK-MAPPING-ISO27001.md`. Gesamtarchitektur und die übrigen Lanes: `docs/KONZEPT-framework-iso27001.md`. --- ## 0. Was **nicht** angefasst werden muss Die Paketinhalte sind generiert. Wer ISO-Texte oder Zuordnungen ändern will, ändert `_iso_crosswalk.json` bzw. `_iso_sections.json` und lässt `python3 _generate_iso.py` laufen — **nie direkt die Markdown-Dateien**, der Generator überschreibt sentinel-begrenzte Blöcke. Danach müssen beide Prüfskripte grün sein: ```bash cd seed/isms-vorlagenpaket-v2 python3 _generate_iso.py # idempotent, zweiter Lauf ändert nichts python3 _verify.py # TISAX-Sicht python3 _verify_iso.py # ISO-Sicht python3 _render_diff.py HEAD # TISAX-Regression gegen den aktuellen Stand ``` Die Sichtbarkeit im Dokument steuern zwei Variablen aus `variables.schema.json`: `FLAG_FW_TISAX` (Default `true`) und `FLAG_FW_ISO27001` (Default `false`). ### Parallelbetrieb ist vorgesehen Sind **beide** Flags gesetzt, rendert das Dokument beide Anforderungssichten untereinander — über **einem** gemeinsamen Umsetzungstext. Genau dafür ist die Bibliothek gebaut: ``` 3.2 Sichere Anmeldung *Anforderungsbezug:* VDA ISA 4.1.2 · ISO/IEC 27001 A.8.5 **Anforderung** *Anforderungen nach VDA ISA 2027:* - **[MUSS]** Verfahren zur Benutzerauthentifizierung nach dem Stand der Technik werden angewandt. - **[SOLL]** Für privilegierte Benutzerkonten werden höherwertige Verfahren genutzt … *Anforderungen nach ISO/IEC 27001:* - **[ISO A.8.5]** Sichere Authentisierungstechnologien und -verfahren sind einzusetzen. **Umsetzung bei {{ORG_NAME}}** Die Authentifizierungsverfahren sind risikobasiert ausgewählt … Passwortvorgaben nach BL-IAM-01 … ``` Der Anforderungsbezug steht in einer eigenen Zeile unter der Überschrift und nennt je nach Betriebsart eine oder beide Normen. Die beiden Zwischenüberschriften erscheinen **nur**, wenn tatsächlich beide Frameworks aktiv sind. Die 19 ISO-only-Abschnitte kommen bei einem Doppel-Mandanten additiv hinzu. Auf der Datenseite ist der Parallelbetrieb erst nach AP1 möglich: `PolicyRequirement` trägt dann beide ID-Namensräume (`4.1.2-M1` und `A.5.15-1`) nebeneinander — vorausgesetzt, Falle 1.1 ist gelöst. --- ## 1. Die vier Fallen — bitte zuerst lesen ### 1.1 `reconcilePackage` archiviert die Anforderungen des jeweils anderen Frameworks **Das ist der kritische Punkt.** `prisma/import-policies.ts:368-371` archiviert jede `PolicyRequirement`, deren `reqId` nicht im importierten Paket steht: ```ts for (const ex of existingReqs) { if (desiredReqIds.has(ex.reqId) || ex.archivedAt) continue; report.requirements.archived++; await prisma.policyRequirement.update({ where: { id: ex.id }, data: { archivedAt: now } }); } ``` Ein ISO-Import in einen Mandanten mit TISAX archiviert damit **alle 321 VDA-ISA-Anforderungen** — und umgekehrt. Die ID-Namensräume kollidieren zwar nicht (`4.1.2-M1` vs. `A.5.15-1`, `@@unique([tenantId, reqId])` bleibt heil), aber der Abgleich muss **framework-scoped** werden: - `PolicyRequirement.framework Framework` ergänzen (Backfill `TISAX`), - den Archivierungslauf auf `where: { tenantId, framework }` einschränken. Für **Dokumente** gilt das nicht: Beide Mappings lesen dieselben `richtlinien/`- und `verfahren/`-Dateien, `pkg.documents` ist identisch. Ebenso Variablen, Baseline-Parameter und Nachweisregister — die sind geteilt und dürfen genau einmal je Mandant abgeglichen werden. ### 1.2 Zwei Unique-Constraints brechen bei zwei Frameworks | Modell | heute | muss werden | |---|---|---| | `PolicyTemplateVersion` (`prisma/schema.prisma:1639`) | `version String @unique` | `@@unique([framework, version])` — sonst kollidieren ISO 2.1 und TISAX 2.1 | | `PolicyPackageState` (`prisma/schema.prisma:1614`) | `tenantId String @unique` | `@@unique([tenantId, framework])` — sonst merkt sich ein Mandant nur eine Paketversion | ### 1.3 TISAX darf sich nicht verändern Bestandsmandanten müssen bitgenau dasselbe sehen wie heute. Der Renderdiff über alle 17 Richtlinien (TISAX-Kontext vorher/nachher) war bei der Paketumstellung **0 Abweichungen** — dieser Wert ist die Messlatte. `src/lib/policy-render.ts` belegt fehlende Framework-Flags bereits vor (`FLAG_FW_TISAX = true`, `FLAG_FW_ISO27001 = false`), damit ein Bestandsmandant vor dem Paket-Re-Import keine leeren Anforderungsblöcke sieht. **Diesen Fail-Safe nicht entfernen**, auch nicht wenn die Flags später über `TenantFramework` gesetzt werden. Nebenbei: `applyProtection` (`src/lib/policy-render.ts:54`) setzt `FLAG_HIGH_PROTECTION` bedingungslos auf `true` mit der Begründung „im TISAX-Modell stets aktiv". Für ISO-Mandanten ist das derzeit folgenlos (die ISO-Blöcke nutzen die Schutzbedarf-Flags nicht), sollte aber beim Bau der `IsoStrategy` bewusst entschieden werden. ### 1.4 Migrations- und Build-Konventionen - Migrationsflow Prisma 7 wie in `docs/HANDOVER-DEV.md:100`: `migrate diff --from-config-datasource … --to-schema … --script`, danach den **RLS-DO-Block manuell** an die `migration.sql` anhängen, dann `migrate deploy`. - Jedes neue mandantengebundene Modell gehört in **`TENANT_MODELS`** (`src/server/db.ts:81`) **und** braucht eine RLS-Policy in seiner Migration. - `scripts/check-module-guards.ts` läuft als `prebuild`-Gate: **jede neue Datei** unter `src/server/actions/` muss dort eingetragen sein (Modul-Key oder `EXEMPT`), sonst schlägt der Build fehl. - Bei paralleler Lane-Entwicklung teilen sich die Worktrees dieselbe lokale Postgres-DB. Beim Erzeugen einer Migration nur die **eigenen** DDL-Blöcke übernehmen und Fremd-Drops von Hand entfernen. - `npm run lint` und `npm run build` müssen vor jedem Commit grün sein (`AGENTS.md:25`). --- ## 2. Arbeitspakete ### AP1 — Framework-Dimension *(Fundament, blockiert alles Weitere)* **Schema** ```prisma enum Framework { ISO_27001 TISAX } model TenantFramework { id String @id @default(cuid()) tenantId String @map("tenant_id") framework Framework isPrimary Boolean @default(false) @map("is_primary") config Json? // z. B. { tisaxLevel: "AL3" } bzw. { certScope, certBodyTarget } createdAt DateTime @default(now()) @map("created_at") @@unique([tenantId, framework]) @@index([tenantId]) @@map("tenant_frameworks") } ``` Zusätzlich: `PolicyRequirement.framework`, `PolicyTemplateVersion.framework`, `PolicyPackageState.framework` (siehe Fallen 1.1 und 1.2). Alles additiv, Backfill der Bestandsdaten auf `TISAX`. **Paketauflösung** | Datei | Änderung | |---|---| | `prisma/import-policies.ts` | `parsePackageFiles(seedDir, mappingFile = "mapping.json")`; Requirements framework-scoped reconcilen | | `prisma/template-store.ts:147` | `resolvePackageForTenant(prisma, tenantId, seedDir, framework)`; `loadPublishedPackage(prisma, locale, framework)`; `getAvailableVersion` ebenso | | `scripts/sync-policy-templates.ts:23` | über **Frameworks × Sprachen** iterieren statt nur über Sprachen | Die vier Aufrufer von `resolvePackageForTenant` bekommen den Framework-Parameter durchgereicht: `src/server/provision.ts:139`, `src/server/actions/policy-package.ts:28`, `src/server/actions/admin.ts`, `src/app/(app)/policies/updates/page.tsx:44`. **Das Seed-Verzeichnis bleibt für beide Frameworks dasselbe** — die fünf `SEED_DIR`-Konstanten ändern sich nicht, nur der Mapping-Dateiname. Das ist der Vorteil von Variante A. **DoD:** Bestandsmandanten laufen unverändert als TISAX; ein Mandant kann mit `frameworks: ["ISO_27001"]`, `["TISAX"]` oder beiden provisioniert werden; bei Doppel-Framework koexistieren 321 + 120 Anforderungen und **keine** ist fälschlich archiviert. --- ### AP2 — Provisionierung und Flags *(klein, direkt nach AP1)* `ProvisionOpts` (`src/server/provision.ts:37`) um `frameworks: Framework[]` erweitern. `provisionTenant` schreibt die `TenantFramework`-Zeilen, importiert **je Framework** das passende Mapping und setzt die Sichtbarkeits-Flags als `PolicyVariable`: | Mandant führt | `FLAG_FW_TISAX` | `FLAG_FW_ISO27001` | |---|:--:|:--:| | nur TISAX | `true` | `false` | | nur ISO | `false` | `true` | | beides | `true` | `true` | **Achtung Reihenfolge:** `reconcilePackage` erhält nutzergepflegte Variablenwerte und überschreibt sie nicht. Die Flags müssen also **nach** dem Import gesetzt werden, sonst bleibt der Schema-Default stehen und ein ISO-Mandant sieht die VDA-ISA-Sicht. `tisaxLevel` bleibt vorerst auf `TenantSettings`, wird aber als TISAX-scoped dokumentiert (Entscheidung D2). Die AL-Flags dürfen bei einem reinen ISO-Mandanten nicht gesetzt werden. **DoD:** Ein frisch provisionierter ISO-Mandant öffnet `/policies` und sieht in jedem Dokument die ISO-Anforderungssicht plus die 19 ISO-only-Abschnitte; keine „ISA"-Klammern in den Überschriften. --- ### AP3 — SoA-Modul *(das fehlende ISO-Kernartefakt)* Heute ist der Modul-Key `soa` (`src/lib/modules.ts:19`) mit `href: "/soa"` registriert, **die Route existiert aber nicht** — die Logik liegt als Wizard-Schritt 7 in `src/server/actions/soa.ts` und ist ein VDA-ISA-Reifegrad-Assessment (0–3), nicht die ISO-Anwendbarkeitserklärung. ```prisma model SoaEntry { id String @id @default(cuid()) tenantId String @map("tenant_id") framework Framework control String // "A.5.15" applicable Boolean @default(true) justification String // Begründung Einbeziehung ODER Ausschluss source String? // Risiko-ID / gesetzliche / vertragliche Anforderung implementationStatus String @default("geplant") // umgesetzt | teilweise | geplant ownerId String? @map("owner_id") policyCode String? @map("policy_code") evidenceId String? @map("evidence_id") @@unique([tenantId, framework, control]) @@index([tenantId]) @@map("soa_entries") } ``` Die vier Felder `applicable`, `justification`, `implementationStatus` und die Ausschlussbegründung sind **normative Pflichtangaben** (ISO/IEC 27001:2022, 6.1.3 d) — ohne sie ist die SoA im Zertifizierungsaudit angreifbar. Vorbefüllung aus `mapping-iso.json`: 93 Controls; das Feld `condition` (z. B. `FLAG_DEV_INHOUSE`) steuert die Default-Anwendbarkeit. Als fachliche Vorlage für Aufbau und Spalten dient `seed/isms-vorlagenpaket-v2/Statement-of-Applicability-ISO.md`. **DoD:** Ein ISO-Mandant kann die SoA vollständig pflegen und als PDF/XLSX exportieren; ein Control ohne Begründung wird als unvollständig markiert. --- ### AP4 — Managementklauseln: Kennzahlen, Bewertung, Korrekturmaßnahmen Drei Modelle fehlen; alle drei sind ISO-Pflichtthemen und heute nicht abbildbar: | Klausel | Modell | Inhalt | |---|---|---| | 9.1 | `Kpi` / `KpiValue` | Kennzahl, Datenquelle, Zielwert, Turnus, Verantwortlicher, Messwerte je Periode | | 9.3 | `ManagementReview` | Datum, Eingaben nach 9.3.2, Ergebnisse nach 9.3.3, Beschlüsse mit Verantwortlichem und Termin | | 10.2 | `Nonconformity` + `CorrectiveAction` | Herkunft, Sofortkorrektur, Ursachenanalyse, Maßnahme, Wirksamkeitsbewertung | Vorhandene Bausteine, auf denen das aufsetzen kann: `Task.recurrence` (RRULE), `Task.remindAt`, `Task.effectiveUntil` (Wirksamkeitsintervall, gekoppelt an `Evidence.validUntil`), `TaskParticipant` (RACI) und `AuditLog`. Die Datenquellen für die Kennzahlen liegen bereits im Tool: Aufgabenfristen und Überfälligkeit, Incident-SLA und Meldefristen (`src/lib/incident-deadlines.ts`), Reifegrade je Control, Maßnahmenstatus. Fachlicher Inhalt der Abschnitte steht in R03 der Bibliothek (`ISO-MS-MESSUNG`, `ISO-MS-MGMTREVIEW`, `ISO-MS-CAPA`) — die Modelle sollten die dort beschriebenen Felder tragen. **DoD:** Kennzahlenblatt mit Zielwerten pflegbar und über zwei Perioden auswertbar; Management-Review entlang der 9.3.2-Agenda protokollierbar; ein Maßnahmenfall inklusive dokumentierter Wirksamkeitsprüfung abschließbar. --- ### AP5 — Dokumentenlenkung *(klein, hohe Auditwirkung)* - `PolicyDocument.reviewCycle` und `nextReviewAt` — heute führt nur `ManagedRegister` einen `reviewCycle`; A.5.1 verlangt die Überprüfung „in geplanten Abständen". Behelfsweise über `Task.recurrence` möglich, sauberer am Dokument. - `PolicyAcknowledgement { tenantId, policyDocumentId, version, userId, acknowledgedAt }` — die Lesebestätigung ist in `SPEC.md` §4.6 vorgesehen und fehlt. Sie ist zugleich der einfachste Nachweis für Klausel 7.3 und Control A.6.3. - Änderungshistorie je Dokumentversion — im Entwicklungsstand als offen geführt. Kann aus `AuditLog` (Vorher/Nachher) abgeleitet oder als eigene Tabelle geführt werden. **DoD:** Übersicht „Prüfung fällig" im Tool; Auswertung der Lesebestätigungen je Richtlinienversion; Historie eines Dokuments über mindestens zwei Versionen sichtbar. --- ## 3. Reihenfolge und Aufwand ``` AP1 Framework-Dimension ██████ 4–6 PT ← blockiert alles ├─ AP2 Provisionierung ██ 1–2 PT ├─ AP3 SoA-Modul ██████ 5–8 PT ├─ AP4 Managementkl. ██████ 5–8 PT └─ AP5 Dok.-Lenkung ███ 2–3 PT ``` AP3, AP4 und AP5 sind nach AP1 parallelisierbar. Die Schätzung entspricht den Lanes 1, 4 und 5 aus `KONZEPT-framework-iso27001.md`; der inhaltliche Teil von Lane 2 (ISO-Mapping und -Texte) ist erledigt und entfällt. **Feature-Flag:** ISO bleibt laut Entscheidung D7 hinter einem Plattform-Schalter, bis AP3 abgenommen ist. --- ## 4. Abnahme | Prüfung | Erwartung | |---|---| | `python3 _verify.py` und `_verify_iso.py` | beide `OK` | | `python3 _render_diff.py ` (TISAX-Sicht) | 0 Abweichungen — Skript liegt im Paket bei. **Vergleichsstand ist der Kopf dieses Branches, nicht `a9649b3`**: der Anforderungsbezug ist dort bewusst aus der Überschrift in eine eigene Zeile gewandert (14 von 17 Richtlinien betroffen, ausschließlich diese Zeile — der Anforderungs- und Umsetzungstext ist unverändert). | | `python3 _render_diff.py --framework BEIDE` | Parallelbetrieb prüfbar | | `npx tsx scripts/test-framework-dryrun.ts` | alle Prüfungen bestanden — Trockenlauf ohne Schreibzugriff über Paketebene und alle Mandanten der lokalen DB | | Bestandsmandant (TISAX) nach Deploy | Readiness und Exporte identisch zum Snapshot vor dem Umbau | | Import ISO in Mandant mit TISAX | 120 neue Anforderungen, **0 archivierte** ISA-Anforderungen | | Import ISO, Umsetzungstexte | 120 von 120 gefüllt (0 leer) | | Dokumente bei Doppel-Framework | 39 Dokumente, **nicht** doppelt | | `npm run lint`, `npx tsc --noEmit`, `npm run build` | grün | Der Importer lässt sich ohne Datenbank gegen beide Mappings prüfen — `parsePackageFiles` ist reine Dateiarbeit und liefert `documents`, `requirements`, `variables`, `baseline`, `evidence` als `ParsedPackage`. Ein Trockenlauf des Mandanten-Imports geht über `reconcilePackage(..., { dryRun: true })`; er erzeugt den Änderungsreport ohne Schreibzugriff und eignet sich als Freigabebedingung. --- ## 5. Offene Punkte auf der Paketseite (nicht Entwicklung) Diese Punkte gehören dem ISB bzw. der Redaktion, nicht dem Entwicklungsteam — hier nur zur Abgrenzung: - Nachweisregister um Zeilen für Kennzahlenblatt, Management-Review-Protokoll und Maßnahmenregister ergänzen. - VA-15 trennen (internes Audit vs. Managementbewertung) und ein Verfahren für Korrekturmaßnahmen ergänzen — **Nummer ab VA-21**, VA-20 ist belegt. - `REVIEW_CYCLE` wird an 33 Bestandsstellen für fünf verschiedene Zyklen verwendet; die getrennten Variablen (`POLICY_REVIEW_CYCLE`, `MGMT_REVIEW_CYCLE`, `RISK_REVIEW_CYCLE`) greifen bisher nur in den neuen ISO-Abschnitten. - ISB-Freigabe der 19 neuen Abschnittstexte und Review des Crosswalks.