Files
breakpilot-compliance/backend-compliance/reference_scenarios/_helpers.py
T
Benjamin Admin 9c02c2c4a2 feat(programs): start the Environmental Knowledge Program — domains, not architecture
The architecture is stable; from here the value comes from DOMAINS, not more software. Phase B is
organized as law-first Domain Knowledge Programs, each delivering the same production line: Corpus ->
Obligations -> Capabilities -> Transition Patterns -> Playbooks -> Reference Scenarios -> Completeness.
No new runtime framework (Freeze v1.0).

- knowledge/programs/README.md: reusable Domain Program blueprint (production line, per-stage ownership,
  law-first ordering, planned programs Environmental/Automotive/IEC62443/Functional-Safety).
- knowledge/programs/environmental.yaml: the Environmental domain as DATA. Law-first: B1 Environmental
  Regulatory Corpus (water/chemicals/emissions/energy/waste/product-responsibility — law + obligations
  only) -> B2 Capability Model -> B3 Transition Patterns (ISO 14001 -> corpus, built LAST). ISO 14001
  is a source state, NOT the domain.
- Ownership handoffs: B1 -> Legal Knowledge, B2 -> Compliance Execution, B3+/playbooks/reference ->
  Reasoning. Coordinate via the board; no session builds another's artifacts.
- reference suite: "Domain Knowledge Programs" section renders the program stages + a measurable
  Completeness baseline (6 areas, 0 assessed today) that flips automatically as stages land.
- ADR-008: from architecture to domains; Phase B as law-first programs; architecture frozen.

6 program-contract tests (law-first order + ownership pinned), check-loc 0. Knowledge data + ADR +
reference harness = non-runtime -> no deploy (ADR-001). No new module, no runtime change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 14:36:03 +02:00

177 lines
8.9 KiB
Python

