feat(scope): Split HT-H01 B2B/B2C + register Verbraucherschutz document types + RAG ingestion
Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 38s
CI/CD / test-python-backend-compliance (push) Successful in 39s
CI/CD / test-python-document-crawler (push) Successful in 27s
CI/CD / test-python-dsms-gateway (push) Successful in 24s
CI/CD / deploy-hetzner (push) Has been cancelled

- Split HT-H01 into HT-H01a (B2C/Hybrid mit Verbraucherschutzpflichten) und
  HT-H01b (reiner B2B mit Basis-Pflichten). B2B-Webshops bekommen keine
  Widerrufsbelehrung/Preisangaben/Fernabsatz mehr.
- Add excludeWhen/requireWhen to HardTriggerRule for conditional trigger logic
- Register 6 neue ScopeDocumentType: widerrufsbelehrung, preisangaben,
  fernabsatz_info, streitbeilegung, produktsicherheit, ai_act_doku
- Full DOCUMENT_SCOPE_MATRIX L1-L4 for all new types
- Align HardTriggerRule interface with actual engine field names
- Add Phase H (Verbraucherschutz) to RAG ingestion script:
  10 deutsche Gesetze + 4 EU-Verordnungen + HLEG Ethics Guidelines
- Add scripts/rag-sources.md with license documentation
- 9 new tests for B2B/B2C trigger split, all 326 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-11 16:03:49 +01:00
parent cb48b8289e
commit 7f38df9d9c
5 changed files with 666 additions and 20 deletions

View File

@@ -370,3 +370,101 @@ describe('evaluateRiskFlags', () => {
expect(orgFlag).toBeUndefined()
})
})
// ============================================================================
// HT-H01a/b: B2B vs B2C Webshop Trigger Split
// ============================================================================
describe('HT-H01a/b — B2B vs B2C Webshop', () => {
it('B2C webshop triggers HT-H01a with Verbraucherschutz documents', () => {
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
ans('org_business_model', 'B2C'),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
expect(h01a).toBeDefined()
expect(h01b).toBeUndefined()
expect(h01a!.mandatoryDocuments).toContain('WIDERRUFSBELEHRUNG')
expect(h01a!.mandatoryDocuments).toContain('PREISANGABEN')
expect(h01a!.mandatoryDocuments).toContain('FERNABSATZ_INFO')
expect(h01a!.mandatoryDocuments).toContain('STREITBEILEGUNG')
})
it('B2B webshop triggers HT-H01b without Verbraucherschutz documents', () => {
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
ans('org_business_model', 'B2B'),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
expect(h01a).toBeUndefined()
expect(h01b).toBeDefined()
expect(h01b!.mandatoryDocuments).toContain('DSE')
expect(h01b!.mandatoryDocuments).toContain('AGB')
expect(h01b!.mandatoryDocuments).toContain('COOKIE_BANNER')
expect(h01b!.mandatoryDocuments).not.toContain('WIDERRUFSBELEHRUNG')
expect(h01b!.mandatoryDocuments).not.toContain('PREISANGABEN')
})
it('B2B_B2C (hybrid) webshop triggers HT-H01a (Verbraucherschutz applies)', () => {
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
ans('org_business_model', 'B2B_B2C'),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
expect(h01a).toBeDefined()
expect(h01b).toBeUndefined()
})
it('no webshop → neither HT-H01a nor HT-H01b fires', () => {
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', false),
ans('org_business_model', 'B2C'),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
expect(h01a).toBeUndefined()
expect(h01b).toBeUndefined()
})
it('webshop without business_model answer → HT-H01a fires (excludeWhen not matched)', () => {
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
// excludeWhen B2B: not matched (undefined !== 'B2B') → fires
expect(h01a).toBeDefined()
// requireWhen B2B: not matched (undefined !== 'B2B') → does not fire
expect(h01b).toBeUndefined()
})
})
// ============================================================================
// excludeWhen / requireWhen Logic (unit)
// ============================================================================
describe('excludeWhen / requireWhen — generic logic', () => {
it('excludeWhen with array value excludes any matching value', () => {
// HT-H01a has excludeWhen: { questionId: 'org_business_model', value: 'B2B' }
// This test verifies the single-value case works (B2B excluded)
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
ans('org_business_model', 'B2B'),
])
const h01a = triggers.find((t: any) => t.ruleId === 'HT-H01a')
expect(h01a).toBeUndefined()
})
it('requireWhen with non-matching value prevents trigger', () => {
// HT-H01b has requireWhen: { questionId: 'org_business_model', value: 'B2B' }
const triggers = complianceScopeEngine.evaluateHardTriggers([
ans('prod_webshop', true),
ans('org_business_model', 'B2C'),
])
const h01b = triggers.find((t: any) => t.ruleId === 'HT-H01b')
expect(h01b).toBeUndefined()
})
})

