fix(agent99): terminalize unscoped cold-start recovery
This commit is contained in:
@@ -34,6 +34,8 @@ AGENT99_CONTROL_PLANE_HEALTH_MODE = "SelfCheck"
|
||||
AGENT99_CONTROL_PLANE_HEALTH_ROUTE_ID = (
|
||||
"agent99:agent99_control_plane_health:SelfCheck"
|
||||
)
|
||||
AGENT99_COLD_START_INCIDENT_ID = "INC-20260711-11C751"
|
||||
AGENT99_COLD_START_RECOVER_ROUTE_ID = "agent99:host_recovery:Recover"
|
||||
AGENT99_DURABLE_ROUTE_KINDS = {
|
||||
AGENT99_CONTROL_PLANE_HEALTH_KIND,
|
||||
"provider_freshness_signal",
|
||||
@@ -265,6 +267,91 @@ def resolve_agent99_durable_route(
|
||||
}
|
||||
|
||||
|
||||
def _cold_start_no_write_terminal(
|
||||
*,
|
||||
alert_id: str,
|
||||
project_id: str,
|
||||
incident_id: str,
|
||||
approval_id: str,
|
||||
fingerprint: str,
|
||||
route_id: str,
|
||||
work_item_id: str,
|
||||
typed_target_route: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Return a traceable terminal before claim or transport can occur."""
|
||||
|
||||
receipt_digest = hashlib.sha256(
|
||||
(
|
||||
f"{project_id}:{incident_id}:{approval_id}:{fingerprint}:"
|
||||
f"{route_id}:exact-inventory-scope-missing"
|
||||
).encode()
|
||||
).hexdigest()
|
||||
terminal = {
|
||||
"schema_version": "agent99_cold_start_no_write_terminal_v1",
|
||||
"receipt_id": f"cold-start-no-write:{receipt_digest[:24]}",
|
||||
"status": "no_write_terminal",
|
||||
"reason": "exact_inventory_scope_missing",
|
||||
"project_id": project_id,
|
||||
"incident_id": incident_id,
|
||||
"approval_id": approval_id,
|
||||
"source_alert_id": alert_id,
|
||||
"source_fingerprint": fingerprint,
|
||||
"route_id": route_id,
|
||||
"work_item_id": work_item_id or None,
|
||||
"canonical_asset_id": typed_target_route.get("canonical_asset_id"),
|
||||
"source_namespace": typed_target_route.get("source_namespace"),
|
||||
"normalized_execution_namespace": typed_target_route.get(
|
||||
"normalized_execution_namespace"
|
||||
),
|
||||
"kubernetes_namespace_applicable": False,
|
||||
"allowed_inventory_hosts": list(
|
||||
typed_target_route.get("allowed_inventory_hosts") or []
|
||||
),
|
||||
"playbook": {
|
||||
"check": "Agent99 Status controlledApply=false",
|
||||
"apply": "blocked_exact_inventory_scope_missing",
|
||||
"rollback": "not_applicable_no_runtime_write",
|
||||
},
|
||||
"transport": {
|
||||
"status": "suppressed_before_single_flight_claim",
|
||||
"claim_performed": False,
|
||||
"transport_performed": False,
|
||||
},
|
||||
"verifier_contract": {
|
||||
"name": "cold_start_no_write_terminal_contract",
|
||||
"status": "ready_for_independent_source_validation",
|
||||
"scope": "source_no_write_only",
|
||||
"runtime_verifier_applicable": False,
|
||||
"assertions": [
|
||||
"normalized_execution_namespace_is_host_recovery",
|
||||
"kubernetes_namespace_is_not_execution_identity",
|
||||
"exact_inventory_scope_is_absent",
|
||||
"single_flight_claim_not_performed",
|
||||
"agent99_transport_not_performed",
|
||||
"runtime_write_not_performed",
|
||||
],
|
||||
},
|
||||
"runtime_write_performed": False,
|
||||
"agent99_dispatch_performed": False,
|
||||
"production_executor_invoked": False,
|
||||
"incident_resolution_allowed": False,
|
||||
"next_safe_action": (
|
||||
"bind_exact_inventory_scope_before_creating_a_new_recover_candidate"
|
||||
),
|
||||
}
|
||||
return {
|
||||
"schemaVersion": "agent99_cold_start_no_write_result_v1",
|
||||
"status": "no_write_terminal",
|
||||
"reason": terminal["reason"],
|
||||
"dispatchPerformed": False,
|
||||
"runtimeClosureVerified": False,
|
||||
"runtimeWritePerformed": False,
|
||||
"controlledApplyAuthorized": False,
|
||||
"typedTargetRoute": typed_target_route,
|
||||
"noWriteTerminal": terminal,
|
||||
}
|
||||
|
||||
|
||||
def _resolution_policy_for_kind(kind: str) -> str:
|
||||
if kind in {
|
||||
AGENT99_CONTROL_PLANE_HEALTH_KIND,
|
||||
@@ -1226,6 +1313,43 @@ async def bridge_alertmanager_to_agent99(
|
||||
str(route_id or "").strip()
|
||||
or f"agent99:{str(payload.get('kind') or 'monitoring_alert')}:{suggested_mode}"
|
||||
)
|
||||
if (
|
||||
incident_id == AGENT99_COLD_START_INCIDENT_ID
|
||||
and resolved_route_id == AGENT99_COLD_START_RECOVER_ROUTE_ID
|
||||
and mutating
|
||||
and routing.get("typedDispatchAllowed") is True
|
||||
and typed_target_route.get("resolution_status") == "resolved"
|
||||
and typed_target_route.get("target_kind") == "control_plane_recovery"
|
||||
and typed_target_route.get("canonical_asset_id")
|
||||
== "control-plane:cold-start-gate"
|
||||
and typed_target_route.get("executor") == "Agent99"
|
||||
and typed_target_route.get("verifier")
|
||||
== "cold_start_independent_scorecard_verifier"
|
||||
and typed_target_route.get("cross_domain_fallback_allowed") is False
|
||||
and typed_target_route.get("normalized_execution_namespace")
|
||||
== "host_recovery"
|
||||
and typed_target_route.get("source_namespace") == "default"
|
||||
and typed_target_route.get("kubernetes_namespace_applicable") is False
|
||||
and typed_target_route.get("controlled_apply_allowed") is False
|
||||
and typed_target_route.get("allowed_inventory_hosts") == []
|
||||
and typed_target_route.get("no_write_terminal_required") is True
|
||||
):
|
||||
logger.info(
|
||||
"agent99_cold_start_no_write_terminal",
|
||||
incident_id=incident_id,
|
||||
route_id=resolved_route_id,
|
||||
reason="exact_inventory_scope_missing",
|
||||
)
|
||||
return _cold_start_no_write_terminal(
|
||||
alert_id=alert_id,
|
||||
project_id=project_id,
|
||||
incident_id=incident_id,
|
||||
approval_id=approval_id,
|
||||
fingerprint=fingerprint,
|
||||
route_id=resolved_route_id,
|
||||
work_item_id=work_item_id,
|
||||
typed_target_route=typed_target_route,
|
||||
)
|
||||
if payload.get("kind") == AGENT99_CONTROL_PLANE_HEALTH_KIND:
|
||||
typed_target_route = (
|
||||
routing.get("typedTargetRoute")
|
||||
|
||||
@@ -404,9 +404,27 @@ def resolve_typed_alert_target(
|
||||
executor="Agent99",
|
||||
verifier="cold_start_independent_scorecard_verifier",
|
||||
risk_class="high",
|
||||
controlled_apply_allowed=False,
|
||||
execution_role="single_writer_control_plane_recovery_executor",
|
||||
)
|
||||
route["route_id"] = "agent99_recover_after_owner_review"
|
||||
route.update(
|
||||
{
|
||||
"source_namespace": legacy_recovery["source_namespace"],
|
||||
"normalized_execution_namespace": legacy_recovery[
|
||||
"normalized_execution_namespace"
|
||||
],
|
||||
"kubernetes_namespace_applicable": False,
|
||||
"inventory_scope_status": "exact_inventory_scope_unresolved",
|
||||
"no_write_terminal_required": True,
|
||||
"no_write_terminal_reason": "exact_inventory_scope_missing",
|
||||
"check_route": legacy_recovery["check_route"],
|
||||
"controlled_apply_route": legacy_recovery[
|
||||
"controlled_apply_route"
|
||||
],
|
||||
"rollback_route": legacy_recovery["rollback_route"],
|
||||
}
|
||||
)
|
||||
return route
|
||||
|
||||
if compact_alert in {
|
||||
@@ -962,10 +980,17 @@ def resolve_controlled_alert_target(
|
||||
"schema_version": "controlled_alert_target_route_v1",
|
||||
"target_kind": "control_plane_recovery",
|
||||
"target_resource": target_resource or "cold-start-gate",
|
||||
"canonical_asset_id": "control-plane:cold-start-gate",
|
||||
"source_namespace": namespace or None,
|
||||
"normalized_execution_namespace": "host_recovery",
|
||||
"kubernetes_namespace_applicable": False,
|
||||
"executor": "Agent99",
|
||||
"verifier": "cold_start_independent_scorecard_verifier",
|
||||
"allowed_inventory_hosts": [],
|
||||
"controlled_apply_allowed": False,
|
||||
"inventory_scope_status": "exact_inventory_scope_unresolved",
|
||||
"no_write_terminal_required": True,
|
||||
"no_write_terminal_reason": "exact_inventory_scope_missing",
|
||||
"route_id": "agent99_recover_after_owner_review",
|
||||
"check_route": "agent99-mode Status controlledApply=false",
|
||||
"controlled_apply_route": "agent99-mode Recover controlledApply=true",
|
||||
@@ -1020,10 +1045,16 @@ def build_controlled_recovery_promotion_contract(
|
||||
},
|
||||
{
|
||||
"field": "controlled_apply_route",
|
||||
"status": "ready",
|
||||
"status": "blocked",
|
||||
"source": "domain_target_normalizer",
|
||||
"value": route["controlled_apply_route"],
|
||||
},
|
||||
{
|
||||
"field": "no_write_terminal_receipt",
|
||||
"status": "pending",
|
||||
"source": "alert_operation_log",
|
||||
"value": "required_before_no_write_terminal_is_durable",
|
||||
},
|
||||
{
|
||||
"field": "dispatch_receipt",
|
||||
"status": "pending",
|
||||
@@ -1089,12 +1120,12 @@ def build_controlled_recovery_promotion_contract(
|
||||
"fields": fields,
|
||||
"runtime_execution_authorized": False,
|
||||
"runtime_write_allowed": False,
|
||||
"controlled_playbook_queue": True,
|
||||
"controlled_playbook_queue": False,
|
||||
"owner_review_required": False,
|
||||
"owner_review_gate": "auto_waived_for_low_medium_high",
|
||||
"needs_human": False,
|
||||
"completion_status": "partial",
|
||||
"completion_blocker": "dispatch_verifier_and_learning_receipts_missing",
|
||||
"completion_status": "no_write_terminal_pending_durable_receipt",
|
||||
"completion_blocker": "exact_inventory_scope_missing",
|
||||
"forbidden_operations": [
|
||||
"kubectl_for_host_control_plane_target",
|
||||
"generic_ansible_keyword_fallback",
|
||||
@@ -1107,12 +1138,16 @@ def build_controlled_recovery_promotion_contract(
|
||||
"controlled_automation_closure": {
|
||||
"schema_version": "controlled_recovery_closure_contract_v1",
|
||||
"trace_id_required": True,
|
||||
"status": "pending_source_check_dispatch_verify_writeback",
|
||||
"status": "no_write_terminal_pending_durable_receipt",
|
||||
"stages": [
|
||||
{"stage": "sensor", "status": "pending", "writes_runtime_state": False},
|
||||
{"stage": "normalize", "status": "passed", "writes_runtime_state": False},
|
||||
{"stage": "check", "status": "pending", "writes_runtime_state": False},
|
||||
{"stage": "controlled_apply", "status": "pending", "writes_runtime_state": False},
|
||||
{
|
||||
"stage": "controlled_apply",
|
||||
"status": "blocked_exact_inventory_scope_missing",
|
||||
"writes_runtime_state": False,
|
||||
},
|
||||
{"stage": "verify", "status": "pending", "writes_runtime_state": False},
|
||||
{"stage": "learn_writeback", "status": "pending", "writes_runtime_state": False},
|
||||
],
|
||||
@@ -1128,7 +1163,7 @@ def build_controlled_recovery_promotion_contract(
|
||||
"asset_type": "PlayBook",
|
||||
"asset_id": "agent99:Recover",
|
||||
"owner": "Agent99",
|
||||
"status": "dispatch_receipt_pending",
|
||||
"status": "blocked_exact_inventory_scope_missing",
|
||||
},
|
||||
{
|
||||
"asset_type": "Verifier",
|
||||
@@ -1162,19 +1197,34 @@ def build_controlled_recovery_handoff(
|
||||
)
|
||||
if contract is None:
|
||||
return None
|
||||
no_write_required = bool(
|
||||
contract.get("target_route", {}).get("no_write_terminal_required") is True
|
||||
)
|
||||
return {
|
||||
"schema_version": "ai_decision_controlled_executor_handoff_v1",
|
||||
"status": "agent99_dispatch_receipt_readback_pending",
|
||||
"status": (
|
||||
"cold_start_no_write_terminal_pending_receipt"
|
||||
if no_write_required
|
||||
else "agent99_dispatch_receipt_readback_pending"
|
||||
),
|
||||
"queued": False,
|
||||
"side_effect_performed": False,
|
||||
"single_writer_executor": "Agent99",
|
||||
"route_id": contract["route_id"],
|
||||
"active_blockers": [
|
||||
"agent99_dispatch_receipt_readback_pending",
|
||||
"post_apply_verifier_missing",
|
||||
"km_playbook_writeback_missing",
|
||||
],
|
||||
"safe_next_action": "read_agent99_dispatch_then_run_independent_cold_start_verifier",
|
||||
"active_blockers": (
|
||||
["exact_inventory_scope_missing"]
|
||||
if no_write_required
|
||||
else [
|
||||
"agent99_dispatch_receipt_readback_pending",
|
||||
"post_apply_verifier_missing",
|
||||
"km_playbook_writeback_missing",
|
||||
]
|
||||
),
|
||||
"safe_next_action": (
|
||||
"persist_no_write_terminal_without_claim_or_transport"
|
||||
if no_write_required
|
||||
else "read_agent99_dispatch_then_run_independent_cold_start_verifier"
|
||||
),
|
||||
"repair_candidate_promotion_contract": contract,
|
||||
"runtime_execution_authorized": False,
|
||||
"runtime_write_allowed": False,
|
||||
|
||||
Reference in New Issue
Block a user