feat(reasoning): Regulatory Reasoning Engine MVP (scope/obligations/implementation/interpretation)
Deterministic reasoning layer ON TOP of the Legal Knowledge Graph (obligation
registry) and the Compliance Execution Graph (control mapping/evidence). Answers
which regulations apply to a concrete product, which obligations follow, whether
the customer's implementation covers them, and whether a customer interpretation
is too narrow/broad/plausible.
- ProductProfile with tri-state facts (Optional[bool]=None => uncertain, never
false security); safe predicate evaluator (no eval).
- 6 regulation triggers (CRA/MaschinenVO/RED/EMV/DataAct/NIS2) with missing-fact
prompts; 24 obligation scope rules.
- CRA obligation_ids RE-USED verbatim from the registry (93 ids) — never re-minted
(control_uuid trap); Machine/Data-Act flagged proposed=True.
- required_evidence constrained to the framework-agnostic shared evidence catalog;
capabilities echo the planned Obligation->Capability layer.
- Overlap groups (CRA<->MaschinenVO cyber-safety) + evidence-for-multiple (USP).
- 4 endpoints POST /reasoning/{scope,obligations,implementation-assessment,
interpretation-assessment}; thin handlers, registered in api/__init__.py.
- 22 tests (5 machine-builder scenarios + 10 acceptance questions). No DB
migration, no RAG, no new controls.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""Known customer interpretation patterns (spec Modus 4).
|
||||
|
||||
Deterministic: a customer interpretation is matched by lowercase substring
|
||||
triggers against a curated library of common misconceptions. No match ->
|
||||
the engine returns `uncertain` and asks for the missing context (no false
|
||||
security, spec §6.3).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from .enums import Confidence, InterpretationVerdict
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InterpretationPattern:
|
||||
pattern_id: str
|
||||
triggers: List[str]
|
||||
verdict: InterpretationVerdict
|
||||
corrected_interpretation: str
|
||||
explanation: str
|
||||
affected_regulations: List[str] = field(default_factory=list)
|
||||
affected_obligations: List[str] = field(default_factory=list)
|
||||
risks: List[str] = field(default_factory=list)
|
||||
legal_basis_refs: List[str] = field(default_factory=list)
|
||||
confidence: Confidence = Confidence.MEDIUM
|
||||
|
||||
|
||||
INTERPRETATION_PATTERNS: List[InterpretationPattern] = [
|
||||
InterpretationPattern(
|
||||
pattern_id="cra_only_new_products",
|
||||
triggers=[
|
||||
"nur für neue", "nur fuer neue", "nur neu entwickelt", "nur neuentwicklung",
|
||||
"nur bei neuentwicklung", "only new product", "gilt nur für neue produkte",
|
||||
],
|
||||
verdict=InterpretationVerdict.TOO_NARROW,
|
||||
corrected_interpretation=(
|
||||
"CRA-Pflichten knüpfen primär an Produkt, Rolle, Marktzugang, Bereitstellung und "
|
||||
"Übergangsfristen an, nicht nur an Neuentwicklung. Ein fertig entwickeltes "
|
||||
"Katalogprodukt kann betroffen sein, wenn es nach dem maßgeblichen Zeitpunkt weiter "
|
||||
"auf dem EU-Markt bereitgestellt wird."
|
||||
),
|
||||
explanation=(
|
||||
"Die relevante Frage ist nicht nur, ob das Produkt neu entwickelt wurde, sondern ob es "
|
||||
"nach dem Anwendungszeitpunkt weiterhin bereitgestellt oder in Verkehr gebracht wird."
|
||||
),
|
||||
affected_regulations=["CRA"],
|
||||
risks=["Katalog-/Bestandsprodukt fällt trotz abgeschlossener Entwicklung unter den CRA."],
|
||||
legal_basis_refs=["CRA Art. 2", "CRA Art. 69 (Übergangsbestimmungen)"],
|
||||
confidence=Confidence.HIGH,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="cra_b2b_exempt",
|
||||
triggers=[
|
||||
"gilt nicht für b2b", "nur für verbraucher", "nur b2c", "nicht im b2b",
|
||||
"only consumer", "b2b ist ausgenommen",
|
||||
],
|
||||
verdict=InterpretationVerdict.TOO_NARROW,
|
||||
corrected_interpretation=(
|
||||
"Der CRA gilt produkt- und marktbezogen, unabhängig von B2B oder B2C. Eine generelle "
|
||||
"B2B-Ausnahme existiert nicht; Industrieprodukte mit digitalen Elementen sind erfasst."
|
||||
),
|
||||
explanation="Der Anwendungsbereich knüpft an 'Produkte mit digitalen Elementen' an, nicht an die Kundengruppe.",
|
||||
affected_regulations=["CRA"],
|
||||
risks=["Industrielle B2B-Steuerungen werden fälschlich als ausgenommen behandelt."],
|
||||
legal_basis_refs=["CRA Art. 2", "CRA Art. 3(1)"],
|
||||
confidence=Confidence.HIGH,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="sbom_is_enough",
|
||||
triggers=[
|
||||
"sbom reicht", "mit sbom sind wir", "sbom genügt", "sbom genuegt", "nur eine sbom",
|
||||
"sbom allein",
|
||||
],
|
||||
verdict=InterpretationVerdict.TOO_NARROW,
|
||||
corrected_interpretation=(
|
||||
"Eine SBOM erfüllt nur einen Teil der Komponenten-Transparenz. Schwachstellen-"
|
||||
"überwachung, Update-/Patch-Prozess und technische Dokumentation bleiben eigenständige Pflichten."
|
||||
),
|
||||
explanation="SBOM ist Voraussetzung, ersetzt aber nicht Vulnerability-Handling und Updates.",
|
||||
affected_regulations=["CRA"],
|
||||
affected_obligations=["sbom_creation", "vuln_handling_process", "provide_security_updates"],
|
||||
risks=["Falsche Annahme vollständiger Erfüllung trotz fehlendem Vulnerability-Prozess."],
|
||||
legal_basis_refs=["CRA Annex I Part II (1)", "CRA Annex I Part II (2)"],
|
||||
confidence=Confidence.HIGH,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="open_source_exempt",
|
||||
triggers=[
|
||||
"open source ist ausgenommen", "open-source ist ausgenommen", "oss ist ausgenommen",
|
||||
"freie software ist ausgenommen", "open source fällt nicht",
|
||||
],
|
||||
verdict=InterpretationVerdict.PARTIALLY_CORRECT,
|
||||
corrected_interpretation=(
|
||||
"Nur nicht-kommerziell bereitgestellte Open-Source-Software ist ausgenommen. Sobald OSS "
|
||||
"kommerziell in ein Produkt integriert und auf dem Markt bereitgestellt wird, greift der CRA."
|
||||
),
|
||||
explanation="Die Ausnahme zielt auf nicht-kommerzielle OSS-Bereitstellung, nicht auf kommerzielle Produktintegration.",
|
||||
affected_regulations=["CRA"],
|
||||
risks=["Kommerziell integrierte OSS-Komponenten werden fälschlich als ausgenommen behandelt."],
|
||||
legal_basis_refs=["CRA Art. 2", "CRA Erwägungsgründe (Open-Source-Stewards)"],
|
||||
confidence=Confidence.MEDIUM,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="reactive_updates_ok",
|
||||
triggers=[
|
||||
"updates nur wenn", "reaktive updates reichen", "wenn kunden melden reicht",
|
||||
"updates wenn fehler gemeldet",
|
||||
],
|
||||
verdict=InterpretationVerdict.TOO_NARROW,
|
||||
corrected_interpretation=(
|
||||
"Der CRA verlangt aktive Schwachstellenüberwachung und zeitnahe Sicherheitsupdates über "
|
||||
"den Supportzeitraum, nicht nur reaktive Updates nach Kundenmeldung."
|
||||
),
|
||||
explanation="Ein rein reaktiver Updateprozess erfüllt die Pflicht zur aktiven Schwachstellenbehandlung nicht.",
|
||||
affected_regulations=["CRA"],
|
||||
affected_obligations=["provide_security_updates", "vuln_handling_process"],
|
||||
risks=["Verzögerte Reaktion auf öffentlich bekannte Schwachstellen; Pflichtverletzung."],
|
||||
legal_basis_refs=["CRA Annex I Part II (1)", "CRA Annex I (2)(c)"],
|
||||
confidence=Confidence.HIGH,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="machinery_covers_cyber",
|
||||
triggers=[
|
||||
"maschinenrichtlinie deckt cyber", "maschinenvo deckt alles", "ce der maschine reicht",
|
||||
"ce maschine reicht für cyber", "maschinen-ce reicht",
|
||||
],
|
||||
verdict=InterpretationVerdict.PARTIALLY_CORRECT,
|
||||
corrected_interpretation=(
|
||||
"Die MaschinenVO deckt die sicherheitsrelevante Korrumpierung ab (Anhang III 1.1.9), "
|
||||
"ersetzt aber nicht die produktbezogenen CRA-Security-Pflichten. Beide Regime gelten parallel."
|
||||
),
|
||||
explanation="Maschinen-CE und CRA überschneiden sich nur dort, wo Cyber eine Sicherheitsfunktion betrifft.",
|
||||
affected_regulations=["CRA", "MaschinenVO"],
|
||||
affected_obligations=["machine_protection_against_corruption", "vuln_handling_process"],
|
||||
risks=["CRA-Pflichten werden übersehen, weil die Maschine bereits CE-gekennzeichnet ist."],
|
||||
legal_basis_refs=["MaschinenVO Anhang III (1.1.9)", "CRA Art. 13"],
|
||||
confidence=Confidence.MEDIUM,
|
||||
),
|
||||
InterpretationPattern(
|
||||
pattern_id="no_radio_no_cyber",
|
||||
triggers=[
|
||||
"ohne funkmodul kein cyber", "kein funk also kein cra", "ohne funk keine security",
|
||||
"ohne funkmodul keine cyber",
|
||||
],
|
||||
verdict=InterpretationVerdict.TOO_NARROW,
|
||||
corrected_interpretation=(
|
||||
"Der CRA knüpft an digitale Elemente an, nicht an ein Funkmodul. Ohne Funk entfällt die "
|
||||
"RED, der CRA bleibt jedoch anwendbar, sobald Software vorhanden ist."
|
||||
),
|
||||
explanation="Funkmodul ist nur für die RED relevant; die CRA-Anwendbarkeit folgt aus der Software.",
|
||||
affected_regulations=["CRA", "RED"],
|
||||
risks=["CRA wird fälschlich verneint, weil kein Funkmodul vorhanden ist."],
|
||||
legal_basis_refs=["CRA Art. 3(1)", "RED 2014/53/EU Art. 1"],
|
||||
confidence=Confidence.HIGH,
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user