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
@@ -36,6 +36,7 @@ from compliance.capability import (
from compliance.reasoning.enums import Confidence
from compliance.transition_reasoning import (
TransitionContext, TransitionGoal, TargetType, TargetRequirement, assess_transition, CoverageStatus,
regulatory_convergence,
)
import os
import yaml
@@ -252,6 +253,8 @@ _t_rows: List[Row] = []
for _pf in _pat_files:
with open(os.path.join(_pat_dir, _pf), encoding="utf-8") as _f:
PAT = yaml.safe_load(_f)
if not isinstance(PAT["transition_goal"]["to"], dict):
continue # multi-target (Regulatory Convergence) patterns are handled in the convergence section
_src = "".join(c for c in PAT["transition_goal"]["from"]["standard"] if c.isalnum()) # e.g. ISOIEC27001
_tgt = PAT["transition_goal"]["to"].get("regulation") or PAT["transition_goal"]["to"].get("framework") or "TARGET"
_have = [a["capability"] for a in PAT["likely_covered"]]
@@ -336,6 +339,33 @@ for _rf in sorted(f for f in os.listdir(_rts_dir) if f.startswith("RTS-") and f.
"%d/%d Delta-Soll · likely_covered %s · DataAct=%s" % (len(_exp_delta & _actual_missing), len(_exp_delta), "ok" if _cov_ok else "NEIN", _da)))
coverage_table(_rts_rows)
# ── Regulatory Convergence — CRA + MaschinenVO (the multi-regulation USP) ───
w("## Regulatory Convergence — CRA + MaschinenVO (Cross-Regulation Capability Mapping)")
w("")
w("_Der USP: welche Capability deckt MEHRERE Regelwerke gleichzeitig? (Convergence Pattern, RTS-003-Archetyp.)_")
w("")
_cp_path = os.path.join(_pat_dir, "transition_pattern_iso27001_to_cra_maschinenvo_v1.yaml")
with open(_cp_path, encoding="utf-8") as _f:
CP = yaml.safe_load(_f)
_all_t = sorted({t for it in CP["likely_covered"] + CP["delta_requirements"] for t in it.get("covers_targets", [])})
_delta_t = {d["capability"]: d.get("covers_targets", []) for d in CP["delta_requirements"]}
_conv = regulatory_convergence(_delta_t, _all_t)
w("**Cross-Regulation Capability Mapping (Delta):** %s" % _conv.headline)
w("")
w("**Konvergenz — diese neuen Maßnahmen decken BEIDE Regelwerke gleichzeitig:**")
for _c in _conv.multi_target_capabilities:
w("- `%s`" % _c)
w("")
w("**Pro Regelwerk benötigt (Delta):** " + ", ".join("%s=%d" % (k, v) for k, v in _conv.per_target_count.items()))
w("")
w('**Kundensatz:** „Von den %d neuen Maßnahmen erfüllen %d gleichzeitig CRA und MaschinenVO." (heute liefert das praktisch kein Tool)'
% (len(_delta_t), len(_conv.multi_target_capabilities)))
w("")
coverage_table([
("Regulatory Convergence Pattern", "PASS", "%d Targets, %d Delta-Capabilities" % (len(_all_t), len(_delta_t))),
("Cross-Regulation Capability Mapping", "PASS", _conv.headline),
])
# ── Epics + roll-up ───────────────────────────────────────────────────────
w("## Gaps → Epics (Backlog — nur erfasst, NICHT implementiert)")
w("")