From 6523286af6261b6eb9ddbb118da7f3a750d611ee Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Wed, 1 Jul 2026 13:22:38 +0200 Subject: [PATCH] =?UTF-8?q?feat(registry-quality):=20scope-Achse=20?= =?UTF-8?q?=E2=80=94=202=20out=5Fof=5Fscope=20+=20derived=5Fobligation=20(?= =?UTF-8?q?User=20Option=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-Entscheidung 2026-07-01 zum Scope-Audit: Adressat der Norm != Handlungspflicht des Herstellers. Neue `scope`-Attribut-Achse (Enum, KEINE neue Objektklasse -> Freeze v1.0 unberuehrt): in_scope (default) / out_of_scope / derived_obligation. - sanctions + market_surveillance_safeguard -> out_of_scope (reine Staats-/Durchsetzungs- bestimmungen; Praezedenz CSIRT/ENISA im CRA-Vuln-Cut). Aus join_keys gefiltert. - notified_body_requirements -> derived_obligation (Norm adressiert primaer die notifizierte Stelle, erzeugt aber mittelbare Herstellerpflichten: NB einbeziehen + Unterlagen + Konformitaetsbewertung) + scope_split_candidate (spaetere Aufspaltung Normadressat <-> abgeleitete Herstellerpflicht). BLEIBT im Set (Prinzip: Wissen nicht zu frueh verwerfen). - export_join_keys.py filtert scope==out_of_scope + fuehrt scope je Eintrag -> join_keys 126->124 (MaschVO 31->29; 123 in_scope + 1 derived_obligation). - scope_audit.py jetzt 3-Wege-klassifikations-bewusst (0 unklassifizierte Reste) + apply_scope_classification.py (deterministisch). Fuer jeden kuenftigen Cut mitlaufen. Co-Authored-By: Claude Opus 4.7 --- obligations/cra_machinery.json | 14 +- obligations/obligation_join_keys.json | 742 ++++++++++-------- obligations/scope_audit_findings.json | 29 +- .../apply_scope_classification.py | 53 ++ .../obligation_discovery/export_join_keys.py | 3 + scripts/obligation_discovery/scope_audit.py | 70 +- 6 files changed, 539 insertions(+), 372 deletions(-) create mode 100644 scripts/obligation_discovery/apply_scope_classification.py diff --git a/obligations/cra_machinery.json b/obligations/cra_machinery.json index 6ceb6d52..a51f1821 100644 --- a/obligations/cra_machinery.json +++ b/obligations/cra_machinery.json @@ -4125,7 +4125,11 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "machinery" + "family": "machinery", + "scope": "derived_obligation", + "scope_reason": "Norm adressiert primär die notifizierte Stelle (Unabhängigkeit/Kompetenz/Unparteilichkeit), erzeugt aber mittelbare Hersteller-Pflichten: notifizierte Stelle einbeziehen, erforderliche Unterlagen bereitstellen, Konformitätsbewertung korrekt durchführen.", + "scope_split_candidate": true, + "scope_split_note": "Kandidat für spätere Aufspaltung: 'Normadressat' (Anforderungen AN die notifizierte Stelle = institutional/out_of_scope) ↔ 'abgeleitete Herstellerpflicht' (NB einbeziehen + Unterlagen + Konformitätsbewertung = in_scope). NICHT vorzeitig festziehen." }, { "id": "market_surveillance_safeguard", @@ -4204,7 +4208,9 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "machinery" + "family": "machinery", + "scope": "out_of_scope", + "scope_reason": "Adressat = Marktüberwachungsbehörden/Kommission (Schutzmaßnahmen, Schutzklauselverfahren); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA." }, { "id": "sanctions", @@ -4268,7 +4274,9 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "machinery" + "family": "machinery", + "scope": "out_of_scope", + "scope_reason": "Adressat = Mitgliedstaaten (legen Sanktionen fest); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA (CRA-Vuln-Cut)." }, { "id": "scope_transition_application", diff --git a/obligations/obligation_join_keys.json b/obligations/obligation_join_keys.json index b87ad077..19a7e3db 100644 --- a/obligations/obligation_join_keys.json +++ b/obligations/obligation_join_keys.json @@ -1,13 +1,14 @@ { "schema_version": "obligation_join_keys_v1", "contract": "obligation_id ist der stabile Join-Key. Legal Knowledge Graph haengt citation_spans an obligation_id; Compliance Execution Graph mappt control_mapping.source_norm -> obligation_id. Interim-Bruecke = citation_units. obligation_id NIE neu vergeben (re-link).", - "count": 126, + "count": 124, "obligation_ids": [ { "obligation_id": "sbom_creation", "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (1)" ], @@ -18,6 +19,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Art. 3(36) i.V.m. Annex I Part II (1)" ], @@ -28,6 +30,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (1)" ], @@ -38,6 +41,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (1)" ], @@ -48,6 +52,7 @@ "regulation": "CRA", "family": "sbom", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -56,6 +61,7 @@ "regulation": "CRA", "family": "sbom", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "IMPLEMENTATION" }, @@ -64,6 +70,7 @@ "regulation": "CRA", "family": "sbom", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -72,6 +79,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Art. 31 / Annex I Part II (1)" ], @@ -82,6 +90,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Art. 31(4)" ], @@ -92,6 +101,7 @@ "regulation": "CRA", "family": "sbom", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -100,6 +110,7 @@ "regulation": "CRA", "family": "sbom", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Art. 31 i.V.m. Annex VII" ], @@ -110,6 +121,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (1)" ], @@ -120,6 +132,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (1)" ], @@ -130,6 +143,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (2) & (8)" ], @@ -140,6 +154,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Article 13(8) & Annex VII" ], @@ -150,6 +165,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (5)" ], @@ -160,6 +176,7 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Article 14 & Article 16" ], @@ -170,36 +187,18 @@ "regulation": "CRA", "family": "vuln", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part II (4) & (6)" ], "source_role": "LEGAL_BASIS" }, - { - "obligation_id": "attack_surface_minimization", - "regulation": "CRA", - "family": "core", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Annex I Part I (2)(j)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "software_integrity_protection", - "regulation": "CRA", - "family": "core", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Annex I Part I (2)(f)" - ], - "source_role": "LEGAL_BASIS" - }, { "obligation_id": "user_authentication_required", "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(d)" ], @@ -210,6 +209,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -218,6 +218,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -226,6 +227,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -234,6 +236,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -242,6 +245,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -250,6 +254,7 @@ "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(e)" ], @@ -260,6 +265,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -268,6 +274,7 @@ "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(e)" ], @@ -278,6 +285,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -286,6 +294,7 @@ "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(a)" ], @@ -296,6 +305,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -304,6 +314,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -312,6 +323,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -320,6 +332,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -328,6 +341,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -336,6 +350,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -344,6 +359,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -352,6 +368,7 @@ "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(e)" ], @@ -362,6 +379,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -370,6 +388,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -378,6 +397,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -386,6 +406,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -394,6 +415,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -402,6 +424,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -410,6 +433,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -418,6 +442,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -426,6 +451,7 @@ "regulation": "CRA", "family": "authentication", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -434,16 +460,40 @@ "regulation": "CRA", "family": "authentication", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(c)" ], "source_role": "LEGAL_BASIS" }, + { + "obligation_id": "attack_surface_minimization", + "regulation": "CRA", + "family": "core", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Annex I Part I (2)(j)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "software_integrity_protection", + "regulation": "CRA", + "family": "core", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Annex I Part I (2)(f)" + ], + "source_role": "LEGAL_BASIS" + }, { "obligation_id": "event_logging_security_events", "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -454,6 +504,7 @@ "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -464,6 +515,7 @@ "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -474,6 +526,7 @@ "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -484,6 +537,7 @@ "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -494,6 +548,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -502,6 +557,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -510,6 +566,7 @@ "regulation": "CRA", "family": "logging", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I Part I (2)(k)" ], @@ -520,6 +577,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -528,6 +586,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -536,6 +595,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -544,6 +604,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -552,6 +613,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "IMPLEMENTATION" }, @@ -560,6 +622,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -568,6 +631,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -576,6 +640,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -584,6 +649,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -592,6 +658,7 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -600,6 +667,314 @@ "regulation": "CRA", "family": "logging", "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "risk_assessment_machinery_lifecycle", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1 (Allgemeine Grundsätze)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "risk_assessment_documentation", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang IV (Technische Unterlagen)" + ], + "source_role": "EVIDENCE" + }, + { + "obligation_id": "risk_assessment_methodology_competence", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "residual_risk_management", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.1.2 (Grundsätze für die Integration der Sicherheit)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "guards_protective_devices", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.4 (Anforderungen an Schutzeinrichtungen)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "emergency_stop_interlocking", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.2.4 (Stillsetzen, Not-Halt)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "safety_functions_design", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.2.1 (Sicherheit und Zuverlässigkeit von Steuerungen)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "safety_components_conformity", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang I (Liste der Sicherheitsbauteile), Art. 5" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "operating_instructions", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.7.4 (Betriebsanleitung)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "blocking_release_procedure", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.3.7/1.7.4" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "conformity_assessment", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Art. 25 (Konformitätsbewertungsverfahren)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "technical_documentation", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang IV (Technische Unterlagen)" + ], + "source_role": "EVIDENCE" + }, + { + "obligation_id": "eu_declaration_ce_marking", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Art. 21, Art. 22 (EU-Konformitätserklärung, CE-Kennzeichnung)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "manufacturer_economic_operator_obligations", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Art. 10, Art. 11 (Pflichten der Hersteller)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "essential_safety_requirements_compliance", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III (Grundlegende Sicherheits- und Gesundheitsschutzanforderungen)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "harmonised_standards_selection", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "notified_body_requirements", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "derived_obligation", + "citation_units": [ + "Kapitel IV (Notifizierung von Konformitätsbewertungsstellen)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "scope_transition_application", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Art. 1, Art. 53, Art. 54 (Anwendungsbereich, Übergangsbestimmungen, Geltungsbeginn)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "modification_substantial_change", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Art. 18 (wesentliche Veränderung)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "access_control_safety_functions", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.1.9 (Schutz gegen Korrumpierung)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "protection_against_corruption", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.1.9, Nr. 1.2.1" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "ml_safety_components", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang I Teil A, Anhang III Nr. 1.2.1" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "autonomous_mobile_machinery", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 3 (Mobile Maschinen) / Nr. 6" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "specific_machine_types", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 2-6 (besondere Maschinenkategorien)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "vibration_noise_emission", + "regulation": "MaschVO", + "family": "machinery", + "tier": "LEGAL_MINIMUM", + "scope": "in_scope", + "citation_units": [ + "Anhang III Nr. 1.5.8/1.5.9, Nr. 1.7.4.2" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "verification_inspection_maintenance", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "quality_management_system", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "long_term_availability_updates", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", + "citation_units": [], + "source_role": "GUIDANCE" + }, + { + "obligation_id": "security_functions_default_free", + "regulation": "MaschVO", + "family": "machinery", + "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -608,6 +983,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(2)(d)" ], @@ -618,6 +994,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(2)(b)(c)" ], @@ -628,6 +1005,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -636,6 +1014,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -644,6 +1023,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -652,6 +1032,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -660,6 +1041,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(2)(g)" ], @@ -670,6 +1052,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -678,6 +1061,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -686,6 +1070,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -694,6 +1079,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(2)(a)" ], @@ -704,6 +1090,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(1)" ], @@ -714,6 +1101,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -722,6 +1110,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -730,6 +1119,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -738,6 +1128,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -746,6 +1137,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -754,6 +1146,7 @@ "regulation": "CRA", "family": "remote_access", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -762,6 +1155,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(c)", "Art. 13" @@ -773,6 +1167,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Art. 13(8)" ], @@ -783,6 +1178,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(3)(f)" ], @@ -793,6 +1189,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(3)(d)" ], @@ -803,6 +1200,7 @@ "regulation": "CRA", "family": "updates", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -811,6 +1209,7 @@ "regulation": "CRA", "family": "updates", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "GUIDANCE" }, @@ -819,6 +1218,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (2)(c)" ], @@ -829,6 +1229,7 @@ "regulation": "CRA", "family": "updates", "tier": "LEGAL_MINIMUM", + "scope": "in_scope", "citation_units": [ "Annex I (1)(2)" ], @@ -839,306 +1240,9 @@ "regulation": "CRA", "family": "updates", "tier": "BEST_PRACTICE", + "scope": "in_scope", "citation_units": [], "source_role": "IMPLEMENTATION" - }, - { - "obligation_id": "risk_assessment_machinery_lifecycle", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1 (Allgemeine Grundsätze)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "risk_assessment_documentation", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang IV (Technische Unterlagen)" - ], - "source_role": "EVIDENCE" - }, - { - "obligation_id": "risk_assessment_methodology_competence", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" - }, - { - "obligation_id": "residual_risk_management", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.1.2 (Grundsätze für die Integration der Sicherheit)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "guards_protective_devices", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.4 (Anforderungen an Schutzeinrichtungen)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "emergency_stop_interlocking", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.2.4 (Stillsetzen, Not-Halt)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "safety_functions_design", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.2.1 (Sicherheit und Zuverlässigkeit von Steuerungen)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "safety_components_conformity", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang I (Liste der Sicherheitsbauteile), Art. 5" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "operating_instructions", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.7.4 (Betriebsanleitung)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "blocking_release_procedure", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.3.7/1.7.4" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "conformity_assessment", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 25 (Konformitätsbewertungsverfahren)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "technical_documentation", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang IV (Technische Unterlagen)" - ], - "source_role": "EVIDENCE" - }, - { - "obligation_id": "eu_declaration_ce_marking", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 21, Art. 22 (EU-Konformitätserklärung, CE-Kennzeichnung)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "manufacturer_economic_operator_obligations", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 10, Art. 11 (Pflichten der Hersteller)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "essential_safety_requirements_compliance", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III (Grundlegende Sicherheits- und Gesundheitsschutzanforderungen)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "harmonised_standards_selection", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" - }, - { - "obligation_id": "notified_body_requirements", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Kapitel IV (Notifizierung von Konformitätsbewertungsstellen)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "market_surveillance_safeguard", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Kapitel V/VI (Marktüberwachung, Schutzklauselverfahren)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "sanctions", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 50 (Sanktionen)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "scope_transition_application", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 1, Art. 53, Art. 54 (Anwendungsbereich, Übergangsbestimmungen, Geltungsbeginn)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "modification_substantial_change", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Art. 18 (wesentliche Veränderung)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "access_control_safety_functions", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.1.9 (Schutz gegen Korrumpierung)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "protection_against_corruption", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.1.9, Nr. 1.2.1" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "ml_safety_components", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang I Teil A, Anhang III Nr. 1.2.1" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "autonomous_mobile_machinery", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 3 (Mobile Maschinen) / Nr. 6" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "specific_machine_types", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 2-6 (besondere Maschinenkategorien)" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "vibration_noise_emission", - "regulation": "MaschVO", - "family": "machinery", - "tier": "LEGAL_MINIMUM", - "citation_units": [ - "Anhang III Nr. 1.5.8/1.5.9, Nr. 1.7.4.2" - ], - "source_role": "LEGAL_BASIS" - }, - { - "obligation_id": "verification_inspection_maintenance", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" - }, - { - "obligation_id": "quality_management_system", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" - }, - { - "obligation_id": "long_term_availability_updates", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" - }, - { - "obligation_id": "security_functions_default_free", - "regulation": "MaschVO", - "family": "machinery", - "tier": "BEST_PRACTICE", - "citation_units": [], - "source_role": "GUIDANCE" } ] } \ No newline at end of file diff --git a/obligations/scope_audit_findings.json b/obligations/scope_audit_findings.json index af4ff308..127a7cc4 100644 --- a/obligations/scope_audit_findings.json +++ b/obligations/scope_audit_findings.json @@ -1,20 +1,18 @@ { "audit": "obligation scope audit (Adressat: Hersteller vs Behörde/notified_body)", - "principle": "Registry modelliert Hersteller-Pflichten; Enforcement/Institutions-Recht = out_of_scope-Kandidat", + "principle": "Adressat der Norm != Handlungspflicht des Herstellers; scope-Achse in_scope/out_of_scope/derived_obligation", "false_positive_guard": "Melde-AN-Behörde-Pflichten (applicability=domain:products…) bleiben IN-SCOPE", "obligations_scanned": 126, - "out_of_scope_candidates": [ + "classified": [ { "file": "cra_machinery.json", "id": "notified_body_requirements", "name": "Anforderungen an notifizierte Stellen", "tier": "LEGAL_MINIMUM", "applicability": "domain:notified_body", - "subdomain": "notified_body", - "member_count": 11, - "reason": "Adressat ist Behörde/notifizierte Stelle/Mitgliedstaat, nicht Hersteller", - "precedent": "CRA-Vuln-Cut: CSIRT/ENISA out_of_scope (Adressat != Hersteller)", - "recommendation": "out_of_scope ODER eigene Kategorie 'institutional/enforcement'" + "scope": "derived_obligation", + "scope_reason": "Norm adressiert primär die notifizierte Stelle (Unabhängigkeit/Kompetenz/Unparteilichkeit), erzeugt aber mittelbare Hersteller-Pflichten: notifizierte Stelle einbeziehen, erforderliche Unterlagen bereitstellen, Konformitätsbewertung korrekt durchführen.", + "scope_split_note": "Kandidat für spätere Aufspaltung: 'Normadressat' (Anforderungen AN die notifizierte Stelle = institutional/out_of_scope) ↔ 'abgeleitete Herstellerpflicht' (NB einbeziehen + Unterlagen + Konformitätsbewertung = in_scope). NICHT vorzeitig festziehen." }, { "file": "cra_machinery.json", @@ -22,11 +20,8 @@ "name": "Marktüberwachung, nationale Schutzmaßnahmen und Korrekturmaßnahmen", "tier": "LEGAL_MINIMUM", "applicability": "domain:authority", - "subdomain": "market_surveillance", - "member_count": 30, - "reason": "Adressat ist Behörde/notifizierte Stelle/Mitgliedstaat, nicht Hersteller", - "precedent": "CRA-Vuln-Cut: CSIRT/ENISA out_of_scope (Adressat != Hersteller)", - "recommendation": "out_of_scope ODER eigene Kategorie 'institutional/enforcement'" + "scope": "out_of_scope", + "scope_reason": "Adressat = Marktüberwachungsbehörden/Kommission (Schutzmaßnahmen, Schutzklauselverfahren); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA." }, { "file": "cra_machinery.json", @@ -34,12 +29,10 @@ "name": "Sanktionen für Verstöße gegen die Maschinenverordnung", "tier": "LEGAL_MINIMUM", "applicability": "domain:authority", - "subdomain": "sanctions", - "member_count": 19, - "reason": "Adressat ist Behörde/notifizierte Stelle/Mitgliedstaat, nicht Hersteller", - "precedent": "CRA-Vuln-Cut: CSIRT/ENISA out_of_scope (Adressat != Hersteller)", - "recommendation": "out_of_scope ODER eigene Kategorie 'institutional/enforcement'" + "scope": "out_of_scope", + "scope_reason": "Adressat = Mitgliedstaaten (legen Sanktionen fest); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA (CRA-Vuln-Cut)." } ], - "decision_owner": "User/Registry-Owner — Audit FLAGGT nur, reklassifiziert nicht" + "unclassified_candidates": [], + "decision_owner": "User/Registry-Owner — Audit FLAGGT nur; für jeden künftigen Cut mitlaufen lassen" } \ No newline at end of file diff --git a/scripts/obligation_discovery/apply_scope_classification.py b/scripts/obligation_discovery/apply_scope_classification.py new file mode 100644 index 00000000..71caebd9 --- /dev/null +++ b/scripts/obligation_discovery/apply_scope_classification.py @@ -0,0 +1,53 @@ +"""Scope-Klassifikation anwenden (User-Entscheidung 2026-07-01, Option 2 + derived_obligation). + +Neue `scope`-Attribut-Achse (KEINE neue Objektklasse — Enum-Wert, freeze-safe): + in_scope (default/implizit) · out_of_scope · derived_obligation + +Prinzip (User): Adressat der Norm ⊥ Handlungspflicht des Herstellers. Reine Staats-/ +Durchsetzungs-/Institutions-Bestimmungen = out_of_scope. Norm, die primär eine andere Rolle +adressiert ABER mittelbar eine Hersteller-Handlungspflicht erzeugt = derived_obligation +(bleibt im Hersteller-Set, wird NICHT verworfen — 'im Zweifel nicht zu früh Wissen verwerfen'). +""" +from __future__ import annotations + +import glob +import json + +SCOPE = { + "sanctions": { + "scope": "out_of_scope", + "scope_reason": "Adressat = Mitgliedstaaten (legen Sanktionen fest); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA (CRA-Vuln-Cut).", + }, + "market_surveillance_safeguard": { + "scope": "out_of_scope", + "scope_reason": "Adressat = Marktüberwachungsbehörden/Kommission (Schutzmaßnahmen, Schutzklauselverfahren); keine Hersteller-Handlungspflicht. Präzedenz CSIRT/ENISA.", + }, + "notified_body_requirements": { + "scope": "derived_obligation", + "scope_reason": "Norm adressiert primär die notifizierte Stelle (Unabhängigkeit/Kompetenz/Unparteilichkeit), erzeugt aber mittelbare Hersteller-Pflichten: notifizierte Stelle einbeziehen, erforderliche Unterlagen bereitstellen, Konformitätsbewertung korrekt durchführen.", + "scope_split_candidate": True, + "scope_split_note": "Kandidat für spätere Aufspaltung: 'Normadressat' (Anforderungen AN die notifizierte Stelle = institutional/out_of_scope) ↔ 'abgeleitete Herstellerpflicht' (NB einbeziehen + Unterlagen + Konformitätsbewertung = in_scope). NICHT vorzeitig festziehen.", + }, +} + + +def main() -> None: + applied = [] + for f in sorted(glob.glob("obligations/cra*.json")): + d = json.load(open(f, encoding="utf-8")) + changed = False + for o in d.get("obligations", []): + spec = SCOPE.get(o.get("id")) + if spec: + o.update(spec) + applied.append((o["id"], spec["scope"])) + changed = True + if changed: + json.dump(d, open(f, "w", encoding="utf-8"), ensure_ascii=False, indent=1) + for oid, sc in applied: + print(f" {oid:32} scope={sc}") + print(f"angewendet: {len(applied)} (erwartet 3)") + + +if __name__ == "__main__": + main() diff --git a/scripts/obligation_discovery/export_join_keys.py b/scripts/obligation_discovery/export_join_keys.py index e25a6b19..0e08d551 100644 --- a/scripts/obligation_discovery/export_join_keys.py +++ b/scripts/obligation_discovery/export_join_keys.py @@ -23,12 +23,15 @@ def main() -> None: for path in a.registries: reg = json.load(open(path, encoding="utf-8")) for o in reg.get("obligations", []): + if o.get("scope") == "out_of_scope": + continue # Institutions-/Enforcement-Recht (Adressat != Hersteller) -> nicht im Join-Vertrag citation_units = [b.get("anchor", "") for b in o.get("legal_basis", []) if b.get("anchor")] keys.append({ "obligation_id": o["id"], "regulation": reg.get("regulation", ""), "family": o.get("family", ""), "tier": o.get("tier", ""), + "scope": o.get("scope", "in_scope"), "citation_units": citation_units, "source_role": o.get("source_role", ""), }) diff --git a/scripts/obligation_discovery/scope_audit.py b/scripts/obligation_discovery/scope_audit.py index 4595eddf..29af660b 100644 --- a/scripts/obligation_discovery/scope_audit.py +++ b/scripts/obligation_discovery/scope_audit.py @@ -1,24 +1,24 @@ """Scope-Audit: Adressaten-Prüfung der Obligation-Registry (Review-Stage-Werkzeug). -Prinzip (etabliert im CRA-Vuln-Cut, CSIRT/ENISA out_of_scope): die Registry modelliert -**Hersteller-Pflichten**. Bestimmungen, die an BEHÖRDEN / notifizierte Stellen / Mitgliedstaaten -adressiert sind (Marktüberwachung, Sanktionen, Anforderungen an Konformitätsbewertungsstellen), -sind Enforcement-/Institutions-Recht → out_of_scope-KANDIDATEN. +Prinzip (User 2026-07-01): **Adressat der Norm ⊥ Handlungspflicht des Herstellers.** Die Registry +modelliert Hersteller-Pflichten. `scope`-Achse (Attribut-Enum, KEINE neue Objektklasse — freeze-safe): + - in_scope : Norm adressiert direkt den Hersteller (default). + - out_of_scope : reines Staats-/Durchsetzungs-/Institutions-Recht (Adressat != Hersteller, + KEINE mittelbare Herstellerpflicht) — z.B. Sanktionen, Marktüberwachung. + Präzedenz CSIRT/ENISA (CRA-Vuln-Cut). Aus join_keys gefiltert. + - derived_obligation : Norm adressiert primär eine andere Rolle, erzeugt aber MITTELBAR eine + Hersteller-Handlungspflicht — bleibt im Set (Wissen nicht verwerfen), + ggf. `scope_split_candidate` (Aufspaltung Normadressat ↔ abgeleitete Pflicht). -WICHTIG (False-Positive-Abgrenzung): eine Hersteller-Pflicht, Etwas AN eine Behörde zu MELDEN -(z.B. `exploited_vuln_reporting_authorities`, applicability=products_with_digital_elements) ist -IN-SCOPE — Adressat der Pflicht = Hersteller, Behörde = nur Empfänger. Der Audit key't daher auf -`applicability` (Adressat), NICHT auf Behörden-Nennung im Namen. - -Deterministisch, kein LLM. Reklassifizierung = Owner-/User-Entscheidung (dieser Audit FLAGGT nur). -Für jeden künftigen Regulierungs-Cut mitlaufen lassen. +False-Positive-Guard: Melde-AN-Behörde-Pflichten (applicability=domain:products…) sind IN-SCOPE +(Adressat = Hersteller, Behörde nur Empfänger) — der Audit key't auf `applicability`, nicht auf +Behörden-Nennung im Namen. Deterministisch, kein LLM. Audit FLAGGT; Reklassifizierung = User/Owner. """ from __future__ import annotations import glob import json -# applicability-Präfixe, die einen NICHT-Hersteller-Adressaten bezeichnen NON_MANUFACTURER_DOMAINS = { "domain:authority", "domain:notified_body", @@ -29,39 +29,45 @@ NON_MANUFACTURER_DOMAINS = { def main() -> None: - findings = [] + classified = [] # bereits per scope entschieden + unclassified = [] # nicht-Hersteller-Adressat OHNE scope -> Review-Kandidat total = 0 for f in sorted(glob.glob("obligations/cra*.json")): d = json.load(open(f, encoding="utf-8")) for o in d.get("obligations", []): total += 1 appl = (o.get("applicability") or "").strip() - if appl in NON_MANUFACTURER_DOMAINS: - findings.append({ - "file": f.split("/")[-1], - "id": o.get("id") or o.get("obligation_id"), - "name": o.get("name"), - "tier": o.get("tier"), - "applicability": appl, - "subdomain": o.get("subdomain"), - "member_count": o.get("member_count"), - "reason": "Adressat ist Behörde/notifizierte Stelle/Mitgliedstaat, nicht Hersteller", - "precedent": "CRA-Vuln-Cut: CSIRT/ENISA out_of_scope (Adressat != Hersteller)", - "recommendation": "out_of_scope ODER eigene Kategorie 'institutional/enforcement'", - }) + scope = o.get("scope") + rec = { + "file": f.split("/")[-1], "id": o.get("id") or o.get("obligation_id"), + "name": o.get("name"), "tier": o.get("tier"), "applicability": appl, + "scope": scope, "scope_reason": o.get("scope_reason"), + } + if scope in ("out_of_scope", "derived_obligation"): + if o.get("scope_split_candidate"): + rec["scope_split_note"] = o.get("scope_split_note") + classified.append(rec) + elif appl in NON_MANUFACTURER_DOMAINS: + rec["reason"] = "Adressat Behörde/notifizierte Stelle/Mitgliedstaat, nicht klassifiziert" + rec["recommendation"] = "out_of_scope (reine Durchsetzung) ODER derived_obligation (mittelbare Herstellerpflicht)" + unclassified.append(rec) out = { "audit": "obligation scope audit (Adressat: Hersteller vs Behörde/notified_body)", - "principle": "Registry modelliert Hersteller-Pflichten; Enforcement/Institutions-Recht = out_of_scope-Kandidat", + "principle": "Adressat der Norm != Handlungspflicht des Herstellers; scope-Achse in_scope/out_of_scope/derived_obligation", "false_positive_guard": "Melde-AN-Behörde-Pflichten (applicability=domain:products…) bleiben IN-SCOPE", "obligations_scanned": total, - "out_of_scope_candidates": findings, - "decision_owner": "User/Registry-Owner — Audit FLAGGT nur, reklassifiziert nicht", + "classified": classified, + "unclassified_candidates": unclassified, + "decision_owner": "User/Registry-Owner — Audit FLAGGT nur; für jeden künftigen Cut mitlaufen lassen", } json.dump(out, open("obligations/scope_audit_findings.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1) - print(f"gescannt {total} Obligations | out_of_scope-Kandidaten: {len(findings)}") - for fnd in findings: - print(f" [{fnd['file']}] {fnd['id']:32} tier={fnd['tier']} appl={fnd['applicability']}") + print(f"gescannt {total} | klassifiziert {len(classified)} | unklassifizierte Kandidaten {len(unclassified)}") + for r in classified: + extra = " [SPLIT-KANDIDAT]" if r.get("scope_split_note") else "" + print(f" [{r['scope']}] {r['id']} appl={r['applicability']}{extra}") + for r in unclassified: + print(f" [UNKLASSIFIZIERT] {r['id']} appl={r['applicability']}") if __name__ == "__main__":