Files
breakpilot-compliance/backend-compliance/tests/test_knowledge_production.py
T
Benjamin Admin b6cfc0a503 feat(knowledge-production): Playbook Draft Generator — prepare the corpus deterministically
The bottleneck is not content, it is knowledge PRODUCTION. Instead of writing 200 playbooks by
hand, generate drafts deterministically from data the software already owns, then have an expert
review them. Mirrors the legal pipeline (Gesetz -> Parser -> Obligation -> Review) for BreakPilot's
own knowledge: new Capability -> Registry -> Transition Pattern -> Playbook Draft Generator ->
Expert Review -> versioned Playbook.

- compliance/knowledge_production/: generate_playbook_draft(capability, requirement, control_links)
  + drafts_from_pattern(pattern) -> one PlaybookDraft per delta capability. Owned fields (why /
  closes_regulations / expected_evidence / typical_controls) are assembled with per-field provenance;
  the practitioner know-how (tools / process_steps / how_others) is left as an explicit TODO.
- DraftStatus lifecycle (Freigabestatus): draft_generated -> in_review -> reviewed -> validated ->
  proven. Deterministic, NO LLM in the core (any model enrichment stays offline/advisory/propose-only).
- ADR-005: extends "the engine does not change, the corpus grows" with "and the corpus is not written
  by hand — it is deterministically prepared, then curated".
- reference suite: "Knowledge Production" section turns the convergence pattern into 12 auto-assembled
  drafts (why/closes/evidence filled, tools/steps TODO) -> review 12 drafts, don't write 12 playbooks.

10 tests (50 with playbook/optimization/transition/company), mypy --strict clean, check-loc 0.
Product code with no app caller + ADR/reference = non-runtime -> no deploy (ADR-001). Freeze-safe.

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

90 lines
3.7 KiB
Python

"""Tests for Knowledge Production — the Playbook Draft Generator.
Acceptance: deterministically assemble a playbook DRAFT for a capability from a transition-pattern
delta requirement (why / closes / evidence with provenance), leaving practitioner know-how as an
explicit TODO; turn a whole pattern into one draft per delta capability. No LLM, fully deterministic.
The expert reviews drafts instead of writing from a blank page.
"""
from __future__ import annotations
from compliance.knowledge_production import (
DraftStatus, PlaybookDraft, drafts_from_pattern, generate_playbook_draft,
)
REQ = {
"capability": "sbom_creation",
"why_asked": "CRA requires an SBOM; MaschinenVO does not.",
"covers_targets": ["CRA"],
"expected_evidence": ["sbom"],
}
CONV_REQ = {
"capability": "product_cyber_risk_assessment",
"why_asked": "Both require assessing cyber threats.",
"covers_targets": ["CRA", "MaschinenVO"],
"expected_evidence": ["product_risk_assessment"],
}
def test_assembles_owned_fields_with_provenance():
d = generate_playbook_draft("sbom_creation", REQ, control_links=["component_inventory"])
assert d.status == DraftStatus.DRAFT_GENERATED
assert d.why.startswith("CRA requires an SBOM")
assert d.closes_regulations == ["CRA"] and d.expected_evidence == ["sbom"]
assert d.typical_controls == ["component_inventory"]
assert d.provenance["why"] == "transition_pattern:why_asked"
assert d.provenance["closes_regulations"] == "leverage:covers_targets"
assert d.provenance["typical_controls"] == "execution:control_links"
def test_soft_fields_are_todo():
d = generate_playbook_draft("sbom_creation", REQ)
assert d.todo == ["tools", "process_steps", "how_others_do_it"] # practitioner know-how owed
def test_missing_owned_fields_go_to_todo():
d = generate_playbook_draft("x", {})
assert "why" in d.todo and "expected_evidence" in d.todo
assert d.closes_regulations == [] and d.typical_controls == []
assert d.status == DraftStatus.DRAFT_GENERATED
def test_missing_because_fallback_for_why():
d = generate_playbook_draft("x", {"missing_because": "no analogue in ISO 27001"})
assert d.why == "no analogue in ISO 27001" and "why" not in d.todo
def test_closes_deduped_sorted_and_title_humanised():
d = generate_playbook_draft("secure_signed_update_distribution", {"covers_targets": ["MaschinenVO", "CRA", "CRA"]})
assert d.closes_regulations == ["CRA", "MaschinenVO"]
assert d.title == "secure signed update distribution"
def test_controls_default_empty_no_execution_data():
d = generate_playbook_draft("x", REQ)
assert d.typical_controls == [] # nothing injected -> empty
def test_drafts_from_pattern_one_per_delta_in_order():
pattern = {"delta_requirements": [REQ, CONV_REQ]}
drafts = drafts_from_pattern(pattern)
assert [d.capability_id for d in drafts] == ["sbom_creation", "product_cyber_risk_assessment"]
assert drafts[1].closes_regulations == ["CRA", "MaschinenVO"] # leverage 2 carried through
def test_drafts_from_pattern_injects_controls_and_skips_unnamed():
pattern = {"delta_requirements": [REQ, {"why_asked": "no capability key"}]}
drafts = drafts_from_pattern(pattern, control_links_by_cap={"sbom_creation": ["c1"]})
assert len(drafts) == 1 and drafts[0].typical_controls == ["c1"] # entry without capability skipped
def test_deterministic():
pattern = {"delta_requirements": [REQ, CONV_REQ]}
a = [(d.capability_id, d.why, tuple(d.todo)) for d in drafts_from_pattern(pattern)]
b = [(d.capability_id, d.why, tuple(d.todo)) for d in drafts_from_pattern(pattern)]
assert a == b
def test_returns_playbookdraft_type():
assert isinstance(generate_playbook_draft("x", REQ), PlaybookDraft)