78f0ffa9de
Roadmap item 4. After WHAT applies / WHAT is missing / WHICH first, the GF asks HOW. The Implementation Playbook renders, for one capability, the full journey — why / which regulations it closes / tools / process / evidence / controls — and chains the Optimization Roadmap into per-measure playbooks. Another renderer over the same Capability spine (ADR-003/004), not a new engine: ~95% of the data already exists, it just needs a different rendering. - compliance/playbook/: build_playbook() + playbooks_for_plan() (chains optimization -> playbook, acyclic; reuses leverage for "closes which regulations"). Capabilities without curated content render as honest status:missing stubs — the content-owed signal. - knowledge/implementation_playbooks/: curated knowledge layer (Reasoning Knowledge Acquisition), two deep expert drafts (SBOM, CVD/PSIRT, status draft, expert-draft-not-normative) + README. The bottleneck is now CONTENT, not software; Playbook (own knowledge) != regulatory domain. - ADR-004: Implementation Playbooks = renderer + knowledge layer; content is the bottleneck. - reference suite: "Implementation Playbook" section renders the SBOM journey + Roadmap->Playbook table (high-leverage caps flagged "fehlt (Inhalt)" — content backlog, highest leverage first). - refactor: extracted markdown helpers to reference_scenarios/_helpers.py to keep generate.py under the 500-LOC budget. 9 playbook tests (40 with optimization+transition+company), mypy --strict clean, check-loc 0. Product code with no app caller + knowledge/ADR/reference = non-runtime -> no deploy (ADR-001). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
89 lines
3.9 KiB
Python
89 lines
3.9 KiB
Python
"""Tests for the Implementation Playbook renderer (the Berater view, "wie komme ich dort hin?").
|
|
|
|
Acceptance: for one capability, assemble the journey (why / closes-which-regulations / tools /
|
|
process / evidence / controls) from curated knowledge + leverage + injected Execution links; chain
|
|
the Optimization Roadmap into per-measure playbooks; surface capabilities without content as honest
|
|
`missing` stubs. Curated content is an expert draft, never normative.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from compliance.optimization import regulatory_leverage
|
|
from compliance.playbook import Playbook, build_playbook, playbooks_for_plan
|
|
|
|
SBOM = {
|
|
"title": "SBOM aufbauen",
|
|
"why": "CRA verlangt ein Komponenten-Inventar.",
|
|
"tools": ["CycloneDX", "Syft"],
|
|
"process_steps": [{"title": "Format wählen", "detail": "CycloneDX"}, {"title": "In CI erzeugen", "detail": ""}],
|
|
"expected_evidence": ["sbom_per_release"],
|
|
"how_others_do_it": "CI + Dependency-Track.",
|
|
"status": "draft",
|
|
}
|
|
|
|
|
|
def test_build_playbook_full_journey():
|
|
pb = build_playbook("sbom_creation", SBOM, closes_regulations=["CRA", "MaschinenVO"], control_links=["component_inventory"])
|
|
assert pb.title == "SBOM aufbauen" and pb.status == "draft"
|
|
assert pb.closes_regulations == ["CRA", "MaschinenVO"] and pb.leverage == 2
|
|
assert pb.tools == ["CycloneDX", "Syft"]
|
|
assert [s.order for s in pb.process_steps] == [1, 2]
|
|
assert pb.process_steps[0].title == "Format wählen"
|
|
assert pb.expected_evidence == ["sbom_per_release"]
|
|
assert pb.controls == ["component_inventory"] # injected from Execution
|
|
assert pb.disclaimer # always carries the expert-draft caveat
|
|
|
|
|
|
def test_missing_knowledge_is_honest_stub():
|
|
pb = build_playbook("product_cyber_risk_assessment", None, closes_regulations=["CRA", "MaschinenVO"])
|
|
assert pb.status == "missing" # the content-owed signal
|
|
assert pb.leverage == 2 and pb.closes_regulations == ["CRA", "MaschinenVO"] # leverage still known
|
|
assert pb.tools == [] and pb.process_steps == []
|
|
assert "Knowledge Acquisition" in pb.why
|
|
|
|
|
|
def test_closes_regulations_deduped_and_sorted():
|
|
pb = build_playbook("x", SBOM, closes_regulations=["MaschinenVO", "CRA", "CRA"])
|
|
assert pb.closes_regulations == ["CRA", "MaschinenVO"] and pb.leverage == 2
|
|
|
|
|
|
def test_controls_default_empty_no_execution_data():
|
|
pb = build_playbook("x", SBOM, closes_regulations=["CRA"])
|
|
assert pb.controls == [] # no Execution data unless injected
|
|
|
|
|
|
def test_playbooks_for_plan_orders_by_leverage_and_stubs_missing():
|
|
caps = {"sbom_creation": ["CRA"], "pcra": ["CRA", "MaschinenVO"], "guards": ["MaschinenVO"]}
|
|
plan = regulatory_leverage(caps)
|
|
pbs = playbooks_for_plan(plan, {"sbom_creation": SBOM}, top_k=3)
|
|
assert [p.capability_id for p in pbs][0] == "pcra" # highest leverage first
|
|
by = {p.capability_id: p for p in pbs}
|
|
assert by["pcra"].status == "missing" and by["pcra"].leverage == 2
|
|
assert by["sbom_creation"].status == "draft" # has content
|
|
assert by["guards"].status == "missing"
|
|
|
|
|
|
def test_playbooks_for_plan_top_k_and_injected_controls():
|
|
caps = {"a": ["CRA", "MaschinenVO"], "b": ["CRA"], "c": ["CRA"]}
|
|
plan = regulatory_leverage(caps)
|
|
pbs = playbooks_for_plan(plan, {}, top_k=1, control_links_by_cap={"a": ["ctrl_1"]})
|
|
assert len(pbs) == 1 and pbs[0].capability_id == "a"
|
|
assert pbs[0].controls == ["ctrl_1"]
|
|
|
|
|
|
def test_playbooks_for_plan_empty():
|
|
plan = regulatory_leverage({})
|
|
assert playbooks_for_plan(plan, {}) == []
|
|
|
|
|
|
def test_deterministic():
|
|
caps = {"a": ["CRA", "MaschinenVO"], "b": ["CRA"]}
|
|
plan = regulatory_leverage(caps)
|
|
a = [p.capability_id for p in playbooks_for_plan(plan, {})]
|
|
b = [p.capability_id for p in playbooks_for_plan(plan, {})]
|
|
assert a == b
|
|
|
|
|
|
def test_returns_playbook_type():
|
|
assert isinstance(build_playbook("x", None), Playbook)
|