diff --git a/apps/api/src/services/independent_verifier_registry.py b/apps/api/src/services/independent_verifier_registry.py index 9fd3ba166..a61d136b0 100644 --- a/apps/api/src/services/independent_verifier_registry.py +++ b/apps/api/src/services/independent_verifier_registry.py @@ -70,7 +70,7 @@ _DOMAIN_CONTRACTS: tuple[dict[str, Any], ...] = ( { "domain": "backup_restore", "mutating": False, - "executor": None, + "executor": "Agent99", "break_glass_executor": "backup_restore_break_glass", "verifier": "backup_restore_readback_verifier", "runtime_write_allowed": False, diff --git a/apps/api/tests/test_independent_verifier_registry.py b/apps/api/tests/test_independent_verifier_registry.py index 712b84344..f2efbdc5d 100644 --- a/apps/api/tests/test_independent_verifier_registry.py +++ b/apps/api/tests/test_independent_verifier_registry.py @@ -1,3 +1,6 @@ +from src.services.controlled_alert_target_router import ( + resolve_typed_alert_target, +) from src.services.independent_verifier_registry import ( build_independent_verifier_registry, ) @@ -49,7 +52,10 @@ def test_registry_covers_every_typed_domain_without_false_runtime_closure() -> N if row["mutating"] is True ) assert by_domain["backup_restore"]["runtime_write_allowed"] is False - assert by_domain["backup_restore"]["executor"] is None + assert by_domain["backup_restore"]["executor"] == "Agent99" + assert by_domain["backup_restore"]["break_glass_executor"] == ( + "backup_restore_break_glass" + ) assert by_domain["unknown"]["runtime_write_allowed"] is False assert by_domain["unknown"]["executor"] is None assert all( @@ -58,6 +64,26 @@ def test_registry_covers_every_typed_domain_without_false_runtime_closure() -> N ) +def test_backup_readback_route_matches_independent_verifier_contract() -> None: + route = resolve_typed_alert_target( + alertname="BackupFreshnessMissing", + target_resource="backup_restore", + namespace="", + ) + registry = build_independent_verifier_registry() + contract = next( + row + for row in registry["domain_contracts"] + if row["domain"] == route["target_kind"] + ) + + assert route["executor"] == contract["executor"] == "Agent99" + assert route["break_glass_executor"] == contract["break_glass_executor"] + assert route["verifier"] == contract["verifier"] + assert route["controlled_apply_allowed"] is False + assert contract["runtime_write_allowed"] is False + + def test_registry_fails_closed_when_one_catalog_loses_its_postcondition() -> None: catalog = [ { diff --git a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json index 5bcddd744..aca534b90 100644 --- a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json +++ b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json @@ -773,6 +773,7 @@ "apps/api/src/services/agent99_controlled_dispatch_ledger.py", "apps/api/src/services/db_bounded_executor.py", "apps/api/src/services/backup_restore_signal_automation.py", + "apps/api/src/services/controlled_alert_target_router.py", "apps/api/src/services/post_execution_verifier.py" ], "executor": "none", @@ -783,6 +784,7 @@ "machine-readable registry covers all eight typed domains and keeps executor identity separate from verifier identity for all six mutating domains", "all 17 Ansible catalogs have registered independent postconditions; every auto-apply catalog is check-mode capable and the sole exception is explicit break-glass", "K3s, Host/Container, Agent99, DB, Backup/restore and unknown-asset lanes expose domain-specific verifier contracts with cross-domain fallback disabled", + "Backup/restore read-only routing and the verifier registry now bind the same Agent99 evidence collector, backup_restore_readback_verifier and separate backup_restore_break_glass mutation executor; controlled apply and runtime writes remain disabled, with a cross-module contract test", "Host111 three-origin, GCP exact-series, gitea-native metrics and host99 Alertmanager relay readbacks are registered as runtime receipt requirements rather than source completion evidence" ], "runtime_gaps": [