From 629b9d9ca56a036cc9ed9b2b300df38fff98fabb Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Sun, 26 Apr 2026 09:32:56 +0200 Subject: [PATCH] feat(pipeline): store MCP fields (assertion, pass/fail criteria, check_type) in generation_metadata - Add assertion, pass_criteria, fail_criteria, check_type to AtomicControlCandidate dataclass - Parse MCP fields from LLM output in _process_pass0b_control - Store MCP fields in generation_metadata JSON for later use by MCP scanner - Fields default to empty when not present (backward-compatible with old prompts) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../services/decomposition_pass.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/control-pipeline/services/decomposition_pass.py b/control-pipeline/services/decomposition_pass.py index efd21e0..869b1d4 100644 --- a/control-pipeline/services/decomposition_pass.py +++ b/control-pipeline/services/decomposition_pass.py @@ -215,6 +215,11 @@ class AtomicControlCandidate: domain: str = "" source_regulation: str = "" source_article: str = "" + # MCP-taugliche Felder + assertion: str = "" + pass_criteria: list = field(default_factory=list) + fail_criteria: list = field(default_factory=list) + check_type: str = "" def to_dict(self) -> dict: return { @@ -229,6 +234,10 @@ class AtomicControlCandidate: "severity": self.severity, "category": self.category, "domain": self.domain, + "assertion": self.assertion, + "pass_criteria": self.pass_criteria, + "fail_criteria": self.fail_criteria, + "check_type": self.check_type, } @@ -2114,12 +2123,16 @@ Quellreferenz: {source_ref} Antworte als JSON: {{ "title": "Kurzer Titel (max 80 Zeichen, deutsch, HANDLUNG enthalten)", + "assertion": "Eine einzige pruefbare Aussage (wird als MCP-Suchanfrage verwendet)", "objective": "Was muss erreicht werden? (1-2 Sätze)", "requirements": ["Konkrete Anforderung 1", "Anforderung 2"], "test_procedure": ["Prüfschritt 1", "Prüfschritt 2"], "evidence": ["Nachweis/Dokument/Artefakt 1", "Nachweis 2"], + "pass_criteria": ["Wann gilt dieses Control als erfuellt?"], + "fail_criteria": ["Wann gilt dieses Control als nicht erfuellt?"], "severity": "critical|high|medium|low", "category": "security|privacy|governance|operations|finance|reporting", + "check_type": "technical_config_check|document_clause_check|code_pattern_check|evidence_check|interview_required", "merge_key": "action_type:normalized_object:control_phase" }}""" @@ -2206,12 +2219,16 @@ Antworte als JSON-Objekt. Fuer JEDE Pflicht ein Key (die Pflicht-ID): Jedes Control hat dieses Format: {{ "title": "Kurzer Titel (max 80 Zeichen, deutsch, HANDLUNG enthalten)", + "assertion": "Eine einzige pruefbare Aussage (wird als MCP-Suchanfrage verwendet)", "objective": "Was muss erreicht werden? (1-2 Sätze)", "requirements": ["Konkrete Anforderung 1", "Anforderung 2"], "test_procedure": ["Prüfschritt 1", "Prüfschritt 2"], "evidence": ["Nachweis/Dokument/Artefakt 1", "Nachweis 2"], + "pass_criteria": ["Wann gilt dieses Control als erfuellt?"], + "fail_criteria": ["Wann gilt dieses Control als nicht erfuellt?"], "severity": "critical|high|medium|low", "category": "security|privacy|governance|operations|finance|reporting", + "check_type": "technical_config_check|document_clause_check|code_pattern_check|evidence_check|interview_required", "merge_key": "action_type:normalized_object:control_phase" }}""" @@ -2950,6 +2967,10 @@ class DecompositionPass: parsed.get("severity", obl["parent_severity"]) ), category=parsed.get("category", obl["parent_category"]), + assertion=parsed.get("assertion", "")[:500], + pass_criteria=_ensure_list(parsed.get("pass_criteria", [])), + fail_criteria=_ensure_list(parsed.get("fail_criteria", [])), + check_type=parsed.get("check_type", ""), ) # Store merge_key from LLM output in metadata llm_merge_key = parsed.get("merge_key", "") @@ -3412,6 +3433,11 @@ class DecompositionPass: "framework_domain": getattr(atomic, "_framework_domain", None), "framework_subcontrol_id": getattr(atomic, "_framework_subcontrol_id", None), "decomposition_source": getattr(atomic, "_decomposition_source", "direct"), + # MCP-taugliche Felder + "assertion": atomic.assertion or "", + "pass_criteria": atomic.pass_criteria or [], + "fail_criteria": atomic.fail_criteria or [], + "check_type": atomic.check_type or "", }), "framework_id": "14b1bdd2-abc7-4a43-adae-14471ee5c7cf", },