Merge pull request 'fix(onboarding): review decisions — ISO13485 + patch rationale + summary counter' (#50) from feat/review-decisions into main
CI / detect-changes (push) Successful in 5s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Successful in 4s
CI / validate-canonical-controls (push) Successful in 3s
CI / loc-budget (push) Successful in 18s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 23s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped

This commit is contained in:
pilotadmin
2026-06-28 16:18:32 +02:00
5 changed files with 23 additions and 5 deletions
@@ -41,6 +41,7 @@ class SignalMapping(BaseModel):
evidence: Optional[str] = None # the artifact found (already in hand -> no upload needed)
product_fact: Optional[str] = None # e.g. "connected_to_internet"
fact_value: str = "true"
rationale: str = "" # curated note: WHY only indicative (esp. for partial mappings)
class DetectedCapability(BaseModel):
@@ -111,10 +112,12 @@ def silent_intake(
detected = [caps[k] for k in sorted(caps)]
product_facts = [facts[k] for k in sorted(facts)]
requirements_seen = sorted(requirements)
n_detected = sum(1 for d in detected if d.relationship == "detected") # concrete artifacts -> auto-detected
n_indication = len(detected) - n_detected # partial -> indication, still asked
summary = (
"Stille Vorbefüllung: %d Fähigkeit(en) automatisch erkannt, %d Produktfakt(en), %d Nachweis(e) "
"bereits vorhanden, %d Anforderung(en) erkannt (nicht als vorhanden gewertet)."
% (len(detected), len(product_facts), len(evidence), len(requirements_seen))
"Stille Vorbefüllung: %d Fähigkeit(en) automatisch erkannt, %d Indikation(en), %d Produktfakt(en), "
"%d Nachweis(e) bereits vorhanden, %d Anforderung(en) erkannt (nicht als vorhanden gewertet)."
% (n_detected, n_indication, len(product_facts), len(evidence), len(requirements_seen))
)
return SilentIntakeResult(
detected_capabilities=detected, product_facts=product_facts,
@@ -22,8 +22,11 @@ hypotheses:
- {id: HYP-document_control, capability: document_and_change_control, relationship: supports, kind: shared,
supported_by: [ISO9001, ISO13485, ISO27001, TISAX, ASPICE, IATF16949],
verification_required: true, question_intent: verify_existence, expected_evidence: [document_control_procedure]}
# NOTE: ISO13485 deliberately NOT here — its CAPA / quality-safety incident handling is not security
# incident management; that mapping was too broad (would seed false hypotheses). A dedicated
# manage_quality_and_safety_incidents capability can be added later if a target actually needs it.
- {id: HYP-incident_management, capability: incident_management, relationship: supports, kind: shared,
supported_by: [ISO27001, TISAX, IEC62443, ISO13485],
supported_by: [ISO27001, TISAX, IEC62443],
verification_required: true, question_intent: verify_existence, expected_evidence: [incident_procedure]}
- {id: HYP-supplier_security, capability: supplier_security, relationship: supports, kind: shared,
supported_by: [ISO27001, TISAX, IEC62443],
@@ -23,7 +23,8 @@ mappings:
# ── documents ─────────────────────────────────────────────────────────────────────────────
- {signal: ce_conformity_doc, capability: ce_conformity_assessment_and_technical_documentation, relationship: detected, evidence: technical_documentation}
- {signal: product_risk_assessment_doc, capability: product_cyber_risk_assessment, relationship: detected, evidence: product_risk_assessment}
- {signal: patch_policy_doc, capability: secure_signed_update_distribution, relationship: partial, evidence: patch_policy}
- {signal: patch_policy_doc, capability: secure_signed_update_distribution, relationship: partial, evidence: patch_policy,
rationale: "indicates update governance, does not evidence signed distribution"}
- {signal: incident_response_plan_doc, capability: incident_management, relationship: detected, evidence: incident_procedure}
# ── product facts (drive scope / target applicability) ──────────────────────────────────────
- {signal: cloud_connectivity, product_fact: connected_to_internet}
@@ -71,6 +71,15 @@ def test_resolve_adapts_to_advisor_input():
assert "document_and_change_control" in res["ISO9001"]
def test_iso13485_does_not_suggest_security_incident_management():
# ISO 13485 CAPA / quality-safety incident handling is NOT security incident management -> too broad,
# removed from the incident_management hypothesis (review decision 2026-06-28).
res = resolve_for_certifications(["ISO13485"], _LIB)
assert "incident_management" not in res.get("ISO13485", [])
inc = next(h for h in _LIB if h.capability == "incident_management")
assert "ISO13485" not in inc.supported_by
def test_advisor_consumes_the_library_end_to_end():
cra = yaml.safe_load(open(os.path.join(_DIR, "..", "knowledge", "transition_patterns",
"transition_pattern_iso27001_to_cra_maschinenvo_v1.yaml"), encoding="utf-8"))
@@ -85,6 +85,8 @@ def test_partial_signal_is_indicative_not_detected():
res = silent_intake([IntakeSignal(source="repository", signal="github_actions_ci")], _MAP)
assert "secure_development_lifecycle" not in res.capability_ids() # not counted as present
assert res.indicative_capability_ids() == ["secure_development_lifecycle"] # surfaced as an indication
# the summary counts detected and indications SEPARATELY (no over-claim of "automatisch erkannt")
assert "0 Fähigkeit(en) automatisch erkannt, 1 Indikation(en)" in res.summary
def test_partial_indication_does_not_remove_the_question():