fix(agent): bind pre-decision evidence to runtime
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m37s
CD Pipeline / build-and-deploy (push) Successful in 5m1s
CD Pipeline / post-deploy-checks (push) Successful in 3m35s

This commit is contained in:
ogt
2026-07-10 20:55:09 +08:00
parent 98557b01ed
commit b72b6ee048
11 changed files with 314 additions and 41 deletions

View File

@@ -139,6 +139,38 @@ async def test_audited_provider_strips_internal_audit_context(monkeypatch):
assert audit_calls[0]["incident_id"] == "INC-1"
@pytest.mark.asyncio
async def test_audited_provider_does_not_duplicate_first_class_gateway_audit(monkeypatch):
audit_calls = []
async def fake_record_mcp_call(**kwargs):
audit_calls.append(kwargs)
monkeypatch.setattr(
"src.services.mcp_audit_service.record_mcp_call",
fake_record_mcp_call,
)
provider = FakeProvider()
audited = AuditedMCPToolProvider(provider)
result = await audited.execute(
"kubectl_get",
{
"resource": "pods",
"_mcp_audit": {
"gateway_path": "awooop_mcp_gateway",
"agent_role": "pre_decision_investigator",
"incident_id": "INC-GW-1",
},
},
)
assert result.success is True
assert provider.calls == [("kubectl_get", {"resource": "pods"})]
assert audit_calls == []
@pytest.mark.asyncio
async def test_agent_tool_executor_blocks_disallowed_tool():
restart_tool = _tool("kubernetes", "kubectl_restart")

View File

@@ -57,8 +57,26 @@ async def test_backfill_enqueues_catalog_matched_incident(monkeypatch: pytest.Mo
yield fake_db
recorded: list[dict] = []
call_order: list[str] = []
async def fake_evidence_collector(**kwargs):
call_order.append("evidence")
assert kwargs["project_id"] == "awoooi"
assert kwargs["automation_run_id"]
return MagicMock(
snapshot_id="evidence-1",
incident_id="INC-20260627-NODE110",
sensors_succeeded=1,
recent_logs="sanitized logs",
)
async def fake_evidence_verifier(**kwargs):
call_order.append("verify")
assert kwargs["snapshot"].snapshot_id == "evidence-1"
return True
async def fake_recorder(**kwargs):
call_order.append("record")
recorded.append(kwargs)
return True
@@ -86,6 +104,8 @@ async def test_backfill_enqueues_catalog_matched_incident(monkeypatch: pytest.Mo
recorder=fake_recorder,
receipt_backfiller=fake_receipt_backfiller,
retry_replayer=fake_retry_replayer,
evidence_collector=fake_evidence_collector,
evidence_verifier=fake_evidence_verifier,
)
assert result["queued"] == 1
@@ -95,8 +115,12 @@ async def test_backfill_enqueues_catalog_matched_incident(monkeypatch: pytest.Mo
assert result["failed_apply_retry_check_mode_passed"] == 1
assert result["failed_apply_retry_check_mode_failed"] == 0
assert result["failed_apply_retry_stage_receipt_written"] == 1
assert result["pre_decision_evidence_ready"] == 1
assert result["pre_decision_evidence_blocked"] == 0
assert call_order == ["evidence", "verify", "record"]
assert recorded[0]["decision_path"] == "repair_candidate_controlled_queue"
assert recorded[0]["incident"]["incident_id"] == "INC-20260627-NODE110"
assert recorded[0]["automation_run_id"]
@pytest.mark.asyncio

View File

@@ -1368,6 +1368,7 @@ def test_ansible_decision_audit_payload_marks_repair_candidate_queue_claimable()
)
assert payload["input"]["executor_candidates"]
assert payload["dry_run_result"]["check_mode_executed"] is False
assert payload["input"]["project_id"] == "awoooi"
def test_ansible_decision_audit_payload_exposes_check_mode_safety_flags() -> None:

View File

@@ -21,6 +21,7 @@ from __future__ import annotations
import asyncio
from typing import Any
import pytest
from src.plugins.mcp.interfaces import MCPTool, MCPToolProvider, MCPToolResult
@@ -33,12 +34,11 @@ from src.services.mcp_tool_registry import (
)
from src.services.pre_decision_investigator import (
PreDecisionInvestigator,
_build_tool_params,
_compute_fingerprint,
_fill_snapshot_dimension,
_build_tool_params,
)
# ─────────────────────────────────────────────────────────────────────────────
# Stubs
# ─────────────────────────────────────────────────────────────────────────────
@@ -207,6 +207,12 @@ class TestComputeFingerprint:
i2 = _stub_incident("Host", "prod", "pod1", "critical")
assert _compute_fingerprint(i1) != _compute_fingerprint(i2)
def test_automation_run_id_prevents_cross_run_cache_reuse(self):
incident = _stub_incident("HostErrorLogFlood", "infra", "", "warning")
fp1 = _compute_fingerprint(incident, automation_run_id="run-1")
fp2 = _compute_fingerprint(incident, automation_run_id="run-2")
assert fp1 != fp2
def test_fingerprint_length_16(self):
i = _stub_incident()
fp = _compute_fingerprint(i)