View File

@@ -642,16 +642,31 @@ export const HARD_TRIGGER_RULES: HardTriggerRule[] = [
// ========== H: Produkt/Business (7 rules) ==========
{
id: 'HT-H01',
id: 'HT-H01a',
category: 'product',
questionId: 'prod_webshop',
condition: 'EQUALS',
conditionValue: true,
excludeWhen: { questionId: 'org_business_model', value: 'B2B' },
minimumLevel: 'L2',
requiresDSFA: false,
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER', 'EINWILLIGUNGEN', 'VERBRAUCHERSCHUTZ'],
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER', 'EINWILLIGUNGEN',
'WIDERRUFSBELEHRUNG', 'PREISANGABEN', 'FERNABSATZ_INFO', 'STREITBEILEGUNG'],
legalReference: 'Art. 6 DSGVO + Fernabsatzrecht + PAngV + VSBG',
description: 'E-Commerce / Webshop (B2C) — Verbraucherschutzpflichten',
},
{
id: 'HT-H01b',
category: 'product',
questionId: 'prod_webshop',
condition: 'EQUALS',
conditionValue: true,
requireWhen: { questionId: 'org_business_model', value: 'B2B' },
minimumLevel: 'L2',
requiresDSFA: false,
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER'],
legalReference: 'Art. 6 DSGVO + eCommerce',
description: 'E-Commerce / Webshop-Betrieb',
description: 'E-Commerce / Webshop (B2B) — Basis-Pflichten',
},
{
id: 'HT-H02',
@@ -1224,6 +1239,26 @@ export class ComplianceScopeEngine {
if (!baseCondition) return false
// Exclude-Bedingung: Regel feuert NICHT wenn excludeWhen zutrifft
if (rule.excludeWhen) {
const exVal = answerMap.get(rule.excludeWhen.questionId)
if (Array.isArray(rule.excludeWhen.value)
? rule.excludeWhen.value.includes(exVal)
: exVal === rule.excludeWhen.value) {
return false
}
}
// Require-Bedingung: Regel feuert NUR wenn requireWhen zutrifft
if (rule.requireWhen) {
const reqVal = answerMap.get(rule.requireWhen.questionId)
if (Array.isArray(rule.requireWhen.value)
? !rule.requireWhen.value.includes(reqVal)
: reqVal !== rule.requireWhen.value) {
return false
}
}
// Combined checks
if (rule.combineWithArt9) {
const art9 = answerMap.get('data_art9')
@@ -1388,10 +1423,14 @@ export class ComplianceScopeEngine {
NOTFALLPLAN: 12,
COOKIE_BANNER: 4,
AGB: 6,
VERBRAUCHERSCHUTZ: 4,
WIDERRUFSBELEHRUNG: 3,
PREISANGABEN: 2,
FERNABSATZ_INFO: 4,
STREITBEILEGUNG: 1,
PRODUKTSICHERHEIT: 8,
AI_ACT_DOKU: 12,
AUDIT_CHECKLIST: 8,
VENDOR_MANAGEMENT: 10,
AI_ACT_DOKU: 12,
}
return effortMap[docType] || 6
}

View File

@@ -130,24 +130,38 @@ export type HardTriggerOperator =
export interface HardTriggerRule {
/** Eindeutige ID der Regel */
id: string;
/** Kurze Bezeichnung */
label: string;
/** Detaillierte Beschreibung */
description: string;
/** Feld, das geprüft wird (questionId oder company_profile Feld) */
conditionField: string;
/** Kategorie der Regel */
category: string;
/** Frage-ID, die geprüft wird */
questionId: string;
/** Bedingungsoperator */
conditionOperator: HardTriggerOperator;
condition: HardTriggerOperator;
/** Wert, der geprüft wird */
conditionValue: unknown;
/** Minimal erforderliches Level */
minimumLevel: ComplianceDepthLevel;
/** Pflichtdokumente bei Trigger */
mandatoryDocuments: ScopeDocumentType[];
/** DSFA erforderlich? */
dsfaRequired: boolean;
requiresDSFA: boolean;
/** Pflichtdokumente bei Trigger */
mandatoryDocuments: string[];
/** Rechtsgrundlage */
legalReference: string;
/** Detaillierte Beschreibung */
description: string;
/** Kombiniert mit Art. 9 Daten? */
combineWithArt9?: boolean;
/** Kombiniert mit Minderjährigen-Daten? */
combineWithMinors?: boolean;
/** Kombiniert mit KI-Nutzung? */
combineWithAI?: boolean;
/** Kombiniert mit Mitarbeiterüberwachung? */
combineWithEmployeeMonitoring?: boolean;
/** Kombiniert mit automatisierter Entscheidungsfindung? */
combineWithADM?: boolean;
/** Regel feuert NICHT wenn diese Bedingung zutrifft */
excludeWhen?: { questionId: string; value: string | string[] };
/** Regel feuert NUR wenn diese Bedingung zutrifft */
requireWhen?: { questionId: string; value: string | string[] };
}
/**
@@ -195,7 +209,13 @@ export type ScopeDocumentType =
| 'notfallplan' // Notfall- & Krisenplan
| 'zertifizierung' // Zertifizierungsvorbereitung
| 'datenschutzmanagement' // Datenschutzmanagement-System (DSMS)
| 'iace_ce_assessment'; // CE-Risikobeurteilung SW/FW/KI (IACE)
| 'iace_ce_assessment' // CE-Risikobeurteilung SW/FW/KI (IACE)
| 'widerrufsbelehrung' // Widerrufsbelehrung (§ 312g BGB)
| 'preisangaben' // Preisangaben (PAngV)
| 'fernabsatz_info' // Informationspflichten Fernabsatz (§ 312d BGB)
| 'streitbeilegung' // Streitbeilegungshinweis (VSBG § 36)
| 'produktsicherheit' // Produktsicherheit (GPSR EU 2023/988)
| 'ai_act_doku'; // AI Act Technische Dokumentation (Art. 11)
// ============================================================================
// Decision & Output Types
@@ -429,6 +449,12 @@ export const DOCUMENT_TYPE_LABELS: Record<ScopeDocumentType, string> = {
zertifizierung: 'Zertifizierungsvorbereitung',
datenschutzmanagement: 'Datenschutzmanagement-System (DSMS)',
iace_ce_assessment: 'CE-Risikobeurteilung SW/FW/KI (IACE)',
widerrufsbelehrung: 'Widerrufsbelehrung (§ 312g BGB)',
preisangaben: 'Preisangaben (PAngV)',
fernabsatz_info: 'Informationspflichten Fernabsatz (§ 312d BGB)',
streitbeilegung: 'Streitbeilegungshinweis (VSBG § 36)',
produktsicherheit: 'Produktsicherheitsdokumentation (GPSR)',
ai_act_doku: 'AI Act Technische Dokumentation (Art. 11)',
};
/**
@@ -1311,6 +1337,231 @@ export const DOCUMENT_SCOPE_MATRIX: Record<ScopeDocumentType, DocumentScopeRequi
estimatedEffort: '24 Stunden',
},
},
widerrufsbelehrung: {
L1: {
required: false,
depth: 'Nicht relevant',
detailItems: ['Nur bei B2C-Fernabsatz erforderlich'],
estimatedEffort: '0',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Muster-Widerrufsbelehrung nach EGBGB Anlage 1',
'Muster-Widerrufsformular nach EGBGB Anlage 2',
'Integration in Bestellprozess',
'14-Tage Widerrufsfrist korrekt dargestellt',
],
estimatedEffort: '2-4 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + digitale Inhalte (§ 356 Abs. 5 BGB)',
'Ausnahmen dokumentiert (§ 312g Abs. 2 BGB)',
],
estimatedEffort: '4-6 Stunden',
},
L4: {
required: true,
depth: 'Vollstaendig',
detailItems: [
'Wie L3 + automatisierte Pruefung',
'Mehrsprachig bei EU-Verkauf',
],
estimatedEffort: '6-8 Stunden',
},
},
preisangaben: {
L1: {
required: false,
depth: 'Nicht relevant',
detailItems: ['Nur bei B2C-Preisauszeichnung erforderlich'],
estimatedEffort: '0',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Gesamtpreisangabe inkl. MwSt (§ 1 PAngV)',
'Grundpreisangabe bei Mengenware (§ 4 PAngV)',
'Versandkosten deutlich angegeben',
],
estimatedEffort: '2-3 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + Preishistorie bei Rabattaktionen (Omnibus-RL)',
'Streichpreise korrekt dargestellt',
],
estimatedEffort: '3-5 Stunden',
},
L4: {
required: true,
depth: 'Vollstaendig',
detailItems: [
'Wie L3 + automatisierte Pruefung',
'Mehrwaehrungsunterstuetzung',
],
estimatedEffort: '5-8 Stunden',
},
},
fernabsatz_info: {
L1: {
required: false,
depth: 'Nicht relevant',
detailItems: ['Nur bei Fernabsatzvertraegen erforderlich'],
estimatedEffort: '0',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Pflichtinformationen nach § 312d BGB i.V.m. Art. 246a EGBGB',
'Wesentliche Eigenschaften der Ware/Dienstleistung',
'Identitaet und Anschrift des Unternehmers',
'Zahlungs-, Liefer- und Leistungsbedingungen',
],
estimatedEffort: '3-5 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + Informationen zu digitalen Inhalten/Diensten',
'Funktionalitaet und Interoperabilitaet (§ 327 BGB)',
],
estimatedEffort: '5-8 Stunden',
},
L4: {
required: true,
depth: 'Vollstaendig',
detailItems: [
'Wie L3 + mehrsprachige Informationspflichten',
'Automatisierte Vollstaendigkeitspruefung',
],
estimatedEffort: '8-12 Stunden',
},
},
streitbeilegung: {
L1: {
required: false,
depth: 'Nicht relevant',
detailItems: ['Nur bei B2C-Handel erforderlich'],
estimatedEffort: '0',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Hinweis auf OS-Plattform der EU-Kommission (Art. 14 ODR-VO)',
'Erklaerung zur Teilnahmebereitschaft an Streitbeilegung (§ 36 VSBG)',
'Link zur OS-Plattform im Impressum/AGB',
],
estimatedEffort: '1-2 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + Benennung zustaendiger Verbraucherschlichtungsstelle',
'Prozess fuer Streitbeilegungsanfragen dokumentiert',
],
estimatedEffort: '2-3 Stunden',
},
L4: {
required: true,
depth: 'Vollstaendig',
detailItems: [
'Wie L3 + Eskalationsprozess dokumentiert',
'Regelmaessige Auswertung von Beschwerden',
],
estimatedEffort: '3-4 Stunden',
},
},
produktsicherheit: {
L1: {
required: false,
depth: 'Minimal',
detailItems: ['Grundlegende Produktkennzeichnung pruefen'],
estimatedEffort: '1 Stunde',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Produktsicherheitsbewertung nach GPSR (EU 2023/988)',
'CE-Kennzeichnung und Konformitaetserklaerung',
'Wirtschaftsakteur-Angaben auf Produkt/Verpackung',
'Technische Dokumentation fuer Marktaufsicht',
],
estimatedEffort: '8-12 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + Risikoanalyse fuer alle Produktvarianten',
'Rueckrufplan und Marktbeobachtungspflichten',
'Supply-Chain-Dokumentation',
],
estimatedEffort: '16-24 Stunden',
},
L4: {
required: true,
depth: 'Vollstaendig',
detailItems: [
'Wie L3 + vollstaendige GPSR-Konformitaetsakte',
'Post-Market-Surveillance System',
'Audit-Trail fuer alle Sicherheitsbewertungen',
],
estimatedEffort: '24-40 Stunden',
},
},
ai_act_doku: {
L1: {
required: false,
depth: 'Minimal',
detailItems: ['KI-Risikokategorisierung (Art. 6 AI Act)'],
estimatedEffort: '2 Stunden',
},
L2: {
required: true,
depth: 'Standard',
detailItems: [
'Technische Dokumentation nach Art. 11 AI Act',
'Transparenzpflichten (Art. 52 AI Act)',
'Risikomanagement-Grundlagen (Art. 9 AI Act)',
'Menschliche Aufsicht dokumentiert (Art. 14 AI Act)',
],
estimatedEffort: '8-12 Stunden',
},
L3: {
required: true,
depth: 'Erweitert',
detailItems: [
'Wie L2 + Datenqualitaetsmanagement (Art. 10 AI Act)',
'Genauigkeits- und Robustheitstests (Art. 15 AI Act)',
'Vollstaendige Konformitaetsbewertung fuer Hochrisiko-KI',
],
estimatedEffort: '16-24 Stunden',
},
L4: {
required: true,
depth: 'Audit-Ready',
detailItems: [
'Wie L3 + Zertifizierungsfertige AI Act Dokumentation',
'EU-Datenbank-Registrierung (Art. 60 AI Act)',
'Post-Market Monitoring fuer KI-Systeme',
'Continuous Compliance Framework fuer KI',
],
estimatedEffort: '24-40 Stunden',
},
},
};
// ============================================================================
@@ -1339,6 +1590,12 @@ export const DOCUMENT_SDK_STEP_MAP: Partial<Record<ScopeDocumentType, string>> =
zertifizierung: '/sdk/iace',
datenschutzmanagement: '/sdk/dsms',
iace_ce_assessment: '/sdk/iace',
widerrufsbelehrung: '/sdk/policy-generator',
preisangaben: '/sdk/policy-generator',
fernabsatz_info: '/sdk/policy-generator',
streitbeilegung: '/sdk/policy-generator',
produktsicherheit: '/sdk/iace',
ai_act_doku: '/sdk/ai-act',
};
// ============================================================================