fix(sre): fail closed typed Agent99 dispatch
This commit is contained in:
@@ -15,12 +15,17 @@ from src.services import agent99_controlled_dispatch_ledger as ledger_module
|
||||
from src.services import agent99_outcome_ingestion as ingestion_module
|
||||
from src.services.agent99_controlled_dispatch_ledger import (
|
||||
PostgresAgent99DispatchLedger,
|
||||
attach_agent99_dispatch_identity,
|
||||
build_agent99_dispatch_identity,
|
||||
build_agent99_dispatch_receipt_envelope,
|
||||
parse_agent99_dispatch_identity,
|
||||
record_agent99_learning_writeback,
|
||||
)
|
||||
from src.services.agent99_sre_bridge import bridge_alertmanager_to_agent99
|
||||
from src.services.agent99_sre_bridge import (
|
||||
_agent99_dispatch_scope_from_payload,
|
||||
bridge_alertmanager_to_agent99,
|
||||
build_agent99_sre_alert,
|
||||
)
|
||||
|
||||
|
||||
def _identity():
|
||||
@@ -491,6 +496,115 @@ async def test_verifier_requires_transport_and_all_evidence(monkeypatch) -> None
|
||||
assert passed["runtime_closure_verified"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backupcheck_read_only_scope_passes_real_ledger_verifier(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
identity = build_agent99_dispatch_identity(
|
||||
project_id="awoooi",
|
||||
incident_id="INC-20260711-BACKUP",
|
||||
source_fingerprint="backup-fingerprint",
|
||||
route_id="agent99:backup_health:BackupCheck",
|
||||
execution_generation="1",
|
||||
work_item_id=(
|
||||
"agent99-dispatch:awoooi:INC-20260711-BACKUP:BackupCheck"
|
||||
),
|
||||
)
|
||||
payload = build_agent99_sre_alert(
|
||||
alert_id="backup-readback",
|
||||
alertname="BackupCredentialEscrowEvidenceMissing",
|
||||
severity="warning",
|
||||
namespace="awoooi-prod",
|
||||
target_resource="backup_restore",
|
||||
message="freshness escrow restore evidence required",
|
||||
labels={"event_type": "backup_restore_escrow_signal"},
|
||||
fingerprint="backup-fingerprint",
|
||||
)
|
||||
payload = attach_agent99_dispatch_identity(payload, identity)
|
||||
scope = _agent99_dispatch_scope_from_payload(payload)
|
||||
assert scope["executor"] == "Agent99"
|
||||
assert scope["break_glass_executor"] == "backup_restore_break_glass"
|
||||
assert scope["controlled_apply_requested"] is False
|
||||
|
||||
promoted = build_agent99_dispatch_receipt_envelope(
|
||||
identity=identity,
|
||||
dispatch_receipt={
|
||||
"kind": "backup_health",
|
||||
"suggested_mode": "BackupCheck",
|
||||
"target_resource": "backup_restore",
|
||||
"controlled_apply_requested": False,
|
||||
"accepted": True,
|
||||
"inbox_triggered": True,
|
||||
"queue_accepted": True,
|
||||
"dispatch_identity_matched": True,
|
||||
"delivery_certainty": "delivered",
|
||||
"dispatch_scope": scope,
|
||||
},
|
||||
controlled_apply_authorized=False,
|
||||
)
|
||||
outcome = {
|
||||
"identity": identity.public_dict(),
|
||||
"controlledApply": False,
|
||||
"mode": "BackupCheck",
|
||||
"outcome": {
|
||||
"identity": identity.public_dict(),
|
||||
"schemaVersion": "agent99_outcome_contract_v1",
|
||||
"state": "resolved",
|
||||
"transportOk": True,
|
||||
"verifierName": "backup_restore_readback_verifier",
|
||||
"verifierPassed": True,
|
||||
"sourceEventResolved": True,
|
||||
"verifiedAt": "2026-07-11T20:00:00+08:00",
|
||||
},
|
||||
}
|
||||
|
||||
class BackupVerifierDB:
|
||||
def __init__(self) -> None:
|
||||
self.call = 0
|
||||
|
||||
async def execute(self, _statement):
|
||||
self.call += 1
|
||||
if self.call == 1:
|
||||
return _ScalarResult(
|
||||
row=SimpleNamespace(
|
||||
state="waiting_tool",
|
||||
error_detail=json.dumps(promoted),
|
||||
)
|
||||
)
|
||||
if self.call == 2:
|
||||
return _ScalarResult(identity.run_id)
|
||||
if self.call == 4:
|
||||
return _ScalarResult(identity.incident_id)
|
||||
if self.call in {5, 6}:
|
||||
return _ScalarResult(identity.run_id)
|
||||
return _ScalarResult()
|
||||
|
||||
monkeypatch.setattr(
|
||||
ledger_module,
|
||||
"get_db_context",
|
||||
lambda _project_id: _Context(BackupVerifierDB()),
|
||||
)
|
||||
|
||||
result = await PostgresAgent99DispatchLedger().record_verifier(
|
||||
identity=identity,
|
||||
outcome_receipt=outcome,
|
||||
evidence_refs={
|
||||
**_evidence_refs(),
|
||||
"backup_status_evidence_ref": "backup-status:1",
|
||||
"freshness_evidence_ref": "backup-freshness:1",
|
||||
"offsite_verify_evidence_ref": "offsite-verify:1",
|
||||
"escrow_evidence_ref": "escrow-readback:1",
|
||||
"restore_drill_evidence_ref": "restore-drill:1",
|
||||
"source_resolution_receipt_ref": "alert-resolved:1",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["status"] == "verifier_passed_learning_writeback_pending"
|
||||
assert result["post_verifier_passed"] is True
|
||||
assert result["controlled_apply_authorized"] is False
|
||||
assert result["runtime_closure_verified"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("mismatch", ["verifier", "mode", "controlled_apply"])
|
||||
async def test_verifier_rejects_dispatch_scope_mismatch(
|
||||
|
||||
Reference in New Issue
Block a user