Merge pull request 'Customer Mission #5 — a non-security target (evidence relevance flips)' (#33) from feat/customer-mission-5-non-security into main

This commit is contained in:
pilotadmin
2026-06-28 10:18:52 +02:00
3 changed files with 211 additions and 0 deletions
@@ -0,0 +1,27 @@
# Customer Mission #5 — ein Nicht-Security-Ziel: kippt die Evidence-Relevanz?
_Enger Scope: KEIN Umweltrecht, KEINE ISO-14001-Normmodellierung, KEIN neues Modul, KEIN Deploy. Nur die EINE Frage: ist `relevance(evidence, target)` wirklich eine Funktion des Ziels — oder ein Attribut der Evidence? Das Umwelt-/Materialnachweis-Ziel ist ein hand-authored `Required`-Satz (synthetisch), nur um die bestehende Engine auf ein Nicht-Security-Ziel zu richten. Synthetischer Kunde, keine echten Namen._
## Der Kunde (synthetisch) — EIN Profil (hat u. a. ISO 14001)
> **ISO 9001 · ISO 27001 · ISO 14001 · TISAX · PSIRT** · vernetzte Maschinen · Export EU
## 1. Evidence-Relevanz über drei Ziele — zwei Security, eines NICHT
| Zertifizierung (Evidence) | → CRA | → TISAX | → Umwelt-/Material |
|---|---|---|---|
| **ISO27001** | hoch (5) | hoch (3) | keine (0) |
| **TISAX** | niedrig (2) | hoch (6) | keine (0) |
| **PSIRT** | hoch (3) | keine (0) | keine (0) |
| **ISO14001** | keine (0) | keine (0) | hoch (3) | ⟵ kippt
| **ISO9001** | niedrig (1) | keine (0) | keine (0) |
## 2. Beweis — Relevanz ist eine Funktion des ZIELS (in BEIDE Richtungen)
- **ISO 14001:** gegen CRA/TISAX **keine**, gegen das Umwelt-/Materialziel **hoch (3)**. Dieselbe Zertifizierung — von wertlos zu entscheidend, nur weil das Ziel wechselt.
- **Symmetrisch:** **ISO 27001** (hoch gegen CRA/TISAX) ist gegen das Umwelt-/Materialziel **keine (0)**; **PSIRT** ebenso **keine (0)**. Security-Evidence ist hier wertlos.
- Delta des Umwelt-/Materialziels: **5** fehlende Fähigkeiten (das Profil deckt nur die ISO-14001-nahen ab) — über dieselbe `assess_transition`-Engine, kein Sonderpfad.
**Damit ist `relevance(evidence, target)` zweiseitig bewiesen:** keine Evidence ist „an sich" relevant; Relevanz entsteht erst gegen ein Ziel. Eine Capability/Zertifizierung ohne Ziel hat keinen Relevanzwert.
## Befund
> **Ein und dieselbe Evidence kann je Ziel wertlos oder hoch relevant sein** — hier erstmals an einem NICHT-Security-Ziel gezeigt, in beide Richtungen (ISO 14001 kippt von keine→hoch, Security-Certs von hoch→keine). Folgerung für das spätere Modell: Relevanz darf NICHT als Attribut der Evidence gespeichert werden, sondern nur als `relevance(evidence, target)` berechnet (computed-not-stored). **Damit ist die Ziel-Diversität für den späteren Selektor beisammen: Regulation · Certification · Contract/Tender · OEM-Spec · Umwelt-/Material-Ziel — fünf Zielarten durch dieselbe Engine. Erst jetzt wird ein Scope→Journey-Selektor sinnvoll** (er optimiert nicht mehr auf einer einzigen Zielart).
@@ -0,0 +1,129 @@
# ruff: noqa
# mypy: ignore-errors
"""Customer Mission #5 — a NON-security target: does evidence relevance really flip?
The whole „Evidence-Relevance(Target)" claim is only convincing if it holds in BOTH directions. Missions
#2#4 showed security evidence (ISO 27001, PSIRT) ranking differently across security targets, and ISO
14001 being worthless against the CRA/TISAX. This mission closes the loop with a deliberately NON-security
target — an environmental / material-evidence requirement set — and asks the one question:
is `relevance(evidence, target)` genuinely a function of the TARGET, or an attribute of the evidence?
Expected and shown: ISO 14001 is keine/niedrig against CRA and TISAX but HOCH against the environmental
target — while the security certificates (ISO 27001, PSIRT) flip the other way (relevant for security,
keine for the environmental target). The same evidence is worthless or decisive depending on the target.
DELIBERATELY NOT here (per scope): no environmental corpus, no ISO-14001 norm model, no new runtime
module, no deploy, no real names. The environmental target is a hand-authored Required set (injected like
any TargetRequirement) purely to point the existing engine at a non-security goal.
Run: cd backend-compliance && PYTHONPATH=. python3 reference_scenarios/mission_non_security_target.py
"""
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,
)
OUT = []
def w(s=""):
OUT.append(s)
_K = os.path.join(os.path.dirname(__file__), "..", "knowledge", "transition_patterns")
def _caps(pattern_file):
p = yaml.safe_load(open(os.path.join(_K, pattern_file), encoding="utf-8"))
return [a["capability"] for a in p["likely_covered"]] + [d["capability"] for d in p["delta_requirements"]]
# ── Three targets — two security, one NON-security (the new axis) ───────────────────────────
CRA = _caps("transition_pattern_iso27001_to_cra_maschinenvo_v1.yaml") # security regulation
TISAX = _caps("transition_pattern_isms_to_tisax_v1.yaml") # security certification
# NON-security target: an environmental / material-evidence requirement set. Hand-authored Required
# capabilities (NOT a corpus, NOT an ISO-14001 norm model) — just a goal to point the engine at.
ENVIRONMENTAL = [
"environmental_management_documentation", "energy_efficiency_documentation",
"supply_chain_environmental_due_diligence", "material_declaration_scip_reach",
"hazardous_substance_restriction_rohs", "carbon_footprint_accounting",
"recycling_and_take_back", "battery_passport_material_data",
]
TARGETS = [("CRA", "Security/Regulation", CRA), ("TISAX", "Security/Certification", TISAX),
("Umwelt-/Materialnachweis", "NICHT-Security", ENVIRONMENTAL)]
# ── ONE company profile (the same multi-certified archetype; it HAS ISO 14001) ──────────────
CERT_OBS = {
"ISO27001": ["information_security_management", "incident_management", "access_control_and_authentication",
"technical_vulnerability_management", "security_logging_and_monitoring", "secure_development_lifecycle"],
"TISAX": ["information_security_management", "access_control_and_authentication", "incident_management",
"supplier_security", "physical_security", "prototype_protection"],
"PSIRT": ["coordinated_vulnerability_disclosure", "exploited_vuln_and_incident_reporting",
"public_security_advisories"],
# an EMS (ISO 14001) touches several environmental process areas — relevant ONLY to an env target:
"ISO14001": ["environmental_management_documentation", "energy_efficiency_documentation",
"supply_chain_environmental_due_diligence"],
"ISO9001": ["ce_conformity_assessment_and_technical_documentation"],
}
cmap = {k: CapabilityMappingEntry(capability_ids=v, confidence=Confidence.MEDIUM) for k, v in CERT_OBS.items()}
profile = build_company_profile(
CompanyContext(company_id="mc5", certifications=[Certification(certification_id=k) for k in CERT_OBS]), cmap)
def _delta(caps):
reqs = [TargetRequirement(capability_id=c) for c in caps]
a = assess_transition(TransitionContext(company_id="mc5", target=TransitionGoal(target_id="t")), reqs, profile)
return sorted({c.capability_id for c in a.coverage if c.status == CoverageStatus.MISSING})
def _rel(cert_caps, target_caps):
n = len(set(cert_caps) & set(target_caps))
return n, ("hoch" if n >= 3 else "niedrig" if n >= 1 else "keine")
w('# Customer Mission #5 — ein Nicht-Security-Ziel: kippt die Evidence-Relevanz?')
w("")
w('_Enger Scope: KEIN Umweltrecht, KEINE ISO-14001-Normmodellierung, KEIN neues Modul, KEIN Deploy. Nur die EINE Frage: ist `relevance(evidence, target)` wirklich eine Funktion des Ziels — oder ein Attribut der Evidence? Das Umwelt-/Materialnachweis-Ziel ist ein hand-authored `Required`-Satz (synthetisch), nur um die bestehende Engine auf ein Nicht-Security-Ziel zu richten. Synthetischer Kunde, keine echten Namen._')
w("")
w("## Der Kunde (synthetisch) — EIN Profil (hat u. a. ISO 14001)")
w("> **ISO 9001 · ISO 27001 · ISO 14001 · TISAX · PSIRT** · vernetzte Maschinen · Export EU")
w("")
# ── 1. Die Relevanz-Matrix (zwei Security-Ziele + ein Umwelt-Ziel) ─────────
w("## 1. Evidence-Relevanz über drei Ziele — zwei Security, eines NICHT")
w("| Zertifizierung (Evidence) | → CRA | → TISAX | → Umwelt-/Material |")
w("|---|---|---|---|")
for cert, caps in CERT_OBS.items():
cells = [_rel(caps, t)[1] + " (%d)" % _rel(caps, t)[0] for _, _, t in TARGETS]
mark = " ⟵ kippt" if cert == "ISO14001" else ""
w("| **%s** | %s | %s | %s |%s" % (cert, cells[0], cells[1], cells[2], mark))
w("")
# ── 2. Der Beweis: dieselbe Evidence, gegensätzlicher Wert je Ziel ─────────
iso14_env = _rel(CERT_OBS["ISO14001"], ENVIRONMENTAL)
iso27_env = _rel(CERT_OBS["ISO27001"], ENVIRONMENTAL)
psirt_env = _rel(CERT_OBS["PSIRT"], ENVIRONMENTAL)
w("## 2. Beweis — Relevanz ist eine Funktion des ZIELS (in BEIDE Richtungen)")
w("- **ISO 14001:** gegen CRA/TISAX **keine**, gegen das Umwelt-/Materialziel **%s (%d)**. Dieselbe Zertifizierung — von wertlos zu entscheidend, nur weil das Ziel wechselt." % (iso14_env[1], iso14_env[0]))
w("- **Symmetrisch:** **ISO 27001** (hoch gegen CRA/TISAX) ist gegen das Umwelt-/Materialziel **%s (%d)**; **PSIRT** ebenso **%s (%d)**. Security-Evidence ist hier wertlos." % (iso27_env[1], iso27_env[0], psirt_env[1], psirt_env[0]))
w("- Delta des Umwelt-/Materialziels: **%d** fehlende Fähigkeiten (das Profil deckt nur die ISO-14001-nahen ab) — über dieselbe `assess_transition`-Engine, kein Sonderpfad." % len(_delta(ENVIRONMENTAL)))
w("")
w('→ **Damit ist `relevance(evidence, target)` zweiseitig bewiesen:** keine Evidence ist „an sich" relevant; Relevanz entsteht erst gegen ein Ziel. Eine Capability/Zertifizierung ohne Ziel hat keinen Relevanzwert.')
w("")
# ── Befund ────────────────────────────────────────────────────────────────
w("## Befund")
w("")
w('> **Ein und dieselbe Evidence kann je Ziel wertlos oder hoch relevant sein** — hier erstmals an einem NICHT-Security-Ziel gezeigt, in beide Richtungen (ISO 14001 kippt von keine→hoch, Security-Certs von hoch→keine). Folgerung für das spätere Modell: Relevanz darf NICHT als Attribut der Evidence gespeichert werden, sondern nur als `relevance(evidence, target)` berechnet (computed-not-stored). **Damit ist die Ziel-Diversität für den späteren Selektor beisammen: Regulation · Certification · Contract/Tender · OEM-Spec · Umwelt-/Material-Ziel — fünf Zielarten durch dieselbe Engine. Erst jetzt wird ein Scope→Journey-Selektor sinnvoll** (er optimiert nicht mehr auf einer einzigen Zielart).')
w("")
print("\n".join(OUT))
@@ -0,0 +1,55 @@
"""Customer Mission #5 — a non-security target: evidence relevance flips both ways.
Pins the one claim this mission exists to prove: relevance(evidence, target) is a function of the
TARGET, not an attribute of the evidence. The same ISO 14001 is keine against CRA/TISAX but hoch
against an environmental/material target, while the security certs flip the other way (hoch against
security targets, keine against the environmental one). Tight scope: no corpus, no norm model, no
new runtime module, no real names.
"""
from __future__ import annotations
import os
import subprocess
import sys
def _run():
root = os.path.join(os.path.dirname(__file__), "..")
r = subprocess.run(
[sys.executable, "reference_scenarios/mission_non_security_target.py"],
cwd=root, env={**os.environ, "PYTHONPATH": "."}, capture_output=True, text=True,
)
assert r.returncode == 0, r.stderr
return r.stdout
def test_runs_end_to_end():
out = _run()
assert "Customer Mission #5" in out
def test_iso14001_relevance_flips_to_high_on_environmental_target():
out = _run()
# the headline: same cert, keine against security targets, hoch against the environmental one
assert "**ISO14001** | keine (0) | keine (0) | hoch (3) |" in out
def test_security_evidence_is_worthless_against_environmental_target():
out = _run()
# symmetry: security certs are relevant for security, keine for the environmental target
assert "**ISO27001** | hoch (5) | hoch (3) | keine (0) |" in out
assert "**PSIRT** | hoch (3) | keine (0) | keine (0) |" in out
def test_relevance_is_a_function_of_the_target():
out = _run()
assert "relevance(evidence, target)` zweiseitig bewiesen" in out
# five target types now covered -> selector becomes sensible
assert "fünf Zielarten" in out
def test_no_real_company_names():
out = _run().lower()
for name in ["eto", "owis", "winterhalter"]:
assert name not in out