fix(sre): reconcile typed backup playbooks

This commit is contained in:
Your Name
2026-07-19 01:14:21 +08:00
parent 207f00522a
commit 4944d40b10
2 changed files with 96 additions and 1 deletions

View File

@@ -356,6 +356,52 @@ def _learning_profile(
}
def _reconcile_existing_playbook_profile(
playbook: PlaybookRecord,
*,
profile: dict[str, Any],
mode: str,
) -> bool:
"""Idempotently remove legacy cold-start semantics from typed routes."""
if profile["affected_services"] != ["backup_restore"]:
return False
desired = {
"description": profile["description"],
"symptom_pattern": {
"alert_names": [],
"affected_services": profile["affected_services"],
"severity_range": ["P0", "P1", "P2"],
"label_patterns": {},
"keywords": profile["keywords"],
},
"repair_steps": [{
"step_number": 1,
"action_type": profile["action_type"],
"command": mode,
"expected_result": profile["expected_result"],
"rollback_command": profile["rollback_command"],
"requires_approval": False,
"risk_level": "LOW",
}],
"notes": profile["notes"],
"stateful_targets": [],
"requires_pre_backup": False,
}
changed = False
for field, value in desired.items():
if getattr(playbook, field, None) != value:
setattr(playbook, field, value)
changed = True
tags = list(playbook.tags or [])
for tag in profile["tags"]:
if tag not in tags:
tags.append(tag)
changed = True
playbook.tags = tags
return changed
async def _ensure_playbook_trust(
identity: Agent99DispatchIdentity,
*,
@@ -423,13 +469,21 @@ async def _ensure_playbook_trust(
else:
tags = list(playbook.tags or [])
incidents = list(playbook.source_incident_ids or [])
if run_tag not in tags:
profile_changed = _reconcile_existing_playbook_profile(
playbook,
profile=profile,
mode=mode,
)
tags = list(playbook.tags or [])
new_run = run_tag not in tags
if new_run:
tags.append(run_tag)
playbook.success_count = int(playbook.success_count or 0) + 1
playbook.trust_score = min(
1.0,
0.9 * float(playbook.trust_score or 0.3) + 0.1,
)
if new_run or profile_changed:
playbook.version = int(playbook.version or 0) + 1
if identity.incident_id not in incidents:
incidents.append(identity.incident_id)

View File

@@ -71,6 +71,47 @@ def test_backup_learning_profile_is_typed_and_no_write() -> None:
assert "verified recovery" not in profile["km_title"]
def test_existing_backup_playbook_profile_is_reconciled_idempotently() -> None:
identity = build_agent99_dispatch_identity(
project_id="awoooi",
incident_id="INC-20260719-BACKUP-PLAYBOOK",
source_fingerprint="backup-playbook-fingerprint",
route_id="agent99:backup_health:BackupCheck",
)
profile = job._learning_profile(identity, mode="BackupCheck")
playbook = SimpleNamespace(
description="legacy recovery",
symptom_pattern={"affected_services": ["cold-start-gate"]},
repair_steps=[{"action_type": "agent99_mode", "command": "Recover"}],
notes="legacy",
stateful_targets=["cold-start-gate"],
requires_pre_backup=True,
tags=["agent99", "controlled_dispatch"],
)
changed = job._reconcile_existing_playbook_profile(
playbook,
profile=profile,
mode="BackupCheck",
)
replay_changed = job._reconcile_existing_playbook_profile(
playbook,
profile=profile,
mode="BackupCheck",
)
assert changed is True
assert replay_changed is False
assert playbook.symptom_pattern["affected_services"] == ["backup_restore"]
assert playbook.repair_steps[0]["action_type"] == "agent99_readonly_check"
assert playbook.repair_steps[0]["command"] == "BackupCheck"
assert playbook.repair_steps[0]["rollback_command"] == (
"not_applicable_no_write"
)
assert "backup_restore" in playbook.tags
assert "cold-start" not in str(playbook.__dict__)
class _Context:
def __init__(self, db) -> None: # type: ignore[no-untyped-def]
self.db = db