Merge pull request 'feat: Implementation Playbooks (Berater renderer over the Capability spine)' (#18) from feat/implementation-playbooks into main
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
"""Implementation Playbook — the Berater renderer ("wie komme ich dort hin?").
|
||||||
|
|
||||||
|
For one capability it assembles the full implementation journey (why / closes which regulations /
|
||||||
|
tools / process / evidence / controls) from curated playbook knowledge + regulatory leverage +
|
||||||
|
injected Execution links. `playbooks_for_plan` chains the Optimization Roadmap into per-measure
|
||||||
|
playbooks. Pure, deterministic, computed-not-stored. No new corpus, no new meta-model class
|
||||||
|
(freeze v1.0). Curated content = expert draft, never normative.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .engine import build_playbook, playbooks_for_plan
|
||||||
|
from .schemas import Playbook, PlaybookStep
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"build_playbook",
|
||||||
|
"playbooks_for_plan",
|
||||||
|
"Playbook",
|
||||||
|
"PlaybookStep",
|
||||||
|
]
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
"""Implementation Playbook — the Berater renderer ("wie komme ich dort hin?").
|
||||||
|
|
||||||
|
After the Capability Delta Engine says WHAT is missing and the Optimization renderer says WHICH
|
||||||
|
measure first, the Playbook renderer says HOW to implement it. For one capability it assembles the
|
||||||
|
full journey from three sources:
|
||||||
|
- curated playbook KNOWLEDGE (why / tools / process steps / evidence / how others do it) — the
|
||||||
|
Reasoning Knowledge Acquisition layer under `knowledge/implementation_playbooks/`,
|
||||||
|
- the regulatory LEVERAGE (which regulations a delivered capability closes) — reused from the
|
||||||
|
Optimization renderer,
|
||||||
|
- injected Procedure/Control/Evidence links (Execution-owned; empty until linked).
|
||||||
|
|
||||||
|
Pure, deterministic, computed-not-stored. Chains optimization -> playbook (acyclic). No new corpus,
|
||||||
|
no new meta-model class (freeze v1.0). Python 3.9 compatible.
|
||||||
|
|
||||||
|
The curated content is an EXPERT DRAFT, never a normative requirement. When no playbook knowledge
|
||||||
|
exists for a capability yet, the renderer emits a `status: missing` stub — the honest signal that
|
||||||
|
the bottleneck is CONTENT (Knowledge Acquisition), not software.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from ..optimization import OptimizationPlan
|
||||||
|
from .schemas import Playbook, PlaybookStep
|
||||||
|
|
||||||
|
_MISSING_WHY = "(Playbook-Inhalt fehlt — Knowledge Acquisition offen.)"
|
||||||
|
_DRAFT_DISCLAIMER = (
|
||||||
|
"Kuratiertes Experten-Wissen (Erstentwurf), KEINE normative Anforderung. Tools/Schritte sind "
|
||||||
|
"Empfehlungen, kein Pflichtkatalog; Controls werden aus der Execution-Schicht injiziert."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _steps(raw: Any) -> List[PlaybookStep]:
|
||||||
|
steps: List[PlaybookStep] = []
|
||||||
|
for i, s in enumerate(raw or [], 1):
|
||||||
|
steps.append(PlaybookStep(order=i, title=str(s.get("title", "")), detail=str(s.get("detail", ""))))
|
||||||
|
return steps
|
||||||
|
|
||||||
|
|
||||||
|
def build_playbook(
|
||||||
|
capability_id: str,
|
||||||
|
knowledge: Optional[Dict[str, Any]] = None,
|
||||||
|
closes_regulations: Optional[List[str]] = None,
|
||||||
|
control_links: Optional[List[str]] = None,
|
||||||
|
) -> Playbook:
|
||||||
|
"""Assemble the implementation journey for ONE capability.
|
||||||
|
|
||||||
|
`knowledge`: the curated playbook dict (None/empty -> a `missing` stub). `closes_regulations`:
|
||||||
|
the regulations a delivered capability closes (leverage, from `covers_targets`). `control_links`:
|
||||||
|
Execution-owned control refs, injected (default empty — no Execution data in Reasoning code).
|
||||||
|
"""
|
||||||
|
closes = sorted(set(closes_regulations or []))
|
||||||
|
if not knowledge:
|
||||||
|
return Playbook(
|
||||||
|
capability_id=capability_id, title=capability_id, why=_MISSING_WHY,
|
||||||
|
closes_regulations=closes, leverage=len(closes), controls=list(control_links or []),
|
||||||
|
status="missing", disclaimer=_DRAFT_DISCLAIMER,
|
||||||
|
)
|
||||||
|
return Playbook(
|
||||||
|
capability_id=capability_id,
|
||||||
|
title=str(knowledge.get("title", capability_id)),
|
||||||
|
why=str(knowledge.get("why", "")),
|
||||||
|
closes_regulations=closes,
|
||||||
|
leverage=len(closes),
|
||||||
|
tools=list(knowledge.get("tools", [])),
|
||||||
|
process_steps=_steps(knowledge.get("process_steps")),
|
||||||
|
expected_evidence=list(knowledge.get("expected_evidence", [])),
|
||||||
|
controls=list(control_links or []),
|
||||||
|
how_others_do_it=str(knowledge.get("how_others_do_it", "")),
|
||||||
|
status=str(knowledge.get("status", "draft")),
|
||||||
|
disclaimer=str(knowledge.get("disclaimer", _DRAFT_DISCLAIMER)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def playbooks_for_plan(
|
||||||
|
plan: OptimizationPlan,
|
||||||
|
knowledge_by_cap: Dict[str, Dict[str, Any]],
|
||||||
|
top_k: Optional[int] = None,
|
||||||
|
control_links_by_cap: Optional[Dict[str, List[str]]] = None,
|
||||||
|
) -> List[Playbook]:
|
||||||
|
"""Render playbooks for the highest-leverage measures of an OptimizationPlan (Roadmap -> How).
|
||||||
|
|
||||||
|
Walks the ranked measures (top_k, or all) and builds each capability's playbook, using the
|
||||||
|
measure's own `covers` as the regulations it closes. Measures without curated knowledge become
|
||||||
|
`missing` stubs — surfacing exactly where playbook content is still owed.
|
||||||
|
"""
|
||||||
|
links = control_links_by_cap or {}
|
||||||
|
measures = plan.ranked_measures if top_k is None else plan.ranked_measures[: max(0, top_k)]
|
||||||
|
return [
|
||||||
|
build_playbook(
|
||||||
|
m.capability_id, knowledge_by_cap.get(m.capability_id),
|
||||||
|
closes_regulations=m.covers, control_links=links.get(m.capability_id),
|
||||||
|
)
|
||||||
|
for m in measures
|
||||||
|
]
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""Schemas for the Implementation Playbook renderer.
|
||||||
|
|
||||||
|
A Playbook is a *derived view* (computed-not-stored): it assembles, for one capability, the full
|
||||||
|
"wie komme ich dort hin?" journey from (a) curated playbook KNOWLEDGE, (b) the regulatory leverage
|
||||||
|
(which regulations a delivered capability closes), and (c) injected Procedure/Control/Evidence links
|
||||||
|
(Execution-owned). Nothing here is persisted. No new meta-model class, no graph (freeze v1.0).
|
||||||
|
Python 3.9 compatible (no `|` unions).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class PlaybookStep(BaseModel):
|
||||||
|
"""One step in the recommended way to stand up a capability."""
|
||||||
|
|
||||||
|
order: int
|
||||||
|
title: str
|
||||||
|
detail: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class Playbook(BaseModel):
|
||||||
|
"""The complete implementation journey for ONE capability — the Berater view.
|
||||||
|
|
||||||
|
Answers, in order: Warum? -> Welche Regelwerke schliesst das? -> Welche Tools? -> Welche
|
||||||
|
Prozesse? -> Welche Nachweise? -> Welche Controls? The curated parts (why/tools/steps/evidence/
|
||||||
|
how-others) are an EXPERT DRAFT, not a normative requirement; controls are injected from
|
||||||
|
Execution (may be empty until linked).
|
||||||
|
"""
|
||||||
|
|
||||||
|
capability_id: str
|
||||||
|
title: str = ""
|
||||||
|
why: str = "" # why this is required (regulatory rationale)
|
||||||
|
closes_regulations: List[str] = Field(default_factory=list) # leverage: regulations a delivered cap closes
|
||||||
|
leverage: int = 0 # = len(closes_regulations)
|
||||||
|
tools: List[str] = Field(default_factory=list) # typical tooling (curated knowledge)
|
||||||
|
process_steps: List[PlaybookStep] = Field(default_factory=list) # how to stand it up
|
||||||
|
expected_evidence: List[str] = Field(default_factory=list) # artifacts that prove it
|
||||||
|
controls: List[str] = Field(default_factory=list) # control refs (injected from Execution; may be empty)
|
||||||
|
how_others_do_it: str = "" # "wie machen das andere?" (curated)
|
||||||
|
status: str = "draft" # draft -> reviewed -> validated -> proven
|
||||||
|
disclaimer: str = "" # expert draft, not a normative requirement
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Implementation Playbooks — curated knowledge ("wie komme ich dort hin?")
|
||||||
|
|
||||||
|
**Curated implementation KNOWLEDGE in machine-readable form — not an algorithm, not runtime code.**
|
||||||
|
After the Capability Delta Engine says WHAT is missing and the Optimization renderer says WHICH
|
||||||
|
measure first, a Playbook says HOW to implement one capability: why it is required, which tools and
|
||||||
|
process steps stand it up, which evidence proves it, which controls it maps to, and which regulatory
|
||||||
|
gaps it closes. The renderer is `compliance/playbook/`; this directory holds the content it renders.
|
||||||
|
|
||||||
|
Nothing imports these at runtime — `compliance/playbook` reads them and assembles a `Playbook`
|
||||||
|
view. Adding or curating a playbook is therefore **non-runtime → no deploy** (ADR-001).
|
||||||
|
|
||||||
|
## Playbook vs. regulatory domain (a deliberate distinction)
|
||||||
|
|
||||||
|
- A **Playbook** is BreakPilot's OWN knowledge layer: `Capability → recommended approach → tools →
|
||||||
|
process → typical evidence → controls`. It does not introduce a new regulation.
|
||||||
|
- A **regulatory domain** (e.g. ISO 14001 → environmental law) is a new *regulatory* knowledge
|
||||||
|
domain (obligations, applicability), owned with Legal Knowledge / Execution.
|
||||||
|
|
||||||
|
These scale independently. Once a domain lands, every new capability it needs can immediately be
|
||||||
|
embedded into the SAME playbook mechanism — which is why playbooks come first.
|
||||||
|
|
||||||
|
## The bottleneck is CONTENT, not software
|
||||||
|
|
||||||
|
The renderer is small and done. The value now grows with the **number and depth of playbooks**.
|
||||||
|
A capability with no playbook renders as a `status: missing` stub (the honest "content owed" signal).
|
||||||
|
This is Reasoning's Knowledge Acquisition responsibility (same as `../transition_patterns/`):
|
||||||
|
AI produces the first draft offline; BreakPilot reviews, versions and OWNS the library.
|
||||||
|
|
||||||
|
## Maturity lifecycle
|
||||||
|
|
||||||
|
`draft (L1) → reviewed (L2, internal) → validated (L3, domain expert) → proven (L4, field)`.
|
||||||
|
Curated content is an **EXPERT DRAFT, never a normative requirement**; tools/steps are recommended
|
||||||
|
practice, not a mandatory catalogue. Controls are **injected from the Execution layer** (may be
|
||||||
|
empty until linked) — no Execution data lives in the playbook content or in Reasoning product code.
|
||||||
|
|
||||||
|
## Schema (per file)
|
||||||
|
|
||||||
|
`id` · `capability_id` · `status` · `title` · `why` · `tools[]` · `process_steps[{title, detail}]`
|
||||||
|
· `expected_evidence[]` · `typical_controls[]` (indicative) · `how_others_do_it` · `disclaimer`.
|
||||||
|
`closes_regulations` / `leverage` are NOT stored here — the renderer supplies them from the
|
||||||
|
Optimization leverage (`covers_targets`), so one playbook serves every regulation it closes.
|
||||||
|
|
||||||
|
## Catalogue
|
||||||
|
|
||||||
|
| Playbook | Capability | status |
|
||||||
|
|---|---|---|
|
||||||
|
| `playbook_sbom_creation_v1.yaml` | sbom_creation | `draft` (L1) |
|
||||||
|
| `playbook_coordinated_vulnerability_disclosure_v1.yaml` | coordinated_vulnerability_disclosure (PSIRT) | `draft` (L1) |
|
||||||
|
|
||||||
|
Next candidates (high-leverage CRA/MaschinenVO delta): `security_update_support_period` ·
|
||||||
|
`secure_signed_update_distribution` · `product_cyber_risk_assessment`.
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
# Implementation Playbook — curated KNOWLEDGE (the "wie komme ich dort hin?" layer), not runtime code.
|
||||||
|
# Capability: coordinated_vulnerability_disclosure (PSIRT). Expert FIRST DRAFT — a product-security
|
||||||
|
# practitioner would validate this; NOT a normative requirement.
|
||||||
|
|
||||||
|
id: PB-coordinated_vulnerability_disclosure-v1
|
||||||
|
capability_id: coordinated_vulnerability_disclosure
|
||||||
|
status: draft # draft -> reviewed -> validated -> proven
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
title: "Coordinated Vulnerability Disclosure (CVD) / PSIRT aufbauen"
|
||||||
|
|
||||||
|
why: >
|
||||||
|
Der CRA verlangt eine Richtlinie zur koordinierten Offenlegung von Schwachstellen und einen
|
||||||
|
Meldekanal (Annex I Teil II) sowie deren Bearbeitung. Forscher und Nutzer brauchen einen Weg,
|
||||||
|
Schwachstellen verantwortungsvoll zu melden; der Hersteller braucht einen Prozess, um zu
|
||||||
|
triagieren, zu beheben und abgestimmt zu veröffentlichen. Diese Funktion heißt PSIRT (Product
|
||||||
|
Security Incident Response Team).
|
||||||
|
|
||||||
|
tools:
|
||||||
|
- "security.txt (RFC 9116) — maschinenlesbarer Sicherheitskontakt"
|
||||||
|
- "Veröffentlichte CVD-Policy (Webseite)"
|
||||||
|
- "Dediziertes PSIRT-Postfach + PGP-Schlüssel"
|
||||||
|
- "Triage-/Ticketsystem mit CVSS-Bewertung"
|
||||||
|
- "CSAF (Common Security Advisory Framework) für maschinenlesbare Advisories"
|
||||||
|
- "CVE Numbering Authority (CNA) — optional, für eigene CVE-Vergabe"
|
||||||
|
|
||||||
|
process_steps:
|
||||||
|
- title: "CVD-Policy + Kontakt veröffentlichen"
|
||||||
|
detail: "Öffentliche Richtlinie (Scope, Safe-Harbor, Fristen) + security.txt mit Kontakt/PGP."
|
||||||
|
- title: "Meldekanal betreiben"
|
||||||
|
detail: "PSIRT-Postfach/Portal, verschlüsselte Übermittlung, Eingangsbestätigung mit SLA."
|
||||||
|
- title: "Triage + Schweregrad"
|
||||||
|
detail: "Eingang reproduzieren, CVSS bewerten, Betroffenheit über die SBOM bestimmen."
|
||||||
|
- title: "Behebung koordinieren"
|
||||||
|
detail: "Fix + Offenlegungsfrist mit dem Melder abstimmen (Coordinated Disclosure)."
|
||||||
|
- title: "CVE zuweisen"
|
||||||
|
detail: "Über die eigene oder eine partnernde CNA eine CVE-ID vergeben."
|
||||||
|
- title: "Advisory veröffentlichen + Nutzer benachrichtigen"
|
||||||
|
detail: "Maschinenlesbares CSAF-Advisory; betroffene Nutzer/Behörden informieren (verbindet mit Meldepflicht)."
|
||||||
|
|
||||||
|
expected_evidence:
|
||||||
|
- "Veröffentlichte CVD-Policy + security.txt mit Kontakt"
|
||||||
|
- "PSIRT-Triage-Runbook (Rollen, Fristen, CVSS)"
|
||||||
|
- "Beispiel-Advisory (CSAF oder gleichwertig)"
|
||||||
|
|
||||||
|
typical_controls: # INDIKATIV — echte Control-Zuordnung kommt aus der Execution-Schicht
|
||||||
|
- "vulnerability_disclosure"
|
||||||
|
- "incident_response"
|
||||||
|
|
||||||
|
how_others_do_it: >
|
||||||
|
Verbreitete Praxis: security.txt + öffentliche CVD-Policy, ein PSIRT-Postfach mit PGP, Triage per
|
||||||
|
CVSS und Betroffenheitsanalyse über die SBOM, abgestimmte Veröffentlichung als CSAF-Advisory.
|
||||||
|
Viele Hersteller werden selbst CNA, um CVEs zeitnah vergeben zu können.
|
||||||
|
|
||||||
|
disclaimer: >
|
||||||
|
Kuratiertes Experten-Wissen (Erstentwurf), KEINE normative Anforderung. Werkzeug- und
|
||||||
|
Schrittempfehlungen sind bewährte Praxis, kein Pflichtkatalog. Review durch eine:n PSIRT-/
|
||||||
|
Product-Security-Expert:in ausstehend (status: draft).
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Implementation Playbook — curated KNOWLEDGE (the "wie komme ich dort hin?" layer), not runtime code.
|
||||||
|
# Capability: sbom_creation. Expert FIRST DRAFT — an SBOM practitioner would validate this; it is
|
||||||
|
# NOT a normative requirement. The renderer (compliance/playbook) assembles it into the journey.
|
||||||
|
|
||||||
|
id: PB-sbom_creation-v1
|
||||||
|
capability_id: sbom_creation
|
||||||
|
status: draft # draft -> reviewed -> validated -> proven
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
title: "Software Bill of Materials (SBOM) aufbauen und betreiben"
|
||||||
|
|
||||||
|
why: >
|
||||||
|
Der CRA verlangt von Herstellern, die Komponenten ihres Produkts zu identifizieren und zu
|
||||||
|
dokumentieren (Schwachstellenbehandlung, Annex I Teil II). Eine SBOM ist das maschinenlesbare
|
||||||
|
Inventar aller (auch transitiven) Software-Bestandteile mit Version und Lizenz. Ohne SBOM kann
|
||||||
|
niemand verlässlich sagen, welche Produkte von einer neuen Schwachstelle (CVE) betroffen sind —
|
||||||
|
SBOM ist damit die Voraussetzung für Schwachstellenüberwachung, Security-Updates und Meldepflichten.
|
||||||
|
|
||||||
|
tools:
|
||||||
|
- "CycloneDX (Format, OWASP)"
|
||||||
|
- "SPDX (Format, Linux Foundation)"
|
||||||
|
- "Syft (Generator, Container/Filesystem)"
|
||||||
|
- "cdxgen (Generator, Multi-Ökosystem)"
|
||||||
|
- "Trivy (Generator + Scan)"
|
||||||
|
- "OWASP Dependency-Track (Verwaltung + kontinuierliche Überwachung)"
|
||||||
|
|
||||||
|
process_steps:
|
||||||
|
- title: "Format festlegen"
|
||||||
|
detail: "CycloneDX oder SPDX wählen (maschinenlesbar). CycloneDX ist im Security-Kontext verbreitet."
|
||||||
|
- title: "Automatisch im Build erzeugen"
|
||||||
|
detail: "SBOM-Generierung (Syft/cdxgen/Trivy) als Schritt in die CI/CD-Pipeline einbauen — pro Build, nicht manuell."
|
||||||
|
- title: "Transitive Abhängigkeiten + Versionen + Lizenzen erfassen"
|
||||||
|
detail: "Nicht nur direkte Dependencies; gerade transitive Komponenten tragen die meisten CVEs."
|
||||||
|
- title: "Pro Release versionieren und ablegen"
|
||||||
|
detail: "Jede ausgelieferte Produktversion bekommt ihre eigene, unveränderliche SBOM (Nachvollziehbarkeit)."
|
||||||
|
- title: "An Schwachstellenüberwachung anbinden"
|
||||||
|
detail: "SBOM in Dependency-Track laden -> kontinuierlicher Abgleich gegen neue CVEs/Advisories."
|
||||||
|
- title: "Release-Gate setzen"
|
||||||
|
detail: "Auslieferung nur mit gültiger SBOM (Reifegrad-Schritt) — verbindet SBOM mit dem Update-Prozess."
|
||||||
|
|
||||||
|
expected_evidence:
|
||||||
|
- "Maschinenlesbare SBOM (CycloneDX/SPDX) je ausgelieferter Produktversion"
|
||||||
|
- "CI-Job-Konfiguration, die die SBOM automatisch erzeugt"
|
||||||
|
- "Dependency-Track-Projekt (oder gleichwertig) mit laufender Überwachung"
|
||||||
|
|
||||||
|
typical_controls: # INDIKATIV — echte Control-Zuordnung kommt aus der Execution-Schicht
|
||||||
|
- "component_inventory"
|
||||||
|
- "supply_chain_risk_management"
|
||||||
|
|
||||||
|
how_others_do_it: >
|
||||||
|
Verbreitete Praxis: CycloneDX automatisch in der CI via Syft/cdxgen erzeugen und nach
|
||||||
|
OWASP Dependency-Track pushen, das kontinuierlich gegen neue CVEs prüft. Reifere Organisationen
|
||||||
|
gaten Releases auf das Vorhandensein einer SBOM und teilen sie auf Anfrage mit Kunden/Behörden.
|
||||||
|
|
||||||
|
disclaimer: >
|
||||||
|
Kuratiertes Experten-Wissen (Erstentwurf), KEINE normative Anforderung. Werkzeug- und
|
||||||
|
Schrittempfehlungen sind Beispiele bewährter Praxis, kein Pflichtkatalog. Review durch eine:n
|
||||||
|
Product-Security-Expert:in ausstehend (status: draft).
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 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"
|
||||||
@@ -39,65 +39,19 @@ from compliance.transition_reasoning import (
|
|||||||
regulatory_convergence,
|
regulatory_convergence,
|
||||||
)
|
)
|
||||||
from compliance.optimization import roadmap_from_delta, select_within_budget
|
from compliance.optimization import roadmap_from_delta, select_within_budget
|
||||||
|
from compliance.playbook import playbooks_for_plan
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
Row = Tuple[str, str, str]
|
from _helpers import ( # noqa: E402 (script-dir module; keeps generate.py under the LOC budget)
|
||||||
OUT: List[str] = []
|
OUT, ROLLUP, Row, w, coverage_table, reg_map_block, unsupported_block, interp_status,
|
||||||
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("")
|
|
||||||
|
|
||||||
|
|
||||||
ISO_MAP = {"ISO27001": CapabilityMappingEntry(
|
ISO_MAP = {"ISO27001": CapabilityMappingEntry(
|
||||||
capability_ids=["cap_incident_response", "cap_supplier_management", "cap_asset_management"],
|
capability_ids=["cap_incident_response", "cap_supplier_management", "cap_asset_management"],
|
||||||
confidence=Confidence.MEDIUM)}
|
confidence=Confidence.MEDIUM)}
|
||||||
|
|
||||||
|
|
||||||
def interp_status(verdict_value: str) -> str:
|
|
||||||
return "PARTIAL" if verdict_value in ("uncertain", "unsupported") else "PASS"
|
|
||||||
|
|
||||||
|
|
||||||
w("# Reference Scenario Suite v1")
|
w("# Reference Scenario Suite v1")
|
||||||
w("")
|
w("")
|
||||||
w("> **Kein Doku-Artefakt — die erste Ground Truth / Living Reference Suite.** Erzeugt aus den "
|
w("> **Kein Doku-Artefakt — die erste Ground Truth / Living Reference Suite.** Erzeugt aus den "
|
||||||
@@ -442,6 +396,48 @@ coverage_table([
|
|||||||
("Budget-Priorisierung", "PASS", "Top-5 → %.0f%% der identifizierten Anforderungen" % (_bud.coverage_ratio * 100)),
|
("Budget-Priorisierung", "PASS", "Top-5 → %.0f%% der identifizierten Anforderungen" % (_bud.coverage_ratio * 100)),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# ── Implementation Playbook — Berater-Renderer (wie komme ich dort hin?) ───
|
||||||
|
w("## Implementation Playbook — wie komme ich dort hin? (Berater-Renderer)")
|
||||||
|
w("")
|
||||||
|
w('_Nach „was fehlt?" (Delta) und „womit anfangen?" (Hebel) die nächste Ebene: **wie umsetzen?** Pro Maßnahme eine komplette Reise aus kuratiertem Wissen + Hebel + (injizierten) Execution-Links. Inhalt ist der Engpass, nicht die Software._')
|
||||||
|
w("")
|
||||||
|
_pb_dir = os.path.join(os.path.dirname(__file__), "..", "knowledge", "implementation_playbooks")
|
||||||
|
_pb_kb = {}
|
||||||
|
for _pf in sorted(os.listdir(_pb_dir)):
|
||||||
|
if _pf.endswith(".yaml"):
|
||||||
|
with open(os.path.join(_pb_dir, _pf), encoding="utf-8") as _h:
|
||||||
|
_pd = yaml.safe_load(_h)
|
||||||
|
_pb_kb[_pd["capability_id"]] = _pd
|
||||||
|
_pbs = playbooks_for_plan(_opt, _pb_kb) # chain Roadmap -> Playbook over the SAME delta
|
||||||
|
_have = [p for p in _pbs if p.status != "missing"]
|
||||||
|
_miss = [p for p in _pbs if p.status == "missing"]
|
||||||
|
w("**Reise pro Maßnahme (aus der Roadmap):** %d von %d Maßnahmen haben ein Playbook; %d brauchen noch Inhalt (Knowledge Acquisition)." % (len(_have), len(_pbs), len(_miss)))
|
||||||
|
w("")
|
||||||
|
_show = next((p for p in _pbs if p.capability_id == "sbom_creation"), None)
|
||||||
|
if _show:
|
||||||
|
w("**Beispielreise — `%s`** _(%s, schließt %s)_" % (_show.capability_id, _show.status, "+".join(_show.closes_regulations) or "—"))
|
||||||
|
w("> **Warum?** %s" % _show.why.strip())
|
||||||
|
w("- **Tools:** %s" % ", ".join(_show.tools))
|
||||||
|
w("- **Prozess:** %s" % " → ".join(s.title for s in _show.process_steps))
|
||||||
|
w("- **Nachweise:** %s" % ", ".join(_show.expected_evidence))
|
||||||
|
w("- **Wie andere es tun:** %s" % _show.how_others_do_it.strip())
|
||||||
|
w("")
|
||||||
|
w("**Roadmap → Implementation (Top-Maßnahmen nach Hebel):**")
|
||||||
|
w("")
|
||||||
|
w("| Maßnahme | Hebel | schließt | Playbook |")
|
||||||
|
w("|---|---|---|---|")
|
||||||
|
for _p in _pbs[:6]:
|
||||||
|
w("| `%s` | %d | %s | %s |" % (_p.capability_id, _p.leverage, "+".join(_p.closes_regulations) or "—",
|
||||||
|
("✓ " + _p.status) if _p.status != "missing" else "**fehlt (Inhalt)**"))
|
||||||
|
w("")
|
||||||
|
w("_Derselbe Capability-Strang, neuer Renderer: aus Diagnose wird Beratung. Die `fehlt`-Einträge sind der ehrliche Content-Backlog (höchster Hebel zuerst befüllen)._")
|
||||||
|
w("")
|
||||||
|
coverage_table([
|
||||||
|
("Implementation Playbook Renderer", "PASS", "Reise pro Capability (why/tools/process/evidence/controls)"),
|
||||||
|
("Roadmap → Playbook (Verkettung)", "PASS", "%d/%d Maßnahmen mit Playbook" % (len(_have), len(_pbs))),
|
||||||
|
("Playbook-Inhalt (Knowledge)", "TODO" if _miss else "PASS", "%d Capabilities brauchen noch Inhalt" % len(_miss)),
|
||||||
|
])
|
||||||
|
|
||||||
# ── Epics + roll-up ───────────────────────────────────────────────────────
|
# ── Epics + roll-up ───────────────────────────────────────────────────────
|
||||||
w("## Gaps → Epics (Backlog — nur erfasst, NICHT implementiert)")
|
w("## Gaps → Epics (Backlog — nur erfasst, NICHT implementiert)")
|
||||||
w("")
|
w("")
|
||||||
|
|||||||
@@ -262,6 +262,40 @@ _Eine Wahrheit, zwei Renderer: dasselbe Capability Delta liefert dem Auditor **F
|
|||||||
| Roadmap/Management Renderer (Hebel) | **PASS** | 16 identifizierte Anforderungen aus 2 Regelwerken -> 12 Massnahmen (Ø Hebel 1.3). |
|
| Roadmap/Management Renderer (Hebel) | **PASS** | 16 identifizierte Anforderungen aus 2 Regelwerken -> 12 Massnahmen (Ø Hebel 1.3). |
|
||||||
| Budget-Priorisierung | **PASS** | Top-5 → 56% der identifizierten Anforderungen |
|
| Budget-Priorisierung | **PASS** | Top-5 → 56% der identifizierten Anforderungen |
|
||||||
|
|
||||||
|
## Implementation Playbook — wie komme ich dort hin? (Berater-Renderer)
|
||||||
|
|
||||||
|
_Nach „was fehlt?" (Delta) und „womit anfangen?" (Hebel) die nächste Ebene: **wie umsetzen?** Pro Maßnahme eine komplette Reise aus kuratiertem Wissen + Hebel + (injizierten) Execution-Links. Inhalt ist der Engpass, nicht die Software._
|
||||||
|
|
||||||
|
**Reise pro Maßnahme (aus der Roadmap):** 2 von 12 Maßnahmen haben ein Playbook; 10 brauchen noch Inhalt (Knowledge Acquisition).
|
||||||
|
|
||||||
|
**Beispielreise — `sbom_creation`** _(draft, schließt CRA)_
|
||||||
|
> **Warum?** Der CRA verlangt von Herstellern, die Komponenten ihres Produkts zu identifizieren und zu dokumentieren (Schwachstellenbehandlung, Annex I Teil II). Eine SBOM ist das maschinenlesbare Inventar aller (auch transitiven) Software-Bestandteile mit Version und Lizenz. Ohne SBOM kann niemand verlässlich sagen, welche Produkte von einer neuen Schwachstelle (CVE) betroffen sind — SBOM ist damit die Voraussetzung für Schwachstellenüberwachung, Security-Updates und Meldepflichten.
|
||||||
|
- **Tools:** CycloneDX (Format, OWASP), SPDX (Format, Linux Foundation), Syft (Generator, Container/Filesystem), cdxgen (Generator, Multi-Ökosystem), Trivy (Generator + Scan), OWASP Dependency-Track (Verwaltung + kontinuierliche Überwachung)
|
||||||
|
- **Prozess:** Format festlegen → Automatisch im Build erzeugen → Transitive Abhängigkeiten + Versionen + Lizenzen erfassen → Pro Release versionieren und ablegen → An Schwachstellenüberwachung anbinden → Release-Gate setzen
|
||||||
|
- **Nachweise:** Maschinenlesbare SBOM (CycloneDX/SPDX) je ausgelieferter Produktversion, CI-Job-Konfiguration, die die SBOM automatisch erzeugt, Dependency-Track-Projekt (oder gleichwertig) mit laufender Überwachung
|
||||||
|
- **Wie andere es tun:** Verbreitete Praxis: CycloneDX automatisch in der CI via Syft/cdxgen erzeugen und nach OWASP Dependency-Track pushen, das kontinuierlich gegen neue CVEs prüft. Reifere Organisationen gaten Releases auf das Vorhandensein einer SBOM und teilen sie auf Anfrage mit Kunden/Behörden.
|
||||||
|
|
||||||
|
**Roadmap → Implementation (Top-Maßnahmen nach Hebel):**
|
||||||
|
|
||||||
|
| Maßnahme | Hebel | schließt | Playbook |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `ce_conformity_assessment_and_technical_documentation` | 2 | CRA+MaschinenVO | **fehlt (Inhalt)** |
|
||||||
|
| `product_cyber_risk_assessment` | 2 | CRA+MaschinenVO | **fehlt (Inhalt)** |
|
||||||
|
| `protection_against_corruption_of_safety_functions` | 2 | CRA+MaschinenVO | **fehlt (Inhalt)** |
|
||||||
|
| `secure_signed_update_distribution` | 2 | CRA+MaschinenVO | **fehlt (Inhalt)** |
|
||||||
|
| `coordinated_vulnerability_disclosure` | 1 | CRA | ✓ draft |
|
||||||
|
| `exploited_vuln_and_incident_reporting` | 1 | CRA | **fehlt (Inhalt)** |
|
||||||
|
|
||||||
|
_Derselbe Capability-Strang, neuer Renderer: aus Diagnose wird Beratung. Die `fehlt`-Einträge sind der ehrliche Content-Backlog (höchster Hebel zuerst befüllen)._
|
||||||
|
|
||||||
|
**Architecture Coverage**
|
||||||
|
|
||||||
|
| Layer | Status | Hinweis |
|
||||||
|
|---|---|---|
|
||||||
|
| Implementation Playbook Renderer | **PASS** | Reise pro Capability (why/tools/process/evidence/controls) |
|
||||||
|
| Roadmap → Playbook (Verkettung) | **PASS** | 2/12 Maßnahmen mit Playbook |
|
||||||
|
| Playbook-Inhalt (Knowledge) | **TODO** | 10 Capabilities brauchen noch Inhalt |
|
||||||
|
|
||||||
## Gaps → Epics (Backlog — nur erfasst, NICHT implementiert)
|
## Gaps → Epics (Backlog — nur erfasst, NICHT implementiert)
|
||||||
|
|
||||||
| Epic | Titel | schliesst Coverage-Luecke |
|
| Epic | Titel | schliesst Coverage-Luecke |
|
||||||
@@ -273,6 +307,6 @@ _Eine Wahrheit, zwei Renderer: dasselbe Capability Delta liefert dem Auditor **F
|
|||||||
|
|
||||||
## Suite-Status (Roll-up)
|
## Suite-Status (Roll-up)
|
||||||
|
|
||||||
- Coverage-Zellen gesamt: **32**
|
- Coverage-Zellen gesamt: **35**
|
||||||
- PASS: **24** · PARTIAL: 3 · UNSUPPORTED: 1 · TODO: 3 · N/A: 1 · NEEDS_FACTS: 0
|
- PASS: **26** · PARTIAL: 3 · UNSUPPORTED: 1 · TODO: 4 · N/A: 1 · NEEDS_FACTS: 0
|
||||||
- Fortschritt = PASS-Anteil steigt, wenn Epics RS-001…004 landen (objektiver Maßstab, kein LOC).
|
- Fortschritt = PASS-Anteil steigt, wenn Epics RS-001…004 landen (objektiver Maßstab, kein LOC).
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
"""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)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# ADR-004: Implementation Playbooks — a renderer plus a knowledge layer
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Datum:** 2026-06-27
|
||||||
|
- **Typ:** Architektur-Entscheidung
|
||||||
|
- **Bezug:** [ADR-003](ADR-003-capability-delta-engine-with-renderers.md), [ADR-002](ADR-002-transition-is-data-not-architecture.md), Architektur-Freeze v1.0, [[transition-reasoning]]
|
||||||
|
|
||||||
|
## Kontext
|
||||||
|
|
||||||
|
BreakPilot beantwortet inzwischen drei Fragen: *Was gilt?* (Reasoning), *Was fehlt?*
|
||||||
|
(Capability Delta), *Womit anfangen?* (Optimization). Danach fragt der Geschäftsführer fast immer:
|
||||||
|
**„Wie komme ich dort hin?"** — nicht *was*, sondern *wie* (z. B. „Top-Maßnahme: PSIRT — und wie
|
||||||
|
baue ich einen PSIRT auf? Welche Tools? Wie machen das andere?").
|
||||||
|
|
||||||
|
Diese Umsetzungs-Ebene ist weder Reasoning noch Gap noch Roadmap. Das Risiko: sie als neue Engine
|
||||||
|
zu bauen — obwohl ~95 % der Daten bereits existieren (`Capability → Procedure → Control → Evidence`).
|
||||||
|
Sie muss nur **anders gerendert** werden.
|
||||||
|
|
||||||
|
## Entscheidung
|
||||||
|
|
||||||
|
1. **Ein Implementation Playbook ist ein RENDERER über einer Capability** (`compliance/playbook`),
|
||||||
|
kein neuer Datenpfad. Es setzt die Reise zusammen aus: kuratiertem Playbook-Wissen + der
|
||||||
|
regulatorischen **Leverage** (welche Regelwerke eine umgesetzte Capability schließt, aus der
|
||||||
|
Optimization) + **injizierten** Procedure/Control/Evidence-Links (Execution-owned).
|
||||||
|
|
||||||
|
2. **Playbook ≠ regulatorische Domäne (bewusste Unterscheidung):**
|
||||||
|
- Ein **Playbook** ist BreakPilots EIGENE Wissensschicht (`Capability → empfohlene Vorgehensweise
|
||||||
|
→ Tools → Prozess → typische Nachweise → Controls`). Es führt KEIN neues Regelwerk ein.
|
||||||
|
- Eine **regulatorische Domäne** (z. B. ISO 14001 → Umweltrecht) ist neues *regulatorisches*
|
||||||
|
Wissen (Obligations, Anwendbarkeit), Eigentum von Legal Knowledge / Execution.
|
||||||
|
Beide skalieren unabhängig — und jede neue Domäne dockt sofort an denselben Playbook-Mechanismus an.
|
||||||
|
|
||||||
|
3. **Der Engpass ist INHALT, nicht Software.** Der Renderer ist klein und fertig; der Wert wächst mit
|
||||||
|
Zahl und Tiefe der Playbooks. Eine Capability ohne Playbook rendert als `status: missing`-Stub —
|
||||||
|
das ehrliche „Content owed"-Signal. Playbook-Inhalt ist Reasoning-**Knowledge-Acquisition**
|
||||||
|
(wie `knowledge/transition_patterns/`): KI liefert den Erstentwurf, BreakPilot reviewt/versioniert/besitzt.
|
||||||
|
|
||||||
|
## Konsequenzen
|
||||||
|
|
||||||
|
- **Aus Diagnose wird Beratung:** derselbe Capability-Strang, der Auditor-Fragen (Interview) und
|
||||||
|
GF-Maßnahmen (Roadmap) liefert, liefert nun auch die Umsetzungsreise (Playbook). Konsistent mit
|
||||||
|
ADR-003 (ein Modell, viele Renderer).
|
||||||
|
- **Reifegrad + Ehrlichkeit:** Playbook-Inhalt ist `draft → reviewed → validated → proven`, ein
|
||||||
|
**Experten-Entwurf, KEINE normative Anforderung**; Tools/Schritte sind Empfehlungen. Controls
|
||||||
|
werden aus Execution injiziert — keine Execution-Daten im Reasoning-Produktcode.
|
||||||
|
- **Freeze-konform:** kein neues Metamodell, kein neuer Graph, kein neuer Corpus — `Playbook` ist
|
||||||
|
eine abgeleitete Sicht (computed-not-stored). Abhängigkeit `playbook → optimization →
|
||||||
|
transition_reasoning` ist azyklisch; die Capability Delta Engine bleibt hermetisch.
|
||||||
|
- **Priorisierung:** Playbooks kommen VOR neuen Domänen (ISO 14001), damit jede künftige Capability
|
||||||
|
sofort eine Umsetzungsreise bekommt. Content-Backlog = höchster Hebel zuerst.
|
||||||
|
- Diese ADR ist non-runtime → kein Deploy (siehe [ADR-001](ADR-001-runtime-deploy-policy.md)).
|
||||||
Reference in New Issue
Block a user