feat(convergence): first Regulatory Convergence Pattern (ISO27001 -> CRA + MaschinenVO)

The first multi-regulation pattern: each capability declares `covers_targets`, so we
can answer the convergence USP — "which capability satisfies CRA AND MaschinenVO at once?"

- knowledge: transition_pattern_iso27001_to_cra_maschinenvo_v1.yaml (pattern_type:
  regulatory_convergence, status draft). The cyber-safety bridge = MaschinenVO Annex III
  1.1.9 "protection against corruption" overlapping CRA integrity. 4 convergence
  capabilities cover BOTH; 5 CRA-only; 3 MaschinenVO-only.
- product: compliance/transition_reasoning/convergence.py — regulatory_convergence()
  pure/deterministic/computed-not-stored, no new graph/class (freeze v1.0 untouched).
  No app caller yet -> non-runtime, no deploy (ADR-001).
- reference suite: Cross-Regulation Capability Mapping section renders the customer
  sentence "von N neuen Massnahmen erfuellen M gleichzeitig CRA und MaschinenVO".
- README: term -> Regulatory Transition / Convergence Pattern; covers_targets documented.
- tests: test_regulatory_convergence (18 transition+company pass), mypy --strict clean.

Curated expert knowledge, AI first draft (L1/draft) — Annex/Article refs indicative,
review_required by a machinery-safety expert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-27 09:12:30 +02:00
parent caa9b8b609
commit 66be23f0c4
7 changed files with 222 additions and 6 deletions
@@ -131,3 +131,15 @@ def test_deterministic():
def test_empty():
a = assess_transition(_ctx())
assert a.question_requests == [] and a.coverage == []
# Cross-Regulation Capability Mapping: count capabilities that cover >= 2 regulations.
def test_regulatory_convergence():
from compliance.transition_reasoning import regulatory_convergence
ct = {"a": ["CRA"], "b": ["CRA", "MaschinenVO"], "c": ["MaschinenVO"], "d": ["CRA", "MaschinenVO"]}
c = regulatory_convergence(ct, ["CRA", "MaschinenVO"])
assert set(c.multi_target_capabilities) == {"b", "d"} # cover BOTH
assert set(c.single_target_capabilities) == {"a", "c"}
assert c.per_target_count == {"CRA": 3, "MaschinenVO": 3}
assert c.headline.startswith("2 von 4")