# ruff: noqa
# mypy: ignore-errors
"""Rendering helpers for the Reference Scenario Suite generator.
Holds the shared mutable output buffers (OUT, ROLLUP) and the small markdown helpers so the
generator script (`generate.py`) stays under the LOC budget. Not product code; not imported by
the app — only by the generator (run via `PYTHONPATH=. python3 reference_scenarios/generate.py`).
"""
from __future__ import annotations
from typing import List, Tuple
Row = Tuple[str, str, str]
OUT: List[str] = []
ROLLUP: List[str] = []
def w(s: str = "") -> None:
OUT.append(s)
def coverage_table(rows: List[Row]) -> None:
w("**Architecture Coverage**")
w("")
w("| Layer | Status | Hinweis |")
w("|---|---|---|")
for layer, status, note in rows:
w("| %s | **%s** | %s |" % (layer, status, note))
ROLLUP.append(status)
w("")
def reg_map_block(rmap) -> None:
w("**Expected Regulatory Map**")
w("")
w("> " + rmap.executive_summary)
w("")
for v in rmap.applicable_regulations:
obs = ", ".join(o.obligation_id for o in v.obligations) or v.obligations_note
w("- **%s** (%s) — Pflichten: %s" % (v.regulation_id, v.name, obs))
for u in rmap.uncertain_regulations:
w("- _unsicher_ %s — fehlt: %s" % (u.regulation_id, ", ".join(u.missing_facts) or "-"))
for ov in rmap.overlaps:
w("- Overlap %s: %s" % (ov.overlap_group_id, ", ".join(ov.shared_obligations)))
for ev, ids in rmap.shared_evidence.items():
w("- 1 Nachweis `%s` => %d Pflichten" % (ev, len(ids)))
w("")
def unsupported_block(rmap) -> None:
w("**Expected Unsupported Domains**")
w("")
if not rmap.unsupported_domains:
w("- keine — alle getriggerten Domaenen sind im Korpus")
for d in rmap.unsupported_domains:
w("- `%s` (Trigger: %s) -> %s" % (d.domain, d.trigger, d.note))
w("")
def interp_status(verdict_value: str) -> str:
return "PARTIAL" if verdict_value in ("uncertain", "unsupported") else "PASS"
def knowledge_intake_section(base_dir) -> None:
"""Render the Knowledge Intake section (kept here so generate.py stays under the LOC budget)."""
import os
import yaml
from compliance.knowledge_intake import (
DocumentDescriptor, assess_document_impact, build_knowledge_index,
)
def _load(sub):
d = os.path.join(base_dir, "..", "knowledge", sub)
return [yaml.safe_load(open(os.path.join(d, f), encoding="utf-8"))
for f in sorted(os.listdir(d)) if f.endswith(".yaml")]
idx = build_knowledge_index(
_load("transition_patterns"), _load("implementation_playbooks"),
_load("reference_transition_scenarios"), obligation_index={"CRA": ["cra_obl_1", "cra_obl_2"]})
docs = [
DocumentDescriptor(document_id="ENISA CRA SBOM-FAQ", regulations=["CRA"], keywords=["sbom", "vulnerability"], document_type="faq"),
DocumentDescriptor(document_id="EU Umwelt-Leitfaden", regulations=["UmweltVO"], keywords=["wastewater"], document_type="guidance"),
DocumentDescriptor(document_id="Marketing-Blog", keywords=["newsletter"], document_type="blog"),
]
w("## Knowledge Intake — Impact zuerst, Extraktion später")
w("")
w('_Vor dem Parser: ein neues Dokument NUR einordnen und seinen Impact auf den bestehenden Wissensbestand bestimmen. „Von N Dokumenten verändern wenige tatsächlich unser Wissen." Deterministisch, keine Extraktion, kein LLM._')
w("")
w("| Dokument | Impact | betrifft | Empfehlung |")
w("|---|---|---|---|")
for d in docs:
kp = assess_document_impact(d, idx)
touch = "neue Domäne" if kp.new_domain else "%d%dPB·%dRTS·%dObl" % (
len(kp.affected_capabilities), len(kp.affected_playbooks),
len(kp.affected_reference_scenarios), len(kp.affected_obligations))
w("| %s | **%s** | %s | %s |" % (d.document_id, kp.impact_level.value, touch, kp.recommendation.split(" —")[0]))
w("")
w("**Beispiel-Knowledge-Package** (`%s`): %s" % (docs[0].document_id, assess_document_impact(docs[0], idx).impact_summary))
w("")
w('_So entsteht bei jedem neuen Dokument eine Impact-Analyse statt „200 Seiten PDF" — Targeted Updating statt Schreiben._')
w("")
coverage_table([
("Knowledge Intake (Klassifikation+Impact)", "PASS", "%d Regelwerke / %d Capabilities im Index" % (len(idx.regulations), len(idx.capability_regulations))),
("Impact-Triage (HIGH/LOW/NONE/new_domain)", "PASS", "3 Beispiel-Dokumente korrekt eingeordnet"),
("Regelwerk-ID-Normalisierung", "TODO", "CRA vs Cyber Resilience Act vereinheitlichen"),
])
def completeness_section() -> None:
"""Render the Regulatory Completeness section (kept here so generate.py stays under the LOC budget)."""
from compliance.completeness import assess_completeness
rep = assess_completeness(
identified_regulations=["CRA", "MaschinenVO", "EMV", "Environmental", "DataAct"],
corpus_status={"CRA": "validated", "MaschinenVO": "validated", "EMV": "unsupported",
"Environmental": "unsupported", "DataAct": "validated"},
uncertain=[{"regulation": "DataAct", "deciding_question": "generates_usage_data", "reason": "generates_usage_data = unbekannt"}],
assumptions=[{"key": "Funkmodul", "value": "nein"}, {"key": "personenbezogene Nutzungsdaten", "value": "nein"}],
assessed_obligations=128)
w("## Regulatory Completeness — was wir bewerten konnten, und was bewusst nicht")
w("")
w('_Interne Qualitätsmaschine (KEIN Confidence-Score): trennt IDENTIFIZIERT von BEWERTET und begründet jede Lücke. Keine Prozentzahl — auditierbar und ehrlich: „Wir zeigen auch, was wir noch nicht wissen und warum."_')
w("")
w("**%s**" % rep.completeness_summary)
w("")
w("> %s" % rep.audit_statement)
w("")
w("- **Bewertet:** %s (%d Pflichten)" % (", ".join(rep.assessed_regulations), rep.assessed_obligations))
w("- **Offen (jeweils begründet):**")
for e in rep.exclusions:
dq = (" → Rückfrage: `%s`" % e.deciding_question) if e.deciding_question else ""
w(" - `%s` — %s `[%s]`%s" % (e.subject, e.reason, e.resolution, dq))
w("- **Annahmen:** %s" % ", ".join("%s=%s" % (a.key, a.value) for a in rep.assumptions))
w("")
w("_Sobald der Umwelt-Korpus (ISO 14001 etc.) landet, kippt `Environmental` automatisch von offen auf bewertet — die Completeness Engine dokumentiert den Fortschritt je Domäne._")
w("")
coverage_table([
("Regulatory Completeness (auditierbar)", "PASS", rep.completeness_summary),
("Begründete Ausschlüsse (Korpus/Anwendbarkeit)", "PASS", "%d Ausschlüsse, alle mit Grund" % len(rep.exclusions)),
("Fortschritts-Doku je Domäne", "PASS", "Environmental offen→validated bei Korpus-Landung"),
])
def domain_programs_section(base_dir) -> None:
"""Render the Domain Knowledge Programs section (kept here so generate.py stays under the LOC budget)."""
import os
import yaml
from compliance.completeness import assess_completeness
pdir = os.path.join(base_dir, "..", "knowledge", "programs")
progs = [yaml.safe_load(open(os.path.join(pdir, f), encoding="utf-8"))
for f in sorted(os.listdir(pdir)) if f.endswith(".yaml")]
w("## Domain Knowledge Programs — ab jetzt Domänen, nicht Architektur")
w("")
w('_Die Runtime-Architektur ist eingefroren. Eine neue Domäne = Daten + Wissen, die jede Sicht automatisch erweitern. Produktionsstraße: Corpus→Obligations→Capabilities→Transition→Playbooks→Reference→Completeness. **Law-first: Recht → Pflichten → Capabilities → Managementsystem → Delta.**_')
w("")
for p in progs:
w("**%s** — _%s_ (status: `%s`)" % (p["name"], p["customer_question"], p["status"]))
w("")
w("| Stufe | Artefakt | Owner | Status |")
w("|---|---|---|---|")
for s in p.get("stages", []):
w("| %s | %s | %s | **%s** |" % (s["id"], s["name"], s["owner"], s["status"]))
w("")
areas = next((s.get("areas", []) for s in p.get("stages", []) if s.get("id") == "B1"), [])
if areas:
rep = assess_completeness(identified_regulations=areas, corpus_status={}) # all unknown -> open baseline
w("- **Baseline (Completeness):** %s — die 6 Bereiche: %s" % (rep.completeness_summary, ", ".join(areas)))
w("")
w("_Jedes Programm liefert dieselben Artefakte; Status `open/blocked` kippt automatisch, wenn die Stufen landen — Reference Suite + Completeness dokumentieren den Fortschritt je Domäne._")
w("")
coverage_table([
("Domain Program Blueprint (wiederverwendbar)", "PASS", "Corpus→…→Completeness, law-first, Ownership je Stufe"),
("Environmental Program (Daten)", "PASS", "B1@Legal-KG · B2@Execution · B3@Reasoning (blocked)"),
("Phase B = Domänen, keine Architektur", "PASS", "kein neues Runtime-Framework (Freeze, ADR-008)"),
])