"""Knowledge Production — the Playbook Draft Generator (deterministic assembly + expert review). 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. The generator does not WRITE playbooks — it STRUCTURES drafts from data the software already owns (a transition/convergence pattern's delta requirement: why_asked, covers_targets, expected_evidence) plus injected Execution controls. The practitioner know-how (tools / process steps / how others do it) is left as an explicit TODO for the expert (or a separate offline-propose step). Fully deterministic, NO LLM in the core (deterministic-first: any model enrichment is offline, advisory, never in this assembly). No new corpus, no new meta-model class (freeze v1.0). Python 3.9. """ from __future__ import annotations from typing import Any, Dict, List, Optional from .schemas import DraftStatus, PlaybookDraft _SOFT_FIELDS = ["tools", "process_steps", "how_others_do_it"] # practitioner know-how — expert/offline-propose _DISCLAIMER = ( "Maschinell assemblierter ENTWURF aus vorhandenen Daten (Transition Pattern + Leverage + " "injizierte Controls). KEINE normative Anforderung; erfordert fachliche Kuratierung (TODO-Felder) " "und Statuswechsel draft_generated -> reviewed -> validated." ) def generate_playbook_draft( capability_id: str, requirement: Optional[Dict[str, Any]] = None, control_links: Optional[List[str]] = None, ) -> PlaybookDraft: """Assemble a playbook draft for ONE capability from a pattern delta requirement (deterministic). `requirement`: a delta_requirement dict (why_asked / covers_targets / expected_evidence). Owned fields are filled with provenance; soft fields are listed in `todo`. `control_links`: injected Execution controls (default empty — no Execution data in the draft generator). """ req = requirement or {} why = str(req.get("why_asked") or req.get("missing_because") or "") closes = sorted({str(t) for t in req.get("covers_targets", [])}) evidence = [str(e) for e in req.get("expected_evidence", [])] controls = list(control_links or []) provenance: Dict[str, str] = {} todo: List[str] = [] if why: provenance["why"] = "transition_pattern:why_asked" else: todo.append("why") if closes: provenance["closes_regulations"] = "leverage:covers_targets" if evidence: provenance["expected_evidence"] = "transition_pattern:expected_evidence" else: todo.append("expected_evidence") if controls: provenance["typical_controls"] = "execution:control_links" todo.extend(_SOFT_FIELDS) # always expert-owned return PlaybookDraft( capability_id=capability_id, status=DraftStatus.DRAFT_GENERATED, title=capability_id.replace("_", " "), why=why, closes_regulations=closes, expected_evidence=evidence, typical_controls=controls, provenance=provenance, todo=todo, disclaimer=_DISCLAIMER, ) def drafts_from_pattern( pattern: Dict[str, Any], control_links_by_cap: Optional[Dict[str, List[str]]] = None, ) -> List[PlaybookDraft]: """Assemble one playbook draft per delta capability of a transition/convergence pattern. This is the "produce drafts, don't write them" tool: feed a pattern -> get a draft per missing capability, ready for expert review. Deterministic + order-preserving (pattern order). """ links = control_links_by_cap or {} drafts: List[PlaybookDraft] = [] for d in pattern.get("delta_requirements", []): cap = d.get("capability") if not cap: continue drafts.append(generate_playbook_draft(str(cap), d, links.get(str(cap)))) return drafts