import type { ComplianceDepthLevel, ComplianceScores, ScopeProfilingAnswer, ScopeDecision, HardTriggerRule, TriggeredHardTrigger, RequiredDocument, RiskFlag, ScopeGap, NextAction, ScopeReasoning, ScopeDocumentType, DocumentScopeRequirement, } from './compliance-scope-types' import type { CompanyProfile, MachineBuilderProfile } from './types' import { getDepthLevelNumeric, depthLevelFromNumeric, maxDepthLevel, createEmptyScopeDecision, DOCUMENT_SCOPE_MATRIX, DOCUMENT_TYPE_LABELS, DOCUMENT_SDK_STEP_MAP, } from './compliance-scope-types' // ============================================================================ // SCORE WEIGHTS PRO FRAGE // ============================================================================ export const QUESTION_SCORE_WEIGHTS: Record< string, { risk: number; complexity: number; assurance: number } > = { // Organisationsprofil (6 Fragen) org_employee_count: { risk: 3, complexity: 5, assurance: 4 }, org_industry: { risk: 6, complexity: 4, assurance: 5 }, org_business_model: { risk: 5, complexity: 3, assurance: 4 }, org_customer_count: { risk: 4, complexity: 6, assurance: 5 }, org_cert_target: { risk: 2, complexity: 8, assurance: 9 }, org_has_dpo: { risk: 7, complexity: 2, assurance: 8 }, // Datenarten (5 Fragen) data_art9: { risk: 10, complexity: 7, assurance: 9 }, data_minors: { risk: 10, complexity: 6, assurance: 9 }, data_volume: { risk: 6, complexity: 7, assurance: 6 }, data_retention_years: { risk: 5, complexity: 4, assurance: 5 }, data_sources: { risk: 4, complexity: 5, assurance: 4 }, // Verarbeitungszwecke (9 Fragen) proc_adm_scoring: { risk: 9, complexity: 7, assurance: 8 }, proc_ai_usage: { risk: 8, complexity: 8, assurance: 8 }, proc_video_surveillance: { risk: 7, complexity: 5, assurance: 7 }, proc_employee_monitoring: { risk: 7, complexity: 5, assurance: 7 }, proc_tracking: { risk: 6, complexity: 4, assurance: 6 }, proc_dsar_process: { risk: 8, complexity: 6, assurance: 8 }, proc_deletion_concept: { risk: 7, complexity: 5, assurance: 7 }, proc_incident_response: { risk: 9, complexity: 6, assurance: 9 }, proc_regular_audits: { risk: 5, complexity: 7, assurance: 8 }, // Technik (7 Fragen) tech_hosting_location: { risk: 7, complexity: 5, assurance: 7 }, tech_third_country: { risk: 8, complexity: 6, assurance: 8 }, tech_encryption_transit: { risk: 8, complexity: 4, assurance: 8 }, tech_encryption_rest: { risk: 8, complexity: 4, assurance: 8 }, tech_access_control: { risk: 7, complexity: 5, assurance: 7 }, tech_logging: { risk: 6, complexity: 5, assurance: 7 }, tech_backup_recovery: { risk: 6, complexity: 5, assurance: 7 }, // Produkt/Features (5 Fragen) prod_webshop: { risk: 5, complexity: 4, assurance: 5 }, prod_data_broker: { risk: 9, complexity: 7, assurance: 8 }, prod_api_external: { risk: 6, complexity: 5, assurance: 6 }, prod_consent_management: { risk: 7, complexity: 5, assurance: 8 }, prod_data_portability: { risk: 4, complexity: 5, assurance: 5 }, // Compliance Reife (3 Fragen) comp_training: { risk: 5, complexity: 4, assurance: 7 }, comp_vendor_management: { risk: 6, complexity: 6, assurance: 7 }, comp_documentation_level: { risk: 6, complexity: 7, assurance: 8 }, } // ============================================================================ // ANSWER MULTIPLIERS FÜR SINGLE-CHOICE FRAGEN // ============================================================================ export const ANSWER_MULTIPLIERS: Record> = { org_employee_count: { '1-9': 0.1, '10-49': 0.3, '50-249': 0.5, '250-999': 0.7, '1000+': 1.0, }, org_industry: { tech: 0.4, finance: 0.8, healthcare: 0.9, public: 0.7, retail: 0.5, education: 0.6, other: 0.3, }, org_business_model: { b2b: 0.4, b2c: 0.7, b2b2c: 0.6, internal: 0.3, }, org_customer_count: { '0-100': 0.1, '100-1000': 0.2, '1000-10000': 0.4, '10000-100000': 0.7, '100000+': 1.0, }, data_volume: { '<1000': 0.1, '1000-10000': 0.2, '10000-100000': 0.4, '100000-1000000': 0.7, '>1000000': 1.0, }, data_retention_years: { '<1': 0.2, '1-3': 0.4, '3-5': 0.6, '5-10': 0.8, '>10': 1.0, }, tech_hosting_location: { eu: 0.2, eu_us_adequacy: 0.4, us_adequacy: 0.6, drittland: 1.0, }, tech_access_control: { none: 1.0, basic: 0.6, rbac: 0.3, advanced: 0.1, }, tech_logging: { none: 1.0, basic: 0.6, comprehensive: 0.2, }, tech_backup_recovery: { none: 1.0, basic: 0.5, tested: 0.2, }, comp_documentation_level: { none: 1.0, basic: 0.6, structured: 0.3, comprehensive: 0.1, }, } // ============================================================================ // 50 HARD TRIGGER RULES // ============================================================================ export const HARD_TRIGGER_RULES: HardTriggerRule[] = [ // ========== A: Art. 9 Besondere Kategorien (9 rules) ========== { id: 'HT-A01', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'gesundheit', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung von Gesundheitsdaten', }, { id: 'HT-A02', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'biometrie', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung biometrischer Daten zur eindeutigen Identifizierung', }, { id: 'HT-A03', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'genetik', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung genetischer Daten', }, { id: 'HT-A04', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'politisch', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung politischer Meinungen', }, { id: 'HT-A05', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'religion', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung religiöser oder weltanschaulicher Überzeugungen', }, { id: 'HT-A06', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'gewerkschaft', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung von Gewerkschaftszugehörigkeit', }, { id: 'HT-A07', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'sexualleben', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung von Daten zum Sexualleben oder zur sexuellen Orientierung', }, { id: 'HT-A08', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'strafrechtlich', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 10 DSGVO', description: 'Verarbeitung strafrechtlicher Verurteilungen', }, { id: 'HT-A09', category: 'art9', questionId: 'data_art9', condition: 'CONTAINS', conditionValue: 'ethnisch', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 Abs. 1 DSGVO', description: 'Verarbeitung der rassischen oder ethnischen Herkunft', }, // ========== B: Vulnerable Gruppen (3 rules) ========== { id: 'HT-B01', category: 'vulnerable', questionId: 'data_minors', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'], legalReference: 'Art. 8 DSGVO', description: 'Verarbeitung von Daten Minderjähriger', }, { id: 'HT-B02', category: 'vulnerable', questionId: 'data_minors', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L4', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'], legalReference: 'Art. 8 + Art. 9 DSGVO', description: 'Verarbeitung besonderer Kategorien von Daten Minderjähriger', combineWithArt9: true, }, { id: 'HT-B03', category: 'vulnerable', questionId: 'data_minors', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L4', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'], legalReference: 'Art. 8 DSGVO + AI Act', description: 'KI-gestützte Verarbeitung von Daten Minderjähriger', combineWithAI: true, }, // ========== C: ADM/KI (6 rules) ========== { id: 'HT-C01', category: 'adm', questionId: 'proc_adm_scoring', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 22 DSGVO', description: 'Automatisierte Einzelentscheidung mit Rechtswirkung oder erheblicher Beeinträchtigung', }, { id: 'HT-C02', category: 'adm', questionId: 'proc_ai_usage', condition: 'CONTAINS', conditionValue: 'autonom', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'], legalReference: 'Art. 22 DSGVO + AI Act', description: 'Autonome KI-Systeme mit Entscheidungsbefugnis', }, { id: 'HT-C03', category: 'adm', questionId: 'proc_ai_usage', condition: 'CONTAINS', conditionValue: 'scoring', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'Art. 22 DSGVO', description: 'KI-gestütztes Scoring', }, { id: 'HT-C04', category: 'adm', questionId: 'proc_ai_usage', condition: 'CONTAINS', conditionValue: 'profiling', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 22 DSGVO', description: 'KI-gestütztes Profiling mit erheblicher Wirkung', }, { id: 'HT-C05', category: 'adm', questionId: 'proc_ai_usage', condition: 'CONTAINS', conditionValue: 'generativ', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'AI_ACT_DOKU'], legalReference: 'AI Act', description: 'Generative KI-Systeme', }, { id: 'HT-C06', category: 'adm', questionId: 'proc_ai_usage', condition: 'CONTAINS', conditionValue: 'chatbot', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'AI_ACT_DOKU'], legalReference: 'AI Act', description: 'Chatbots mit Personendatenverarbeitung', }, // ========== D: Überwachung (5 rules) ========== { id: 'HT-D01', category: 'surveillance', questionId: 'proc_video_surveillance', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'DSE'], legalReference: 'Art. 6 DSGVO', description: 'Videoüberwachung', }, { id: 'HT-D02', category: 'surveillance', questionId: 'proc_employee_monitoring', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 88 DSGVO + BetrVG', description: 'Mitarbeiterüberwachung', }, { id: 'HT-D03', category: 'surveillance', questionId: 'proc_tracking', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'COOKIE_BANNER', 'EINWILLIGUNGEN'], legalReference: 'Art. 6 DSGVO + ePrivacy', description: 'Online-Tracking', }, { id: 'HT-D04', category: 'surveillance', questionId: 'proc_video_surveillance', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 35 Abs. 3 DSGVO', description: 'Videoüberwachung kombiniert mit Mitarbeitermonitoring', combineWithEmployeeMonitoring: true, }, { id: 'HT-D05', category: 'surveillance', questionId: 'proc_video_surveillance', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 35 Abs. 3 DSGVO', description: 'Videoüberwachung kombiniert mit automatisierter Bewertung', combineWithADM: true, }, // ========== E: Drittland (5 rules) ========== { id: 'HT-E01', category: 'third_country', questionId: 'tech_third_country', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TRANSFER_DOKU'], legalReference: 'Art. 44 ff. DSGVO', description: 'Datenübermittlung in Drittland', }, { id: 'HT-E02', category: 'third_country', questionId: 'tech_hosting_location', condition: 'EQUALS', conditionValue: 'drittland', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU'], legalReference: 'Art. 44 ff. DSGVO', description: 'Hosting in Drittland', }, { id: 'HT-E03', category: 'third_country', questionId: 'tech_hosting_location', condition: 'EQUALS', conditionValue: 'us_adequacy', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TRANSFER_DOKU'], legalReference: 'Art. 45 DSGVO', description: 'Hosting in USA mit Angemessenheitsbeschluss', }, { id: 'HT-E04', category: 'third_country', questionId: 'tech_third_country', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'], legalReference: 'Art. 44 ff. + Art. 9 DSGVO', description: 'Drittlandtransfer besonderer Kategorien', combineWithArt9: true, }, { id: 'HT-E05', category: 'third_country', questionId: 'tech_third_country', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'], legalReference: 'Art. 44 ff. + Art. 8 DSGVO', description: 'Drittlandtransfer von Daten Minderjähriger', combineWithMinors: true, }, // ========== F: Zertifizierung (5 rules) ========== { id: 'HT-F01', category: 'certification', questionId: 'org_cert_target', condition: 'CONTAINS', conditionValue: 'ISO27001', minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'], legalReference: 'ISO/IEC 27001', description: 'Angestrebte ISO 27001 Zertifizierung', }, { id: 'HT-F02', category: 'certification', questionId: 'org_cert_target', condition: 'CONTAINS', conditionValue: 'ISO27701', minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['TOM', 'VVT', 'AUDIT_CHECKLIST'], legalReference: 'ISO/IEC 27701', description: 'Angestrebte ISO 27701 Zertifizierung', }, { id: 'HT-F03', category: 'certification', questionId: 'org_cert_target', condition: 'CONTAINS', conditionValue: 'SOC2', minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'], legalReference: 'SOC 2 Type II', description: 'Angestrebte SOC 2 Zertifizierung', }, { id: 'HT-F04', category: 'certification', questionId: 'org_cert_target', condition: 'CONTAINS', conditionValue: 'TISAX', minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST', 'VENDOR_MANAGEMENT'], legalReference: 'TISAX', description: 'Angestrebte TISAX Zertifizierung', }, { id: 'HT-F05', category: 'certification', questionId: 'org_cert_target', condition: 'CONTAINS', conditionValue: 'BSI-Grundschutz', minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'], legalReference: 'BSI IT-Grundschutz', description: 'Angestrebte BSI-Grundschutz Zertifizierung', }, // ========== G: Volumen/Skala (5 rules) ========== { id: 'HT-G01', category: 'scale', questionId: 'data_volume', condition: 'EQUALS', conditionValue: '>1000000', minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT'], legalReference: 'Art. 35 Abs. 3 lit. b DSGVO', description: 'Umfangreiche Verarbeitung personenbezogener Daten (>1 Mio. Datensätze)', }, { id: 'HT-G02', category: 'scale', questionId: 'data_volume', condition: 'EQUALS', conditionValue: '100000-1000000', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'Art. 35 Abs. 3 lit. b DSGVO', description: 'Großvolumige Datenverarbeitung (100k-1M Datensätze)', }, { id: 'HT-G03', category: 'scale', questionId: 'org_customer_count', condition: 'EQUALS', conditionValue: '100000+', minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'], legalReference: 'Art. 15-22 DSGVO', description: 'Großer Kundenstamm (>100k) mit hoher Betroffenenanzahl', }, { id: 'HT-G04', category: 'scale', questionId: 'org_employee_count', condition: 'GREATER_THAN', conditionValue: 249, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT', 'NOTFALLPLAN'], legalReference: 'Art. 37 DSGVO', description: 'Große Organisation (>250 Mitarbeiter) mit erhöhten Compliance-Anforderungen', }, { id: 'HT-G05', category: 'scale', questionId: 'org_employee_count', condition: 'GREATER_THAN', conditionValue: 999, minimumLevel: 'L4', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'LOESCHKONZEPT'], legalReference: 'Art. 35 + Art. 37 DSGVO', description: 'Sehr große Organisation (>1000 Mitarbeiter) mit Art. 9 Daten', combineWithArt9: true, }, // ========== H: Produkt/Business (7 rules) ========== { id: 'HT-H01', category: 'product', questionId: 'prod_webshop', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER', 'EINWILLIGUNGEN', 'VERBRAUCHERSCHUTZ'], legalReference: 'Art. 6 DSGVO + eCommerce', description: 'E-Commerce / Webshop-Betrieb', }, { id: 'HT-H02', category: 'product', questionId: 'prod_data_broker', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'EINWILLIGUNGEN'], legalReference: 'Art. 35 Abs. 3 DSGVO', description: 'Datenhandel oder Datenmakler-Tätigkeit', }, { id: 'HT-H03', category: 'product', questionId: 'prod_api_external', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM', 'AVV'], legalReference: 'Art. 28 DSGVO', description: 'Externe API mit Datenweitergabe', }, { id: 'HT-H04', category: 'product', questionId: 'org_business_model', condition: 'EQUALS', conditionValue: 'b2c', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['DSE', 'COOKIE_BANNER', 'EINWILLIGUNGEN'], legalReference: 'Art. 6 DSGVO', description: 'B2C-Geschäftsmodell mit Endkundenkontakt', }, { id: 'HT-H05', category: 'product', questionId: 'org_industry', condition: 'EQUALS', conditionValue: 'finance', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'Art. 6 DSGVO + Finanzaufsicht', description: 'Finanzbranche mit erhöhten regulatorischen Anforderungen', }, { id: 'HT-H06', category: 'product', questionId: 'org_industry', condition: 'EQUALS', conditionValue: 'healthcare', minimumLevel: 'L3', requiresDSFA: true, mandatoryDocuments: ['VVT', 'TOM', 'DSFA'], legalReference: 'Art. 9 DSGVO + Gesundheitsrecht', description: 'Gesundheitsbranche mit sensiblen Daten', }, { id: 'HT-H07', category: 'product', questionId: 'org_industry', condition: 'EQUALS', conditionValue: 'public', minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'], legalReference: 'Art. 6 Abs. 1 lit. e DSGVO', description: 'Öffentlicher Sektor', }, // ========== I: Prozessreife - Gap Flags (5 rules) ========== { id: 'HT-I01', category: 'process_maturity', questionId: 'proc_dsar_process', condition: 'EQUALS', conditionValue: false, minimumLevel: 'L1', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'Art. 15-22 DSGVO', description: 'Fehlender Prozess für Betroffenenrechte', }, { id: 'HT-I02', category: 'process_maturity', questionId: 'proc_deletion_concept', condition: 'EQUALS', conditionValue: false, minimumLevel: 'L1', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'Art. 17 DSGVO', description: 'Fehlendes Löschkonzept', }, { id: 'HT-I03', category: 'process_maturity', questionId: 'proc_incident_response', condition: 'EQUALS', conditionValue: false, minimumLevel: 'L1', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'Art. 33 DSGVO', description: 'Fehlender Incident-Response-Prozess', }, { id: 'HT-I04', category: 'process_maturity', questionId: 'proc_regular_audits', condition: 'EQUALS', conditionValue: false, minimumLevel: 'L1', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'Art. 24 DSGVO', description: 'Fehlende regelmäßige Audits', }, { id: 'HT-I05', category: 'process_maturity', questionId: 'comp_training', condition: 'EQUALS', conditionValue: false, minimumLevel: 'L1', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'Art. 39 Abs. 1 lit. b DSGVO', description: 'Fehlende Schulungen zum Datenschutz', }, // ========== J: IACE — AI Act Produkt-Triggers (3 rules) ========== { id: 'HT-J01', category: 'iace_ai_act_product', questionId: 'machineBuilder.containsAI', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'EU AI Act Annex I + EU Maschinenverordnung 2023/1230', description: 'KI mit Sicherheitsfunktion in Maschine → AI Act High-Risk', combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true }, riskWeight: 9, }, { id: 'HT-J02', category: 'iace_ai_act_product', questionId: 'machineBuilder.containsAI', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'EU AI Act + EU Maschinenverordnung 2023/1230', description: 'Autonome KI in Maschine → AI Act + Maschinenverordnung', combineWithMachineBuilder: { field: 'autonomousBehavior', value: true }, riskWeight: 8, }, { id: 'HT-J03', category: 'iace_ai_act_product', questionId: 'machineBuilder.hasSafetyFunction', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['VVT', 'TOM'], legalReference: 'EU AI Act Annex III', description: 'KI-Bildverarbeitung mit Sicherheitsbezug', combineWithMachineBuilder: { field: 'aiIntegrationType', includes: 'vision' }, riskWeight: 8, }, // ========== K: IACE — CRA Triggers (3 rules) ========== { id: 'HT-K01', category: 'iace_cra', questionId: 'machineBuilder.isNetworked', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'EU Cyber Resilience Act (CRA)', description: 'Vernetztes Produkt → Cyber Resilience Act', riskWeight: 6, }, { id: 'HT-K02', category: 'iace_cra', questionId: 'machineBuilder.hasRemoteAccess', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'CRA + NIS2 Art. 21', description: 'Remote-Zugriff → CRA + NIS2 Supply Chain', riskWeight: 7, }, { id: 'HT-K03', category: 'iace_cra', questionId: 'machineBuilder.hasOTAUpdates', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'CRA Art. 10 - Patch Management', description: 'OTA-Updates → CRA Patch Management Pflicht', riskWeight: 7, }, // ========== L: IACE — NIS2 indirekt (2 rules) ========== { id: 'HT-L01', category: 'iace_nis2_indirect', questionId: 'machineBuilder.criticalSectorClients', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'NIS2 Art. 21 - Supply Chain', description: 'Lieferant an KRITIS → NIS2 Supply Chain Anforderungen', riskWeight: 7, }, { id: 'HT-L02', category: 'iace_nis2_indirect', questionId: 'machineBuilder.oemClients', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'NIS2 + EU Maschinenverordnung', description: 'OEM-Zulieferer → Compliance-Nachweispflicht', riskWeight: 5, }, // ========== M: IACE — Maschinenverordnung Triggers (4 rules) ========== { id: 'HT-M01', category: 'iace_machinery_regulation', questionId: 'machineBuilder.containsSoftware', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'EU Maschinenverordnung 2023/1230 Anhang III', description: 'Software als Sicherheitskomponente → Maschinenverordnung', combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true }, riskWeight: 9, }, { id: 'HT-M02', category: 'iace_machinery_regulation', questionId: 'machineBuilder.ceMarkingRequired', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'EU Maschinenverordnung 2023/1230', description: 'CE-Kennzeichnung erforderlich', riskWeight: 6, }, { id: 'HT-M03', category: 'iace_machinery_regulation', questionId: 'machineBuilder.ceMarkingRequired', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L3', requiresDSFA: false, mandatoryDocuments: [], legalReference: 'EU Maschinenverordnung 2023/1230 Art. 10', description: 'CE ohne bestehende Risikobeurteilung → Dringend!', combineWithMachineBuilder: { field: 'hasRiskAssessment', value: false }, riskWeight: 9, }, { id: 'HT-M04', category: 'iace_machinery_regulation', questionId: 'machineBuilder.containsFirmware', condition: 'EQUALS', conditionValue: true, minimumLevel: 'L2', requiresDSFA: false, mandatoryDocuments: ['TOM'], legalReference: 'EU Maschinenverordnung + CRA', description: 'Firmware mit Remote-Update → Change Management Pflicht', combineWithMachineBuilder: { field: 'hasOTAUpdates', value: true }, riskWeight: 7, }, ] // ============================================================================ // COMPLIANCE SCOPE ENGINE // ============================================================================ export class ComplianceScopeEngine { /** * Haupteinstiegspunkt: Evaluiert alle Profiling-Antworten und produziert eine ScopeDecision * Optional: companyProfile fuer machineBuilder-basierte IACE Triggers */ evaluate(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): ScopeDecision { const decision = createEmptyScopeDecision() // 1. Scores berechnen decision.scores = this.calculateScores(answers) // 2. Hard Triggers prüfen (inkl. IACE machineBuilder Triggers) decision.triggeredHardTriggers = this.evaluateHardTriggers(answers, companyProfile) // 3. Finales Level bestimmen decision.determinedLevel = this.determineLevel( decision.scores, decision.triggeredHardTriggers ) // 4. Dokumenten-Scope aufbauen decision.requiredDocuments = this.buildDocumentScope( decision.determinedLevel, decision.triggeredHardTriggers, answers ) // 5. Risk Flags ermitteln decision.riskFlags = this.evaluateRiskFlags(answers, decision.determinedLevel) // 6. Gaps berechnen decision.gaps = this.calculateGaps(answers, decision.determinedLevel) // 7. Next Actions ableiten decision.nextActions = this.buildNextActions( decision.requiredDocuments, decision.gaps ) // 8. Reasoning (Audit Trail) aufbauen decision.reasoning = this.buildReasoning( decision.scores, decision.triggeredHardTriggers, decision.determinedLevel, decision.requiredDocuments ) decision.evaluatedAt = new Date().toISOString() return decision } /** * Berechnet Risk-, Complexity- und Assurance-Scores aus den Profiling-Antworten */ calculateScores(answers: ScopeProfilingAnswer[]): ComplianceScores { let riskSum = 0 let complexitySum = 0 let assuranceSum = 0 let riskWeightSum = 0 let complexityWeightSum = 0 let assuranceWeightSum = 0 for (const answer of answers) { const weights = QUESTION_SCORE_WEIGHTS[answer.questionId] if (!weights) continue const multiplier = this.getAnswerMultiplier(answer) riskSum += weights.risk * multiplier complexitySum += weights.complexity * multiplier assuranceSum += weights.assurance * multiplier riskWeightSum += weights.risk complexityWeightSum += weights.complexity assuranceWeightSum += weights.assurance } const riskScore = riskWeightSum > 0 ? (riskSum / riskWeightSum) * 10 : 0 const complexityScore = complexityWeightSum > 0 ? (complexitySum / complexityWeightSum) * 10 : 0 const assuranceScore = assuranceWeightSum > 0 ? (assuranceSum / assuranceWeightSum) * 10 : 0 const composite = riskScore * 0.4 + complexityScore * 0.3 + assuranceScore * 0.3 return { risk: Math.round(riskScore * 10) / 10, complexity: Math.round(complexityScore * 10) / 10, assurance: Math.round(assuranceScore * 10) / 10, composite: Math.round(composite * 10) / 10, } } /** * Bestimmt den Multiplikator für eine Antwort (0.0 - 1.0) */ private getAnswerMultiplier(answer: ScopeProfilingAnswer): number { const { questionId, answerValue } = answer // Boolean if (typeof answerValue === 'boolean') { return answerValue ? 1.0 : 0.0 } // Number if (typeof answerValue === 'number') { return this.normalizeNumericAnswer(questionId, answerValue) } // Single choice if (typeof answerValue === 'string') { const multipliers = ANSWER_MULTIPLIERS[questionId] if (multipliers && multipliers[answerValue] !== undefined) { return multipliers[answerValue] } return 0.5 // Fallback } // Multi choice if (Array.isArray(answerValue)) { if (answerValue.length === 0) return 0.0 // Simplified: count selected items return Math.min(answerValue.length / 5, 1.0) } return 0.0 } /** * Normalisiert numerische Antworten */ private normalizeNumericAnswer(questionId: string, value: number): number { // Hier könnten spezifische Ranges definiert werden // Vereinfacht: logarithmische Normalisierung if (value <= 0) return 0.0 if (value >= 1000) return 1.0 return Math.log10(value + 1) / 3 // 0-1000 → ~0-1 } /** * Evaluiert Hard Trigger Rules * Optional: companyProfile fuer machineBuilder-basierte IACE Triggers */ evaluateHardTriggers(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): TriggeredHardTrigger[] { const triggered: TriggeredHardTrigger[] = [] const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) for (const rule of HARD_TRIGGER_RULES) { const isTriggered = this.checkTriggerCondition(rule, answerMap, answers, companyProfile) if (isTriggered) { triggered.push({ ruleId: rule.id, category: rule.category, description: rule.description, legalReference: rule.legalReference, minimumLevel: rule.minimumLevel, requiresDSFA: rule.requiresDSFA, mandatoryDocuments: rule.mandatoryDocuments, }) } } return triggered } /** * Liest einen Wert aus dem MachineBuilderProfile anhand eines Feldnamens */ private getMachineBuilderValue(mb: MachineBuilderProfile, field: string): unknown { return (mb as Record)[field] } /** * Prüft, ob eine Trigger-Regel erfüllt ist */ private checkTriggerCondition( rule: HardTriggerRule, answerMap: Map, answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null, ): boolean { // IACE machineBuilder-basierte Triggers if (rule.questionId.startsWith('machineBuilder.')) { const mb = companyProfile?.machineBuilder if (!mb) return false const fieldName = rule.questionId.replace('machineBuilder.', '') const fieldValue = this.getMachineBuilderValue(mb, fieldName) if (fieldValue === undefined) return false let baseCondition = false switch (rule.condition) { case 'EQUALS': baseCondition = fieldValue === rule.conditionValue break case 'CONTAINS': if (Array.isArray(fieldValue)) { baseCondition = fieldValue.includes(rule.conditionValue) } break default: baseCondition = fieldValue === rule.conditionValue } if (!baseCondition) return false // combineWithMachineBuilder: additional AND condition on another MB field const combine = (rule as any).combineWithMachineBuilder if (combine) { const combineVal = this.getMachineBuilderValue(mb, combine.field) if (combine.value !== undefined && combineVal !== combine.value) return false if (combine.includes !== undefined) { if (!Array.isArray(combineVal) || !combineVal.includes(combine.includes)) return false } } return true } // Standard answer-based triggers const answerValue = answerMap.get(rule.questionId) if (answerValue === undefined) return false // Basis-Check let baseCondition = false switch (rule.condition) { case 'EQUALS': baseCondition = answerValue === rule.conditionValue break case 'CONTAINS': if (Array.isArray(answerValue)) { baseCondition = answerValue.includes(rule.conditionValue) } else if (typeof answerValue === 'string') { baseCondition = answerValue.includes(rule.conditionValue) } break case 'IN': if (Array.isArray(rule.conditionValue)) { baseCondition = rule.conditionValue.includes(answerValue) } break case 'GREATER_THAN': if (typeof answerValue === 'number' && typeof rule.conditionValue === 'number') { baseCondition = answerValue > rule.conditionValue } else if (typeof answerValue === 'string') { // Parse employee count from string like "1000+" const parsed = this.parseEmployeeCount(answerValue) baseCondition = parsed > (rule.conditionValue as number) } break case 'NOT_EQUALS': baseCondition = answerValue !== rule.conditionValue break } if (!baseCondition) return false // Combined checks if (rule.combineWithArt9) { const art9 = answerMap.get('data_art9') if (!art9 || (Array.isArray(art9) && art9.length === 0)) return false } if (rule.combineWithMinors) { const minors = answerMap.get('data_minors') if (minors !== true) return false } if (rule.combineWithAI) { const ai = answerMap.get('proc_ai_usage') if (!ai || (Array.isArray(ai) && (ai.length === 0 || ai.includes('keine')))) { return false } } if (rule.combineWithEmployeeMonitoring) { const empMon = answerMap.get('proc_employee_monitoring') if (empMon !== true) return false } if (rule.combineWithADM) { const adm = answerMap.get('proc_adm_scoring') if (adm !== true) return false } return true } /** * Parsed Mitarbeiterzahl aus String */ private parseEmployeeCount(value: string): number { if (value === '1-9') return 9 if (value === '10-49') return 49 if (value === '50-249') return 249 if (value === '250-999') return 999 if (value === '1000+') return 1000 return 0 } /** * Bestimmt das finale Compliance-Level basierend auf Scores und Triggers */ determineLevel( scores: ComplianceScores, triggers: TriggeredHardTrigger[] ): ComplianceDepthLevel { // Score-basiertes Level let levelFromScore: ComplianceDepthLevel if (scores.composite <= 25) levelFromScore = 'L1' else if (scores.composite <= 50) levelFromScore = 'L2' else if (scores.composite <= 75) levelFromScore = 'L3' else levelFromScore = 'L4' // Höchstes Level aus Triggers let maxTriggerLevel: ComplianceDepthLevel = 'L1' for (const trigger of triggers) { if (getDepthLevelNumeric(trigger.minimumLevel) > getDepthLevelNumeric(maxTriggerLevel)) { maxTriggerLevel = trigger.minimumLevel } } // Maximum von beiden return maxDepthLevel(levelFromScore, maxTriggerLevel) } /** * Baut den Dokumenten-Scope basierend auf Level und Triggers */ buildDocumentScope( level: ComplianceDepthLevel, triggers: TriggeredHardTrigger[], answers: ScopeProfilingAnswer[] ): RequiredDocument[] { const requiredDocs: RequiredDocument[] = [] const mandatoryFromTriggers = new Set() // Sammle mandatory docs aus Triggern for (const trigger of triggers) { for (const doc of trigger.mandatoryDocuments) { mandatoryFromTriggers.add(doc as ScopeDocumentType) } } // Für jeden Dokumenttyp prüfen for (const docType of Object.keys(DOCUMENT_SCOPE_MATRIX) as ScopeDocumentType[]) { const requirement = DOCUMENT_SCOPE_MATRIX[docType][level] const isMandatoryFromTrigger = mandatoryFromTriggers.has(docType) if (requirement === 'mandatory' || isMandatoryFromTrigger) { requiredDocs.push({ documentType: docType, label: DOCUMENT_TYPE_LABELS[docType], requirement: 'mandatory', priority: this.getDocumentPriority(docType, isMandatoryFromTrigger), estimatedEffort: this.estimateEffort(docType), sdkStepUrl: DOCUMENT_SDK_STEP_MAP[docType], triggeredBy: isMandatoryFromTrigger ? triggers .filter((t) => t.mandatoryDocuments.includes(docType as any)) .map((t) => t.ruleId) : [], }) } else if (requirement === 'recommended') { requiredDocs.push({ documentType: docType, label: DOCUMENT_TYPE_LABELS[docType], requirement: 'recommended', priority: 'medium', estimatedEffort: this.estimateEffort(docType), sdkStepUrl: DOCUMENT_SDK_STEP_MAP[docType], triggeredBy: [], }) } } // Sortieren: mandatory zuerst, dann nach Priority requiredDocs.sort((a, b) => { if (a.requirement === 'mandatory' && b.requirement !== 'mandatory') return -1 if (a.requirement !== 'mandatory' && b.requirement === 'mandatory') return 1 const priorityOrder: Record = { high: 3, medium: 2, low: 1 } return priorityOrder[b.priority] - priorityOrder[a.priority] }) return requiredDocs } /** * Bestimmt die Priorität eines Dokuments */ private getDocumentPriority( docType: ScopeDocumentType, isMandatoryFromTrigger: boolean ): 'high' | 'medium' | 'low' { if (isMandatoryFromTrigger) return 'high' // Basis-Dokumente haben hohe Priorität if (['VVT', 'TOM', 'DSE'].includes(docType)) return 'high' if (['DSFA', 'AVV', 'EINWILLIGUNGEN'].includes(docType)) return 'high' return 'medium' } /** * Schätzt den Aufwand für ein Dokument (in Stunden) */ private estimateEffort(docType: ScopeDocumentType): number { const effortMap: Record = { VVT: 8, TOM: 12, DSFA: 16, AVV: 4, DSE: 6, EINWILLIGUNGEN: 6, LOESCHKONZEPT: 10, TRANSFER_DOKU: 8, DSR_PROZESS: 8, NOTFALLPLAN: 12, COOKIE_BANNER: 4, AGB: 6, VERBRAUCHERSCHUTZ: 4, AUDIT_CHECKLIST: 8, VENDOR_MANAGEMENT: 10, AI_ACT_DOKU: 12, } return effortMap[docType] || 6 } /** * Evaluiert Risk Flags basierend auf Process Maturity Gaps und anderen Risiken */ evaluateRiskFlags( answers: ScopeProfilingAnswer[], level: ComplianceDepthLevel ): RiskFlag[] { const flags: RiskFlag[] = [] const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) // Process Maturity Gaps (Kategorie I Trigger) const maturityRules = HARD_TRIGGER_RULES.filter((r) => r.category === 'process_maturity') for (const rule of maturityRules) { if (this.checkTriggerCondition(rule, answerMap, answers)) { flags.push({ severity: 'medium', category: 'process', message: rule.description, legalReference: rule.legalReference, recommendation: this.getMaturityRecommendation(rule.id), }) } } // Verschlüsselung fehlt bei L2+ if (getDepthLevelNumeric(level) >= 2) { const encTransit = answerMap.get('tech_encryption_transit') const encRest = answerMap.get('tech_encryption_rest') if (encTransit === false) { flags.push({ severity: 'high', category: 'technical', message: 'Fehlende Verschlüsselung bei Datenübertragung', legalReference: 'Art. 32 DSGVO', recommendation: 'TLS 1.2+ für alle Datenübertragungen implementieren', }) } if (encRest === false) { flags.push({ severity: 'high', category: 'technical', message: 'Fehlende Verschlüsselung gespeicherter Daten', legalReference: 'Art. 32 DSGVO', recommendation: 'Verschlüsselung at-rest für sensitive Daten implementieren', }) } } // Drittland ohne adäquate Grundlage const thirdCountry = answerMap.get('tech_third_country') const hostingLocation = answerMap.get('tech_hosting_location') if ( thirdCountry === true && hostingLocation !== 'eu' && hostingLocation !== 'eu_us_adequacy' ) { flags.push({ severity: 'high', category: 'legal', message: 'Drittlandtransfer ohne angemessene Garantien', legalReference: 'Art. 44 ff. DSGVO', recommendation: 'Standardvertragsklauseln (SCCs) oder Binding Corporate Rules (BCRs) implementieren', }) } // Fehlender DSB bei großen Organisationen const hasDPO = answerMap.get('org_has_dpo') const employeeCount = answerMap.get('org_employee_count') if (hasDPO === false && this.parseEmployeeCount(employeeCount as string) >= 250) { flags.push({ severity: 'medium', category: 'organizational', message: 'Kein Datenschutzbeauftragter bei großer Organisation', legalReference: 'Art. 37 DSGVO', recommendation: 'Bestellung eines Datenschutzbeauftragten prüfen', }) } return flags } /** * Gibt Empfehlung für Maturity Gap */ private getMaturityRecommendation(ruleId: string): string { const recommendations: Record = { 'HT-I01': 'Prozess für Betroffenenrechte (DSAR) etablieren und dokumentieren', 'HT-I02': 'Löschkonzept gemäß Art. 17 DSGVO entwickeln und implementieren', 'HT-I03': 'Incident-Response-Plan für Datenschutzverletzungen (Art. 33 DSGVO) erstellen', 'HT-I04': 'Regelmäßige interne Audits und Reviews einführen', 'HT-I05': 'Schulungsprogramm für Mitarbeiter zum Datenschutz etablieren', } return recommendations[ruleId] || 'Prozess etablieren und dokumentieren' } /** * Berechnet Gaps zwischen Ist-Zustand und Soll-Anforderungen */ calculateGaps( answers: ScopeProfilingAnswer[], level: ComplianceDepthLevel ): ScopeGap[] { const gaps: ScopeGap[] = [] const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) // DSFA Gap (bei L3+) if (getDepthLevelNumeric(level) >= 3) { const hasDSFA = answerMap.get('proc_regular_audits') // Proxy if (hasDSFA === false) { gaps.push({ gapType: 'documentation', severity: 'high', description: 'Datenschutz-Folgenabschätzung (DSFA) fehlt', requiredFor: level, currentState: 'Keine DSFA durchgeführt', targetState: 'DSFA für Hochrisiko-Verarbeitungen durchgeführt und dokumentiert', effort: 16, priority: 'high', }) } } // Löschkonzept Gap const hasDeletion = answerMap.get('proc_deletion_concept') if (hasDeletion === false && getDepthLevelNumeric(level) >= 2) { gaps.push({ gapType: 'process', severity: 'medium', description: 'Löschkonzept fehlt', requiredFor: level, currentState: 'Kein systematisches Löschkonzept', targetState: 'Dokumentiertes Löschkonzept mit definierten Fristen', effort: 10, priority: 'high', }) } // DSAR Prozess Gap const hasDSAR = answerMap.get('proc_dsar_process') if (hasDSAR === false) { gaps.push({ gapType: 'process', severity: 'high', description: 'Prozess für Betroffenenrechte fehlt', requiredFor: level, currentState: 'Kein etablierter DSAR-Prozess', targetState: 'Dokumentierter Prozess zur Bearbeitung von Betroffenenrechten', effort: 8, priority: 'high', }) } // Incident Response Gap const hasIncident = answerMap.get('proc_incident_response') if (hasIncident === false) { gaps.push({ gapType: 'process', severity: 'high', description: 'Incident-Response-Plan fehlt', requiredFor: level, currentState: 'Kein Prozess für Datenschutzverletzungen', targetState: 'Dokumentierter Incident-Response-Plan gemäß Art. 33 DSGVO', effort: 12, priority: 'high', }) } // Schulungen Gap const hasTraining = answerMap.get('comp_training') if (hasTraining === false && getDepthLevelNumeric(level) >= 2) { gaps.push({ gapType: 'organizational', severity: 'medium', description: 'Datenschutzschulungen fehlen', requiredFor: level, currentState: 'Keine regelmäßigen Schulungen', targetState: 'Etabliertes Schulungsprogramm für alle Mitarbeiter', effort: 6, priority: 'medium', }) } return gaps } /** * Baut priorisierte Next Actions aus Required Documents und Gaps */ buildNextActions( requiredDocuments: RequiredDocument[], gaps: ScopeGap[] ): NextAction[] { const actions: NextAction[] = [] // Dokumente zu Actions for (const doc of requiredDocuments) { if (doc.requirement === 'mandatory') { actions.push({ actionType: 'create_document', title: `${doc.label} erstellen`, description: `Pflichtdokument für Compliance-Level erstellen`, priority: doc.priority, estimatedEffort: doc.estimatedEffort, documentType: doc.documentType, sdkStepUrl: doc.sdkStepUrl, blockers: [], }) } } // Gaps zu Actions for (const gap of gaps) { let actionType: NextAction['actionType'] = 'establish_process' if (gap.gapType === 'documentation') actionType = 'create_document' else if (gap.gapType === 'technical') actionType = 'implement_technical' else if (gap.gapType === 'organizational') actionType = 'organizational_change' actions.push({ actionType, title: `Gap schließen: ${gap.description}`, description: `Von "${gap.currentState}" zu "${gap.targetState}"`, priority: gap.priority, estimatedEffort: gap.effort, blockers: [], }) } // Nach Priority sortieren const priorityOrder: Record = { high: 3, medium: 2, low: 1 } actions.sort((a, b) => priorityOrder[b.priority] - priorityOrder[a.priority]) return actions } /** * Baut Reasoning (Audit Trail) für Transparenz */ buildReasoning( scores: ComplianceScores, triggers: TriggeredHardTrigger[], level: ComplianceDepthLevel, docs: RequiredDocument[] ): ScopeReasoning[] { const reasoning: ScopeReasoning[] = [] // 1. Score-Berechnung reasoning.push({ step: 'score_calculation', description: 'Risikobasierte Score-Berechnung aus Profiling-Antworten', factors: [ `Risiko-Score: ${scores.risk}/10`, `Komplexitäts-Score: ${scores.complexity}/10`, `Assurance-Score: ${scores.assurance}/10`, `Composite Score: ${scores.composite}/10`, ], impact: `Score-basiertes Level: ${this.getLevelFromScore(scores.composite)}`, }) // 2. Hard Trigger Evaluation if (triggers.length > 0) { reasoning.push({ step: 'hard_trigger_evaluation', description: `${triggers.length} Hard Trigger Rule(s) aktiviert`, factors: triggers.map( (t) => `${t.ruleId}: ${t.description} (${t.legalReference})` ), impact: `Höchstes Trigger-Level: ${this.getMaxTriggerLevel(triggers)}`, }) } // 3. Level-Bestimmung reasoning.push({ step: 'level_determination', description: 'Finales Compliance-Level durch Maximum aus Score und Triggers', factors: [ `Score-Level: ${this.getLevelFromScore(scores.composite)}`, `Trigger-Level: ${this.getMaxTriggerLevel(triggers)}`, ], impact: `Finales Level: ${level}`, }) // 4. Dokumenten-Scope const mandatoryDocs = docs.filter((d) => d.requirement === 'mandatory') reasoning.push({ step: 'document_scope', description: `Dokumenten-Scope für ${level} bestimmt`, factors: [ `${mandatoryDocs.length} Pflichtdokumente`, `${docs.length - mandatoryDocs.length} empfohlene Dokumente`, ], impact: `Gesamtaufwand: ~${docs.reduce((sum, d) => sum + d.estimatedEffort, 0)} Stunden`, }) return reasoning } /** * Hilfsfunktion: Level aus Score ableiten */ private getLevelFromScore(composite: number): ComplianceDepthLevel { if (composite <= 25) return 'L1' if (composite <= 50) return 'L2' if (composite <= 75) return 'L3' return 'L4' } /** * Hilfsfunktion: Höchstes Level aus Triggern */ private getMaxTriggerLevel(triggers: TriggeredHardTrigger[]): ComplianceDepthLevel { if (triggers.length === 0) return 'L1' let max: ComplianceDepthLevel = 'L1' for (const t of triggers) { if (getDepthLevelNumeric(t.minimumLevel) > getDepthLevelNumeric(max)) { max = t.minimumLevel } } return max } } // ============================================================================ // SINGLETON EXPORT // ============================================================================ export const complianceScopeEngine = new ComplianceScopeEngine()