feat: Customer Mission #2 — the company arrives with a PROFILE, not a journey
Second mission, deliberately different from #1: a highly-certified company (ISO 9001 + ISO 27001 + ISO 14001 + TISAX + CE + PSIRT) asking „what do WE still need for the CRA?". Stresses Mission #1's one open seam (Scope → Journey) and proves the reframe with the real engines: - The start is a Company Capability Profile (certs aggregated), NOT a single cert→target journey. Certifications are OBSERVATIONS feeding the profile. - Evidence is target-relative: ISO 14001 is in the profile but irrelevant to the CRA; PSIRT covers two CRA-delta capabilities. More evidence = smaller delta (12 → 9). - The „journey" is the computed delta (Profile, Target) — not a thing a selector picks. This SHRINKS Mission #1's jump: the seam is profile-intake + target-pick, not a journey-matcher engine. There is no „ISO 27001 → CRA"; only „Profile → CRA". Records the 5 per-mission selection-rationale questions (which journey/why/decisive info/model-extended?/new-parameter?). Selector input = (Company Profile, Target), which collapses the 2^N cert-combination explosion. Non-runtime (reference_scenarios + tests only) -> no deploy. 6 tests pass; check-loc 0.
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
# Customer Mission #2 — „Wir haben SCHON viel. Was fehlt UNS noch für die CRA?"
|
||||||
|
|
||||||
|
_Zweite Mission, bewusst ANDERS als #1: nicht „ein Zertifikat → ein Ziel", sondern ein hoch-zertifiziertes Unternehmen, das mit einem ganzen Profil ankommt. Test der einzigen offenen Naht aus Mission #1 (Scope → Journey). Synthetischer Kunde, keine echten Namen._
|
||||||
|
|
||||||
|
## Der Kunde (synthetisch, hoch-zertifiziert)
|
||||||
|
> **ISO 9001** · **ISO 27001** · **ISO 14001** · **TISAX** · **CE-Prozess** · **PSIRT** · vernetzte Maschinen · Export EU
|
||||||
|
> **Eine Frage:** „Wir sind schon in vielem zertifiziert — was genau fehlt UNS noch, um CRA-konform zu sein?"
|
||||||
|
|
||||||
|
## 0. Company Capability Profile — der eigentliche Startzustand
|
||||||
|
> Das Unternehmen bringt **kein Zertifikat als Startpunkt**, sondern ein **Profil**. Jedes Zertifikat ist eine *Beobachtung*, die wahrscheinliche Fähigkeiten beisteuert; der Startzustand ist ihre **Aggregation**.
|
||||||
|
|
||||||
|
| Zertifikat (Beobachtung) | steuert Fähigkeiten bei | Vertrauen |
|
||||||
|
|---|---|---|
|
||||||
|
| **ISO27001** — Informationssicherheit (ISMS) | `incident_management`, `technical_vulnerability_management`, `access_control_and_authentication`, `secure_development_lifecycle`, `security_logging_and_monitoring` | medium |
|
||||||
|
| **TISAX** — Automotive-ISMS — verstärkt dieselben Infosec-Fähigkeiten | `incident_management`, `technical_vulnerability_management`, `access_control_and_authentication`, `secure_development_lifecycle`, `security_logging_and_monitoring` | medium |
|
||||||
|
| **PSIRT** — Product-Security-Incident-Response — deckt ZWEI CRA-Delta-Fähigkeiten | `coordinated_vulnerability_disclosure`, `exploited_vuln_and_incident_reporting` | high |
|
||||||
|
| **ISO9001** — QM-Dokumentendisziplin → CE-/Technische-Doku-Fähigkeit | `ce_conformity_assessment_and_technical_documentation` | medium |
|
||||||
|
| **ISO14001** — Umweltmanagement — im Profil, aber für die CRA NICHT relevant | `environmental_management_documentation` | medium |
|
||||||
|
|
||||||
|
→ **Evidence ist zielrelativ:** ISO 14001 liegt im Profil, hilft der CRA aber **nicht**; PSIRT (oft übersehen) deckt **zwei** CRA-kritische Delta-Fähigkeiten. Genau deshalb darf man **nicht ein Zertifikat** zur Journey machen — das ganze Profil zählt.
|
||||||
|
|
||||||
|
## 1. Ziel _(Intent)_ — was wollen Sie erreichen?
|
||||||
|
- Intent: **„CRA-konform werden"** → Ziel-Profil = **CRA** (die von der CRA geforderten Fähigkeiten).
|
||||||
|
- Auswahl-Eingabe ist damit **(Company Profile, Ziel)** — **kein** Zertifikat, das auf eine Journey gemappt wird.
|
||||||
|
|
||||||
|
## 2. Capability Delta — Profil → CRA _(das IST die „Journey")_
|
||||||
|
> 17 zu klären, 0 bereits abgedeckt, 8 vermutlich vorhanden, 9 fehlt, 0 n/a, 0 nicht im Korpus.
|
||||||
|
- **Delta dieses Profils:** 9 fehlende Fähigkeiten.
|
||||||
|
- **Gegenprobe (nur ISO 27001):** 12 fehlende Fähigkeiten.
|
||||||
|
- **Mehr Evidence → kleineres Delta:** die zusätzliche Zertifizierung schließt **3** Fähigkeiten *vorab*: `ce_conformity_assessment_and_technical_documentation`, `coordinated_vulnerability_disclosure`, `exploited_vuln_and_incident_reporting`.
|
||||||
|
|
||||||
|
→ Es wurde **keine Journey ausgewählt**. Das Delta `(Profil, Ziel)` IST die Journey — berechnet, nicht gewählt. Die Journey ist nur die *Erklärung* dieses Deltas.
|
||||||
|
|
||||||
|
## 3. Roadmap — was zuerst? _(gleicher Hebel-Engine wie Mission #1)_
|
||||||
|
> 12 identifizierte Anforderungen aus 2 Regelwerken -> 9 Massnahmen (Ø Hebel 1.3).
|
||||||
|
- Top-5 nach Hebel schließen **8 von 12** offenen Anforderungen (67%).
|
||||||
|
|
||||||
|
## Selektions-Rationale — die 5 Fragen (pro Mission zu dokumentieren)
|
||||||
|
|
||||||
|
**Welche Journey wurde gewählt?**
|
||||||
|
**Keine per-Zertifikat-Journey.** Die Journey ist `Company Capability Profile → CRA`. Gewählt wurde nur das **Ziel** (CRA); der Startzustand wurde aus ALLEN Zertifikaten aggregiert.
|
||||||
|
|
||||||
|
**Warum?**
|
||||||
|
Bei 6 Zertifikaten würde „ISO 27001 → CRA" die Evidence aus PSIRT/ISO 9001 wegwerfen (= 3 Fähigkeiten, die sonst fälschlich als Delta erschienen). Nur das **ganze Profil** ergibt das korrekte Delta.
|
||||||
|
|
||||||
|
**Welche Informationen waren für die Auswahl entscheidend?**
|
||||||
|
(a) das **Ziel/Intent** (CRA) und (b) die **vollständige Zertifikatsliste** als Profil — **nicht** ein einzelnes Zertifikat. Welche Evidence hilft, ist **zielrelativ** (ISO 14001 irrelevant, PSIRT hoch-relevant).
|
||||||
|
|
||||||
|
**Musste das Journey-Modell erweitert werden?**
|
||||||
|
**Konzeptionell ja, strukturell nein.** `from → to` bleibt; aber `from` ist ein **Company Capability Profile** (Multi-Cert-Aggregat), kein Zertifikat. Die Engines (`build_company_profile` + `assess_transition`) tun das BEREITS — es war ein Benenn-/Framing-Fehler, kein fehlender Code.
|
||||||
|
|
||||||
|
**Musste ein neuer Selektionsparameter eingeführt werden?**
|
||||||
|
**Ja — und er VEREINFACHT.** Eingabe ist `(Company Profile, Ziel)`, nicht `(Zertifikat, Ziel)`. Die Zertifikate kollabieren ins Profil → **keine 2^N Cert-Kombinationen**, nur Profil→Ziel. Der Selektor wird damit kleiner, nicht größer.
|
||||||
|
|
||||||
|
## Was Mission #2 an Mission #1 verändert
|
||||||
|
|
||||||
|
- Mission #1 nannte **Scope → Journey** einen Sprung („kein Selektor `certs × targets → journeys`"). Mission #2 zeigt: **diese Naht schrumpft.** Es gibt keinen Journey-Matcher zu bauen — die Journey ist das **berechnete Delta** `(Profil, Ziel)`. Was real fehlt, ist nur **Profil-Intake + Ziel-Wahl**, nicht eine Journey-Auswahl-Engine.
|
||||||
|
- Bestätigt den Reframe: **Es gibt keine „ISO 27001 → CRA"-Transition — nur „Company Capability Profile → CRA".** Zertifikate sind **Beobachtungen/Evidence**, kein Journey-Startpunkt.
|
||||||
|
- **Beobachtung für den (noch nicht gebauten) Selektor:** Eingabe = `(Company Profile, Ziel)`. Diversität über weitere Missionen muss zeigen, ob auch **Produktprofil** und **Intent-Klasse** als Parameter nötig werden — erst dann kanonisieren ([[rule-of-three-canonicalization]]).
|
||||||
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
# ruff: noqa
|
||||||
|
# mypy: ignore-errors
|
||||||
|
"""Customer Mission #2 — the company arrives with a PROFILE, not a journey.
|
||||||
|
|
||||||
|
Mission #1 (Maschinenbauer) ended on one open seam: „Scope → Journey" — a consultant had to hand-pick
|
||||||
|
„ISO 27001 → CRA". This mission deliberately STRESSES that seam with a multi-certified company (the
|
||||||
|
ETO-archetype: many certs at once) and asks the reframe question: is the start a *certification* you map
|
||||||
|
to a journey, or a *Company Capability Profile* you compute a delta from?
|
||||||
|
|
||||||
|
Finding (proven below with the real engines): there is NO per-certificate journey. The start is the whole
|
||||||
|
profile; certifications are OBSERVATIONS that feed it; more evidence = smaller delta (12 → 9). The
|
||||||
|
„journey" is not selected — it is the delta `(Company Profile, Target)`. That shrinks Mission #1's jump:
|
||||||
|
the seam is not a journey-matcher, it is profile-intake + target-pick.
|
||||||
|
|
||||||
|
Synthetic multi-certified company (NO real names). Runs the REAL engines end-to-end.
|
||||||
|
Run: cd backend-compliance && PYTHONPATH=. python3 reference_scenarios/mission_multicert_cra.py
|
||||||
|
Not product code; not imported by the app. Non-runtime -> no deploy.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from compliance.company import (
|
||||||
|
CompanyContext, Certification, CapabilityMappingEntry, build_company_profile,
|
||||||
|
)
|
||||||
|
from compliance.reasoning.enums import Confidence
|
||||||
|
from compliance.transition_reasoning import (
|
||||||
|
TransitionContext, TransitionGoal, TargetRequirement, assess_transition, CoverageStatus,
|
||||||
|
)
|
||||||
|
from compliance.optimization import roadmap_from_delta, select_within_budget
|
||||||
|
|
||||||
|
OUT = []
|
||||||
|
RAT = [] # (question, answer) — the per-mission selection rationale the user asked to record
|
||||||
|
|
||||||
|
|
||||||
|
def w(s=""):
|
||||||
|
OUT.append(s)
|
||||||
|
|
||||||
|
|
||||||
|
def rationale(question, answer):
|
||||||
|
RAT.append((question, answer))
|
||||||
|
|
||||||
|
|
||||||
|
_HERE = os.path.dirname(__file__)
|
||||||
|
_K = os.path.join(_HERE, "..", "knowledge")
|
||||||
|
CP = yaml.safe_load(open(os.path.join(_K, "transition_patterns",
|
||||||
|
"transition_pattern_iso27001_to_cra_maschinenvo_v1.yaml"), encoding="utf-8"))
|
||||||
|
|
||||||
|
w('# Customer Mission #2 — „Wir haben SCHON viel. Was fehlt UNS noch für die CRA?"')
|
||||||
|
w("")
|
||||||
|
w('_Zweite Mission, bewusst ANDERS als #1: nicht „ein Zertifikat → ein Ziel", sondern ein hoch-zertifiziertes Unternehmen, das mit einem ganzen Profil ankommt. Test der einzigen offenen Naht aus Mission #1 (Scope → Journey). Synthetischer Kunde, keine echten Namen._')
|
||||||
|
w("")
|
||||||
|
w("## Der Kunde (synthetisch, hoch-zertifiziert)")
|
||||||
|
w("> **ISO 9001** · **ISO 27001** · **ISO 14001** · **TISAX** · **CE-Prozess** · **PSIRT** · vernetzte Maschinen · Export EU")
|
||||||
|
w('> **Eine Frage:** „Wir sind schon in vielem zertifiziert — was genau fehlt UNS noch, um CRA-konform zu sein?"')
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── 0. Company Capability Profile — DER Startzustand (nicht ein Zertifikat) ──
|
||||||
|
# Each certification is an OBSERVATION yielding probable capabilities. The profile is their AGGREGATE.
|
||||||
|
# Evidence is TARGET-RELATIVE: ISO 14001 is in the profile but irrelevant to the CRA (honest).
|
||||||
|
infosec = [a["capability"] for a in CP["likely_covered"]]
|
||||||
|
CERT_OBS = {
|
||||||
|
"ISO27001": (infosec, Confidence.MEDIUM, "Informationssicherheit (ISMS)"),
|
||||||
|
"TISAX": (infosec, Confidence.MEDIUM, "Automotive-ISMS — verstärkt dieselben Infosec-Fähigkeiten"),
|
||||||
|
"PSIRT": (["coordinated_vulnerability_disclosure", "exploited_vuln_and_incident_reporting"],
|
||||||
|
Confidence.HIGH, "Product-Security-Incident-Response — deckt ZWEI CRA-Delta-Fähigkeiten"),
|
||||||
|
"ISO9001": (["ce_conformity_assessment_and_technical_documentation"],
|
||||||
|
Confidence.MEDIUM, "QM-Dokumentendisziplin → CE-/Technische-Doku-Fähigkeit"),
|
||||||
|
"ISO14001": (["environmental_management_documentation"],
|
||||||
|
Confidence.MEDIUM, "Umweltmanagement — im Profil, aber für die CRA NICHT relevant"),
|
||||||
|
}
|
||||||
|
cmap = {k: CapabilityMappingEntry(capability_ids=v[0], confidence=v[1]) for k, v in CERT_OBS.items()}
|
||||||
|
ctx = CompanyContext(company_id="mc", certifications=[Certification(certification_id=k) for k in CERT_OBS])
|
||||||
|
profile = build_company_profile(ctx, cmap)
|
||||||
|
w("## 0. Company Capability Profile — der eigentliche Startzustand")
|
||||||
|
w("> Das Unternehmen bringt **kein Zertifikat als Startpunkt**, sondern ein **Profil**. Jedes Zertifikat ist eine *Beobachtung*, die wahrscheinliche Fähigkeiten beisteuert; der Startzustand ist ihre **Aggregation**.")
|
||||||
|
w("")
|
||||||
|
w("| Zertifikat (Beobachtung) | steuert Fähigkeiten bei | Vertrauen |")
|
||||||
|
w("|---|---|---|")
|
||||||
|
for k, (caps, conf, note) in CERT_OBS.items():
|
||||||
|
w("| **%s** — %s | %s | %s |" % (k, note, ", ".join("`%s`" % c for c in caps), conf.value))
|
||||||
|
w("")
|
||||||
|
w("→ **Evidence ist zielrelativ:** ISO 14001 liegt im Profil, hilft der CRA aber **nicht**; PSIRT (oft übersehen) deckt **zwei** CRA-kritische Delta-Fähigkeiten. Genau deshalb darf man **nicht ein Zertifikat** zur Journey machen — das ganze Profil zählt.")
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── 1. Ziel (Intent) — was wollen SIE erreichen? ──────────────────────────
|
||||||
|
# The selection input is NOT a certificate. It is (Company Profile, Target). Intent = „CRA-konform werden".
|
||||||
|
TARGET = "CRA"
|
||||||
|
w("## 1. Ziel _(Intent)_ — was wollen Sie erreichen?")
|
||||||
|
w('- Intent: **„CRA-konform werden"** → Ziel-Profil = **CRA** (die von der CRA geforderten Fähigkeiten).')
|
||||||
|
w("- Auswahl-Eingabe ist damit **(Company Profile, Ziel)** — **kein** Zertifikat, das auf eine Journey gemappt wird.")
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── 2. Capability Delta — Profil → Ziel (das ist „die Journey") ────────────
|
||||||
|
reqs = [TargetRequirement(capability_id=a["capability"]) for a in CP["likely_covered"]]
|
||||||
|
reqs += [TargetRequirement(capability_id=d["capability"], question_intent=d.get("needed_information", "verify_existence"))
|
||||||
|
for d in CP["delta_requirements"]]
|
||||||
|
assess = assess_transition(TransitionContext(company_id="mc", target=TransitionGoal(target_id=TARGET)), reqs, profile)
|
||||||
|
missing = sorted({c.capability_id for c in assess.coverage if c.status == CoverageStatus.MISSING})
|
||||||
|
|
||||||
|
# counterfactual: a single-certificate company (ISO 27001 only) — to show evidence shrinks the delta.
|
||||||
|
single_prof = build_company_profile(
|
||||||
|
CompanyContext(company_id="s", certifications=[Certification(certification_id="ISO27001")]),
|
||||||
|
{"ISO27001": CapabilityMappingEntry(capability_ids=infosec, confidence=Confidence.MEDIUM)})
|
||||||
|
single_missing = sorted({c.capability_id for c in
|
||||||
|
assess_transition(TransitionContext(company_id="s", target=TransitionGoal(target_id=TARGET)), reqs, single_prof).coverage
|
||||||
|
if c.status == CoverageStatus.MISSING})
|
||||||
|
closed_by_evidence = sorted(set(single_missing) - set(missing))
|
||||||
|
|
||||||
|
w('## 2. Capability Delta — Profil → CRA _(das IST die „Journey")_')
|
||||||
|
w("> %s" % assess.summary.headline)
|
||||||
|
w("- **Delta dieses Profils:** %d fehlende Fähigkeiten." % len(missing))
|
||||||
|
w("- **Gegenprobe (nur ISO 27001):** %d fehlende Fähigkeiten." % len(single_missing))
|
||||||
|
w("- **Mehr Evidence → kleineres Delta:** die zusätzliche Zertifizierung schließt **%d** Fähigkeiten *vorab*: %s." % (
|
||||||
|
len(closed_by_evidence), ", ".join("`%s`" % c for c in closed_by_evidence)))
|
||||||
|
w("")
|
||||||
|
w("→ Es wurde **keine Journey ausgewählt**. Das Delta `(Profil, Ziel)` IST die Journey — berechnet, nicht gewählt. Die Journey ist nur die *Erklärung* dieses Deltas.")
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── 3. Roadmap — was zuerst? (gleicher Engine wie #1) ─────────────────────
|
||||||
|
delta_t = {d["capability"]: d.get("covers_targets", []) for d in CP["delta_requirements"]}
|
||||||
|
opt = roadmap_from_delta(assess, delta_t)
|
||||||
|
bud = select_within_budget({m.capability_id: m.covers for m in opt.ranked_measures}, 5)
|
||||||
|
w("## 3. Roadmap — was zuerst? _(gleicher Hebel-Engine wie Mission #1)_")
|
||||||
|
w("> %s" % opt.headline)
|
||||||
|
w("- Top-5 nach Hebel schließen **%d von %d** offenen Anforderungen (%d%%)." % (
|
||||||
|
bud.requirements_closed, bud.total_requirements, int(round(bud.coverage_ratio * 100))))
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── Selektions-Rationale (die vom User geforderten 5 Fragen) ───────────────
|
||||||
|
rationale("Welche Journey wurde gewählt?",
|
||||||
|
"**Keine per-Zertifikat-Journey.** Die Journey ist `Company Capability Profile → CRA`. Gewählt wurde nur das **Ziel** (CRA); der Startzustand wurde aus ALLEN Zertifikaten aggregiert.")
|
||||||
|
rationale("Warum?",
|
||||||
|
'Bei 6 Zertifikaten würde „ISO 27001 → CRA" die Evidence aus PSIRT/ISO 9001 wegwerfen (= 3 Fähigkeiten, die sonst fälschlich als Delta erschienen). Nur das **ganze Profil** ergibt das korrekte Delta.')
|
||||||
|
rationale("Welche Informationen waren für die Auswahl entscheidend?",
|
||||||
|
"(a) das **Ziel/Intent** (CRA) und (b) die **vollständige Zertifikatsliste** als Profil — **nicht** ein einzelnes Zertifikat. Welche Evidence hilft, ist **zielrelativ** (ISO 14001 irrelevant, PSIRT hoch-relevant).")
|
||||||
|
rationale("Musste das Journey-Modell erweitert werden?",
|
||||||
|
"**Konzeptionell ja, strukturell nein.** `from → to` bleibt; aber `from` ist ein **Company Capability Profile** (Multi-Cert-Aggregat), kein Zertifikat. Die Engines (`build_company_profile` + `assess_transition`) tun das BEREITS — es war ein Benenn-/Framing-Fehler, kein fehlender Code.")
|
||||||
|
rationale("Musste ein neuer Selektionsparameter eingeführt werden?",
|
||||||
|
"**Ja — und er VEREINFACHT.** Eingabe ist `(Company Profile, Ziel)`, nicht `(Zertifikat, Ziel)`. Die Zertifikate kollabieren ins Profil → **keine 2^N Cert-Kombinationen**, nur Profil→Ziel. Der Selektor wird damit kleiner, nicht größer.")
|
||||||
|
w("## Selektions-Rationale — die 5 Fragen (pro Mission zu dokumentieren)")
|
||||||
|
w("")
|
||||||
|
for q, a in RAT:
|
||||||
|
w("**%s** " % q)
|
||||||
|
w(a)
|
||||||
|
w("")
|
||||||
|
|
||||||
|
# ── Was diese Mission über Mission #1 verändert ───────────────────────────
|
||||||
|
w("## Was Mission #2 an Mission #1 verändert")
|
||||||
|
w("")
|
||||||
|
w('- Mission #1 nannte **Scope → Journey** einen Sprung („kein Selektor `certs × targets → journeys`"). Mission #2 zeigt: **diese Naht schrumpft.** Es gibt keinen Journey-Matcher zu bauen — die Journey ist das **berechnete Delta** `(Profil, Ziel)`. Was real fehlt, ist nur **Profil-Intake + Ziel-Wahl**, nicht eine Journey-Auswahl-Engine.')
|
||||||
|
w('- Bestätigt den Reframe: **Es gibt keine „ISO 27001 → CRA"-Transition — nur „Company Capability Profile → CRA".** Zertifikate sind **Beobachtungen/Evidence**, kein Journey-Startpunkt.')
|
||||||
|
w("- **Beobachtung für den (noch nicht gebauten) Selektor:** Eingabe = `(Company Profile, Ziel)`. Diversität über weitere Missionen muss zeigen, ob auch **Produktprofil** und **Intent-Klasse** als Parameter nötig werden — erst dann kanonisieren ([[rule-of-three-canonicalization]]).")
|
||||||
|
w("")
|
||||||
|
|
||||||
|
print("\n".join(OUT))
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
"""Customer Mission #2 — the company arrives with a PROFILE, not a journey.
|
||||||
|
|
||||||
|
Pins the reframe Mission #2 proves with the real engines: the start state is a Company Capability
|
||||||
|
Profile (many certs aggregated), certifications are observations/evidence, and more evidence shrinks
|
||||||
|
the delta (single-cert 12 → multi-cert 9). The „journey" is the computed delta `(Profile, Target)`,
|
||||||
|
not a thing a selector picks — which shrinks Mission #1's one open seam.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def _run_mission():
|
||||||
|
root = os.path.join(os.path.dirname(__file__), "..")
|
||||||
|
r = subprocess.run(
|
||||||
|
[sys.executable, "reference_scenarios/mission_multicert_cra.py"],
|
||||||
|
cwd=root, env={**os.environ, "PYTHONPATH": "."}, capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
assert r.returncode == 0, r.stderr
|
||||||
|
return r.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_mission_runs_end_to_end():
|
||||||
|
out = _run_mission()
|
||||||
|
assert "Customer Mission #2" in out
|
||||||
|
assert "Company Capability Profile — der eigentliche Startzustand" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_more_evidence_shrinks_the_delta():
|
||||||
|
out = _run_mission()
|
||||||
|
# multi-cert profile (9) must beat the single-cert counterfactual (12) — evidence is additive.
|
||||||
|
assert "**Delta dieses Profils:** 9" in out
|
||||||
|
assert "**Gegenprobe (nur ISO 27001):** 12" in out
|
||||||
|
assert "Mehr Evidence → kleineres Delta" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_reframe_no_per_certificate_journey():
|
||||||
|
out = _run_mission()
|
||||||
|
# the journey is the computed delta (Profile, Target), not a selected cert→target transition.
|
||||||
|
assert "Keine per-Zertifikat-Journey" in out
|
||||||
|
assert "Company Capability Profile → CRA" in out
|
||||||
|
assert "Es wurde **keine Journey ausgewählt**" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_five_selection_rationale_questions_present():
|
||||||
|
out = _run_mission()
|
||||||
|
for q in [
|
||||||
|
"Welche Journey wurde gewählt?",
|
||||||
|
"Warum?",
|
||||||
|
"Welche Informationen waren für die Auswahl entscheidend?",
|
||||||
|
"Musste das Journey-Modell erweitert werden?",
|
||||||
|
"Musste ein neuer Selektionsparameter eingeführt werden?",
|
||||||
|
]:
|
||||||
|
assert q in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_evidence_is_target_relative():
|
||||||
|
out = _run_mission()
|
||||||
|
# honest: ISO 14001 is in the profile but does not help the CRA; PSIRT covers two CRA-delta caps.
|
||||||
|
assert "ISO 14001" in out and "NICHT relevant" in out
|
||||||
|
assert "PSIRT" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_real_company_names():
|
||||||
|
out = _run_mission().lower()
|
||||||
|
for name in ["eto", "owis", "winterhalter"]:
|
||||||
|
assert name not in out
|
||||||
Reference in New Issue
Block a user