diff --git a/fixtures/incident_response_page_triage_degraded_v1/README.md b/fixtures/incident_response_page_triage_degraded_v1/README.md new file mode 100644 index 0000000..447cec6 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/README.md @@ -0,0 +1,3 @@ +# incident_response_page_triage_degraded_v1 + +Deterministic fixture for incident_response_page_triage (severe). diff --git a/fixtures/incident_response_page_triage_degraded_v1/expected/admissibility.json b/fixtures/incident_response_page_triage_degraded_v1/expected/admissibility.json new file mode 100644 index 0000000..aaa70e1 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/expected/admissibility.json @@ -0,0 +1,18 @@ +{ + "fixture_id": "incident_response_page_triage_degraded_v1", + "fixture_version": "1.0.0", + "expected_admissible": false, + "must_hold_contracts": [], + "must_fail_contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "expected_failure_labels": [ + "CAUSAL_DEPENDENCY_LOSS", + "INVARIANT_VIOLATION", + "POLICY_ORDER_BROKEN", + "RECOVERY_PATH_INVALID" + ] +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/expected/failures.json b/fixtures/incident_response_page_triage_degraded_v1/expected/failures.json new file mode 100644 index 0000000..0066e0f --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/expected/failures.json @@ -0,0 +1,9 @@ +{ + "expected_failures": [ + "CAUSAL_DEPENDENCY_LOSS", + "INVARIANT_VIOLATION", + "POLICY_ORDER_BROKEN", + "RECOVERY_PATH_INVALID" + ], + "disallowed_failures": [] +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/contracts/alert_ack_before_mitigation.json b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/alert_ack_before_mitigation.json new file mode 100644 index 0000000..7676461 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/alert_ack_before_mitigation.json @@ -0,0 +1,13 @@ +{ + "contract_id": "alert_ack_before_mitigation", + "layer": "operational", + "type": "ordering", + "definition": { + "required_sequence": [ + "alert_received", + "alert_acknowledged", + "mitigation_started" + ] + }, + "severity": "CRITICAL" +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/contracts/no_orphan_mitigation_steps.json b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/no_orphan_mitigation_steps.json new file mode 100644 index 0000000..80d225f --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/no_orphan_mitigation_steps.json @@ -0,0 +1,9 @@ +{ + "contract_id": "no_orphan_mitigation_steps", + "layer": "relational", + "type": "invariant", + "definition": { + "rule": "no_orphan_dependencies" + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/contracts/rollback_reachable.json b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/rollback_reachable.json new file mode 100644 index 0000000..5f39048 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/rollback_reachable.json @@ -0,0 +1,13 @@ +{ + "contract_id": "rollback_reachable", + "layer": "relational", + "type": "reachability", + "definition": { + "from": "mitigation_started", + "to": [ + "rollback_available" + ], + "min_paths": 1 + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/contracts/root_cause_links_incident.json b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/root_cause_links_incident.json new file mode 100644 index 0000000..4b4e2a9 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/contracts/root_cause_links_incident.json @@ -0,0 +1,14 @@ +{ + "contract_id": "root_cause_links_incident", + "layer": "relational", + "type": "causality", + "definition": { + "required_causal_edges": [ + [ + "root_cause_identified", + "mitigation_started" + ] + ] + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/dependency_graph.json b/fixtures/incident_response_page_triage_degraded_v1/original/dependency_graph.json new file mode 100644 index 0000000..1336725 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/dependency_graph.json @@ -0,0 +1,92 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "rollback_available", + "relation": "RECOVERY", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/state.json b/fixtures/incident_response_page_triage_degraded_v1/original/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/original/trace.json b/fixtures/incident_response_page_triage_degraded_v1/original/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/original/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/reconstructed/dependency_graph.json b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/dependency_graph.json new file mode 100644 index 0000000..879db0c --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/dependency_graph.json @@ -0,0 +1,74 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/reconstructed/state.json b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_degraded_v1/reconstructed/trace.json b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/trace.json new file mode 100644 index 0000000..b3e79f4 --- /dev/null +++ b/fixtures/incident_response_page_triage_degraded_v1/reconstructed/trace.json @@ -0,0 +1,28 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "mitigation_started", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "alert_acknowledged", + "step": 4 + }, + { + "action": "rollback_available", + "step": 5 + }, + { + "action": "escalate_to_human", + "step": 6 + } + ] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/README.md b/fixtures/incident_response_page_triage_mild_v1/README.md new file mode 100644 index 0000000..7d1523b --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/README.md @@ -0,0 +1,3 @@ +# incident_response_page_triage_mild_v1 + +Deterministic fixture for incident_response_page_triage (mild). diff --git a/fixtures/incident_response_page_triage_mild_v1/expected/admissibility.json b/fixtures/incident_response_page_triage_mild_v1/expected/admissibility.json new file mode 100644 index 0000000..7f1b5ad --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/expected/admissibility.json @@ -0,0 +1,12 @@ +{ + "fixture_id": "incident_response_page_triage_mild_v1", + "fixture_version": "1.0.0", + "expected_admissible": false, + "must_hold_contracts": [], + "must_fail_contracts": [ + "rollback_reachable" + ], + "expected_failure_labels": [ + "RECOVERY_PATH_INVALID" + ] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/expected/failures.json b/fixtures/incident_response_page_triage_mild_v1/expected/failures.json new file mode 100644 index 0000000..8eb0e2c --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/expected/failures.json @@ -0,0 +1,6 @@ +{ + "expected_failures": [ + "RECOVERY_PATH_INVALID" + ], + "disallowed_failures": [] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/contracts/alert_ack_before_mitigation.json b/fixtures/incident_response_page_triage_mild_v1/original/contracts/alert_ack_before_mitigation.json new file mode 100644 index 0000000..7676461 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/contracts/alert_ack_before_mitigation.json @@ -0,0 +1,13 @@ +{ + "contract_id": "alert_ack_before_mitigation", + "layer": "operational", + "type": "ordering", + "definition": { + "required_sequence": [ + "alert_received", + "alert_acknowledged", + "mitigation_started" + ] + }, + "severity": "CRITICAL" +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/contracts/no_orphan_mitigation_steps.json b/fixtures/incident_response_page_triage_mild_v1/original/contracts/no_orphan_mitigation_steps.json new file mode 100644 index 0000000..80d225f --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/contracts/no_orphan_mitigation_steps.json @@ -0,0 +1,9 @@ +{ + "contract_id": "no_orphan_mitigation_steps", + "layer": "relational", + "type": "invariant", + "definition": { + "rule": "no_orphan_dependencies" + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/contracts/rollback_reachable.json b/fixtures/incident_response_page_triage_mild_v1/original/contracts/rollback_reachable.json new file mode 100644 index 0000000..5f39048 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/contracts/rollback_reachable.json @@ -0,0 +1,13 @@ +{ + "contract_id": "rollback_reachable", + "layer": "relational", + "type": "reachability", + "definition": { + "from": "mitigation_started", + "to": [ + "rollback_available" + ], + "min_paths": 1 + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/contracts/root_cause_links_incident.json b/fixtures/incident_response_page_triage_mild_v1/original/contracts/root_cause_links_incident.json new file mode 100644 index 0000000..4b4e2a9 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/contracts/root_cause_links_incident.json @@ -0,0 +1,14 @@ +{ + "contract_id": "root_cause_links_incident", + "layer": "relational", + "type": "causality", + "definition": { + "required_causal_edges": [ + [ + "root_cause_identified", + "mitigation_started" + ] + ] + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/dependency_graph.json b/fixtures/incident_response_page_triage_mild_v1/original/dependency_graph.json new file mode 100644 index 0000000..1336725 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/dependency_graph.json @@ -0,0 +1,92 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "rollback_available", + "relation": "RECOVERY", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/state.json b/fixtures/incident_response_page_triage_mild_v1/original/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_mild_v1/original/trace.json b/fixtures/incident_response_page_triage_mild_v1/original/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/original/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/reconstructed/dependency_graph.json b/fixtures/incident_response_page_triage_mild_v1/reconstructed/dependency_graph.json new file mode 100644 index 0000000..e99bc8d --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/reconstructed/dependency_graph.json @@ -0,0 +1,86 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_mild_v1/reconstructed/state.json b/fixtures/incident_response_page_triage_mild_v1/reconstructed/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/reconstructed/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_mild_v1/reconstructed/trace.json b/fixtures/incident_response_page_triage_mild_v1/reconstructed/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_mild_v1/reconstructed/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/README.md b/fixtures/incident_response_page_triage_moderate_v1/README.md new file mode 100644 index 0000000..eebd8e1 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/README.md @@ -0,0 +1,3 @@ +# incident_response_page_triage_moderate_v1 + +Deterministic fixture for incident_response_page_triage (moderate). diff --git a/fixtures/incident_response_page_triage_moderate_v1/expected/admissibility.json b/fixtures/incident_response_page_triage_moderate_v1/expected/admissibility.json new file mode 100644 index 0000000..72947b3 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/expected/admissibility.json @@ -0,0 +1,12 @@ +{ + "fixture_id": "incident_response_page_triage_moderate_v1", + "fixture_version": "1.0.0", + "expected_admissible": false, + "must_hold_contracts": [], + "must_fail_contracts": [ + "rollback_reachable" + ], + "expected_failure_labels": [ + "RECOVERY_PATH_INVALID" + ] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/expected/failures.json b/fixtures/incident_response_page_triage_moderate_v1/expected/failures.json new file mode 100644 index 0000000..8eb0e2c --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/expected/failures.json @@ -0,0 +1,6 @@ +{ + "expected_failures": [ + "RECOVERY_PATH_INVALID" + ], + "disallowed_failures": [] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/contracts/alert_ack_before_mitigation.json b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/alert_ack_before_mitigation.json new file mode 100644 index 0000000..7676461 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/alert_ack_before_mitigation.json @@ -0,0 +1,13 @@ +{ + "contract_id": "alert_ack_before_mitigation", + "layer": "operational", + "type": "ordering", + "definition": { + "required_sequence": [ + "alert_received", + "alert_acknowledged", + "mitigation_started" + ] + }, + "severity": "CRITICAL" +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/contracts/no_orphan_mitigation_steps.json b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/no_orphan_mitigation_steps.json new file mode 100644 index 0000000..80d225f --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/no_orphan_mitigation_steps.json @@ -0,0 +1,9 @@ +{ + "contract_id": "no_orphan_mitigation_steps", + "layer": "relational", + "type": "invariant", + "definition": { + "rule": "no_orphan_dependencies" + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/contracts/rollback_reachable.json b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/rollback_reachable.json new file mode 100644 index 0000000..5f39048 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/rollback_reachable.json @@ -0,0 +1,13 @@ +{ + "contract_id": "rollback_reachable", + "layer": "relational", + "type": "reachability", + "definition": { + "from": "mitigation_started", + "to": [ + "rollback_available" + ], + "min_paths": 1 + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/contracts/root_cause_links_incident.json b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/root_cause_links_incident.json new file mode 100644 index 0000000..4b4e2a9 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/contracts/root_cause_links_incident.json @@ -0,0 +1,14 @@ +{ + "contract_id": "root_cause_links_incident", + "layer": "relational", + "type": "causality", + "definition": { + "required_causal_edges": [ + [ + "root_cause_identified", + "mitigation_started" + ] + ] + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/dependency_graph.json b/fixtures/incident_response_page_triage_moderate_v1/original/dependency_graph.json new file mode 100644 index 0000000..1336725 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/dependency_graph.json @@ -0,0 +1,92 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "rollback_available", + "relation": "RECOVERY", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/state.json b/fixtures/incident_response_page_triage_moderate_v1/original/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/original/trace.json b/fixtures/incident_response_page_triage_moderate_v1/original/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/original/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/reconstructed/dependency_graph.json b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/dependency_graph.json new file mode 100644 index 0000000..e99bc8d --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/dependency_graph.json @@ -0,0 +1,86 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/reconstructed/state.json b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_moderate_v1/reconstructed/trace.json b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_moderate_v1/reconstructed/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_v1/README.md b/fixtures/incident_response_page_triage_v1/README.md new file mode 100644 index 0000000..a094e16 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/README.md @@ -0,0 +1,3 @@ +# incident_response_page_triage_v1 + +Deterministic fixture for incident_response_page_triage (baseline). diff --git a/fixtures/incident_response_page_triage_v1/expected/admissibility.json b/fixtures/incident_response_page_triage_v1/expected/admissibility.json new file mode 100644 index 0000000..4320f17 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/expected/admissibility.json @@ -0,0 +1,13 @@ +{ + "fixture_id": "incident_response_page_triage_v1", + "fixture_version": "1.0.0", + "expected_admissible": true, + "must_hold_contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "must_fail_contracts": [], + "expected_failure_labels": [] +} diff --git a/fixtures/incident_response_page_triage_v1/expected/failures.json b/fixtures/incident_response_page_triage_v1/expected/failures.json new file mode 100644 index 0000000..831eee2 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/expected/failures.json @@ -0,0 +1,4 @@ +{ + "expected_failures": [], + "disallowed_failures": [] +} diff --git a/fixtures/incident_response_page_triage_v1/original/contracts/alert_ack_before_mitigation.json b/fixtures/incident_response_page_triage_v1/original/contracts/alert_ack_before_mitigation.json new file mode 100644 index 0000000..7676461 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/contracts/alert_ack_before_mitigation.json @@ -0,0 +1,13 @@ +{ + "contract_id": "alert_ack_before_mitigation", + "layer": "operational", + "type": "ordering", + "definition": { + "required_sequence": [ + "alert_received", + "alert_acknowledged", + "mitigation_started" + ] + }, + "severity": "CRITICAL" +} diff --git a/fixtures/incident_response_page_triage_v1/original/contracts/no_orphan_mitigation_steps.json b/fixtures/incident_response_page_triage_v1/original/contracts/no_orphan_mitigation_steps.json new file mode 100644 index 0000000..80d225f --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/contracts/no_orphan_mitigation_steps.json @@ -0,0 +1,9 @@ +{ + "contract_id": "no_orphan_mitigation_steps", + "layer": "relational", + "type": "invariant", + "definition": { + "rule": "no_orphan_dependencies" + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_v1/original/contracts/rollback_reachable.json b/fixtures/incident_response_page_triage_v1/original/contracts/rollback_reachable.json new file mode 100644 index 0000000..5f39048 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/contracts/rollback_reachable.json @@ -0,0 +1,13 @@ +{ + "contract_id": "rollback_reachable", + "layer": "relational", + "type": "reachability", + "definition": { + "from": "mitigation_started", + "to": [ + "rollback_available" + ], + "min_paths": 1 + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_v1/original/contracts/root_cause_links_incident.json b/fixtures/incident_response_page_triage_v1/original/contracts/root_cause_links_incident.json new file mode 100644 index 0000000..4b4e2a9 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/contracts/root_cause_links_incident.json @@ -0,0 +1,14 @@ +{ + "contract_id": "root_cause_links_incident", + "layer": "relational", + "type": "causality", + "definition": { + "required_causal_edges": [ + [ + "root_cause_identified", + "mitigation_started" + ] + ] + }, + "severity": "HIGH" +} diff --git a/fixtures/incident_response_page_triage_v1/original/dependency_graph.json b/fixtures/incident_response_page_triage_v1/original/dependency_graph.json new file mode 100644 index 0000000..1336725 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/dependency_graph.json @@ -0,0 +1,92 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "rollback_available", + "relation": "RECOVERY", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_v1/original/state.json b/fixtures/incident_response_page_triage_v1/original/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_v1/original/trace.json b/fixtures/incident_response_page_triage_v1/original/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/original/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/incident_response_page_triage_v1/reconstructed/dependency_graph.json b/fixtures/incident_response_page_triage_v1/reconstructed/dependency_graph.json new file mode 100644 index 0000000..1336725 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/reconstructed/dependency_graph.json @@ -0,0 +1,92 @@ +{ + "graph_version": "1.0", + "nodes": [ + { + "node_id": "alert_received", + "label": "Alert received", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "alert_acknowledged", + "label": "Alert acknowledged", + "metadata": { + "phase": "intake" + } + }, + { + "node_id": "incident_classified", + "label": "Incident classified", + "metadata": { + "phase": "triage" + } + }, + { + "node_id": "root_cause_identified", + "label": "Root cause identified", + "metadata": { + "phase": "analysis" + } + }, + { + "node_id": "mitigation_started", + "label": "Mitigation started", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "rollback_available", + "label": "Rollback available", + "metadata": { + "phase": "response" + } + }, + { + "node_id": "escalate_to_human", + "label": "Escalate to human", + "metadata": { + "phase": "governance" + } + } + ], + "edges": [ + { + "source": "alert_received", + "target": "alert_acknowledged", + "relation": "PREREQUISITE", + "metadata": {} + }, + { + "source": "alert_acknowledged", + "target": "incident_classified", + "relation": "TEMPORAL", + "metadata": {} + }, + { + "source": "incident_classified", + "target": "root_cause_identified", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "root_cause_identified", + "target": "mitigation_started", + "relation": "CAUSAL", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "rollback_available", + "relation": "RECOVERY", + "metadata": {} + }, + { + "source": "mitigation_started", + "target": "escalate_to_human", + "relation": "RECOVERY", + "metadata": {} + } + ] +} diff --git a/fixtures/incident_response_page_triage_v1/reconstructed/state.json b/fixtures/incident_response_page_triage_v1/reconstructed/state.json new file mode 100644 index 0000000..323a416 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/reconstructed/state.json @@ -0,0 +1,23 @@ +{ + "evidence": { + "incident_id": "INC-2026-fixture", + "priority": "P1", + "service": "api-gateway" + }, + "constraints": { + "requires_alert_ack": true, + "requires_human_escalation_path": true + }, + "recovery_paths": { + "mitigation_started": [ + "rollback_available", + "escalate_to_human" + ] + }, + "dependencies": { + "mitigation_started": [ + "alert_acknowledged", + "root_cause_identified" + ] + } +} diff --git a/fixtures/incident_response_page_triage_v1/reconstructed/trace.json b/fixtures/incident_response_page_triage_v1/reconstructed/trace.json new file mode 100644 index 0000000..44a3b60 --- /dev/null +++ b/fixtures/incident_response_page_triage_v1/reconstructed/trace.json @@ -0,0 +1,32 @@ +{ + "events": [ + { + "action": "alert_received", + "step": 1 + }, + { + "action": "alert_acknowledged", + "step": 2 + }, + { + "action": "incident_classified", + "step": 3 + }, + { + "action": "root_cause_identified", + "step": 4 + }, + { + "action": "mitigation_started", + "step": 5 + }, + { + "action": "rollback_available", + "step": 6 + }, + { + "action": "escalate_to_human", + "step": 7 + } + ] +} diff --git a/fixtures/manifest.json b/fixtures/manifest.json index cbc9315..668d071 100644 --- a/fixtures/manifest.json +++ b/fixtures/manifest.json @@ -74,6 +74,79 @@ "POLICY_ORDER_BROKEN", "RECOVERY_PATH_INVALID" ] + }, + { + "fixture_id": "incident_response_page_triage_v1", + "fixture_version": "1.0.0", + "category": "incident_response", + "family": "incident_response_page_triage", + "degradation_level": "baseline", + "path": "fixtures/incident_response_page_triage_v1", + "expected_admissible": true, + "contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "expected_failure_labels": [] + }, + { + "fixture_id": "incident_response_page_triage_mild_v1", + "fixture_version": "1.0.0", + "category": "incident_response", + "family": "incident_response_page_triage", + "degradation_level": "mild", + "path": "fixtures/incident_response_page_triage_mild_v1", + "expected_admissible": false, + "contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "expected_failure_labels": [ + "RECOVERY_PATH_INVALID" + ] + }, + { + "fixture_id": "incident_response_page_triage_moderate_v1", + "fixture_version": "1.0.0", + "category": "incident_response", + "family": "incident_response_page_triage", + "degradation_level": "moderate", + "path": "fixtures/incident_response_page_triage_moderate_v1", + "expected_admissible": false, + "contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "expected_failure_labels": [ + "RECOVERY_PATH_INVALID" + ] + }, + { + "fixture_id": "incident_response_page_triage_degraded_v1", + "fixture_version": "1.0.0", + "category": "incident_response", + "family": "incident_response_page_triage", + "degradation_level": "severe", + "path": "fixtures/incident_response_page_triage_degraded_v1", + "expected_admissible": false, + "contracts": [ + "alert_ack_before_mitigation", + "no_orphan_mitigation_steps", + "rollback_reachable", + "root_cause_links_incident" + ], + "expected_failure_labels": [ + "CAUSAL_DEPENDENCY_LOSS", + "INVARIANT_VIOLATION", + "POLICY_ORDER_BROKEN", + "RECOVERY_PATH_INVALID" + ] } ] } diff --git a/tests/test_fixture_manifest.py b/tests/test_fixture_manifest.py index eae4e81..7a38696 100644 --- a/tests/test_fixture_manifest.py +++ b/tests/test_fixture_manifest.py @@ -11,6 +11,10 @@ "coding_workflow_pr_review_mild_v1", "coding_workflow_pr_review_moderate_v1", "coding_workflow_pr_review_degraded_v1", + "incident_response_page_triage_v1", + "incident_response_page_triage_mild_v1", + "incident_response_page_triage_moderate_v1", + "incident_response_page_triage_degraded_v1", ] diff --git a/tests/test_incident_response_fixture_contract_bundle.py b/tests/test_incident_response_fixture_contract_bundle.py new file mode 100644 index 0000000..cfcd8be --- /dev/null +++ b/tests/test_incident_response_fixture_contract_bundle.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import json +from pathlib import Path + +from src.validation.contract_validator import ContractValidator + + +BASELINE_ROOT = Path("fixtures/incident_response_page_triage_v1") +SEVERE_ROOT = Path("fixtures/incident_response_page_triage_degraded_v1") + + +def _load_json(path: Path) -> dict[str, object]: + return json.loads(path.read_text(encoding="utf-8")) + + +def _payload(root: Path) -> tuple[dict[str, object], dict[str, object], list[dict[str, object]]]: + original = { + **_load_json(root / "original/trace.json"), + **_load_json(root / "original/state.json"), + "dependency_graph": _load_json(root / "original/dependency_graph.json"), + } + reconstructed = { + **_load_json(root / "reconstructed/trace.json"), + **_load_json(root / "reconstructed/state.json"), + "dependency_graph": _load_json(root / "reconstructed/dependency_graph.json"), + } + contracts = [_load_json(path) for path in sorted((root / "original/contracts").glob("*.json"))] + return original, reconstructed, contracts + + +def test_incident_response_baseline_contracts_pass_deterministically() -> None: + original, reconstructed, contracts = _payload(BASELINE_ROOT) + expected = _load_json(BASELINE_ROOT / "expected/admissibility.json") + + results = ContractValidator().validate_contracts(original=original, reconstructed=reconstructed, contracts=contracts) + + assert expected["expected_admissible"] is True + assert all(result.passed for result in results) + assert sorted(result.contract_id for result in results) == sorted(expected["must_hold_contracts"]) + + +def test_incident_response_severe_emits_only_expected_failure_labels() -> None: + original, reconstructed, contracts = _payload(SEVERE_ROOT) + expected_admissibility = _load_json(SEVERE_ROOT / "expected/admissibility.json") + expected_failures = _load_json(SEVERE_ROOT / "expected/failures.json") + + results = ContractValidator().validate_contracts(original=original, reconstructed=reconstructed, contracts=contracts) + + assert expected_admissibility["expected_admissible"] is False + assert all(not result.passed for result in results) + + observed_contracts = sorted(result.contract_id for result in results) + assert observed_contracts == sorted(expected_admissibility["must_fail_contracts"]) + + observed_labels = sorted({result.failure_label for result in results if result.failure_label is not None}) + assert observed_labels == sorted(expected_failures["expected_failures"])