fix(awooop): send controlled apply telegram receipts
This commit is contained in:
@@ -21,6 +21,7 @@ from src.services.awooop_ansible_check_mode_service import (
|
||||
_post_apply_km_path_type,
|
||||
_post_apply_verification_result,
|
||||
_record_auto_repair_execution_receipt,
|
||||
_send_controlled_apply_telegram_receipt,
|
||||
build_ansible_apply_command,
|
||||
build_ansible_check_mode_claim_input,
|
||||
build_ansible_check_mode_command,
|
||||
@@ -28,6 +29,7 @@ from src.services.awooop_ansible_check_mode_service import (
|
||||
claim_pending_check_modes,
|
||||
detect_ansible_transport_blockers,
|
||||
recent_ansible_transport_blockers,
|
||||
run_controlled_apply_for_claim,
|
||||
)
|
||||
from src.services.awooop_truth_chain_service import (
|
||||
_ansible_playbook_roots,
|
||||
@@ -1175,6 +1177,48 @@ def test_ansible_truth_surfaces_audited_check_mode_record() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_ansible_truth_marks_worker_apply_as_controlled_apply() -> None:
|
||||
truth = build_ansible_truth(
|
||||
[
|
||||
{
|
||||
"op_id": "apply-op-1",
|
||||
"operation_type": "ansible_apply_executed",
|
||||
"status": "success",
|
||||
"actor": "ansible_controlled_apply_worker",
|
||||
"input_catalog_id": "ansible:188-momo-backup-user",
|
||||
"input_execution_mode": "controlled_apply",
|
||||
"input_playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
|
||||
"input_apply_enabled": "true",
|
||||
"output_apply_executed": "true",
|
||||
"output_returncode": "0",
|
||||
"tags": ["ansible", "controlled_apply", "low", "ai_agent_auto_execution"],
|
||||
},
|
||||
{
|
||||
"op_id": "check-op-1",
|
||||
"operation_type": "ansible_check_mode_executed",
|
||||
"status": "success",
|
||||
"actor": "ansible_check_mode_worker",
|
||||
"input_catalog_id": "ansible:188-momo-backup-user",
|
||||
"input_execution_mode": "check_mode",
|
||||
"input_playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
|
||||
"dry_run_result": {"check_mode_executed": True, "apply_executed": False},
|
||||
"tags": ["ansible", "check_mode", "controlled_apply_allowed"],
|
||||
},
|
||||
],
|
||||
incident={"incident_id": "INC-MOMO", "alertname": "MomoPostgresBackupFailed"},
|
||||
drift=None,
|
||||
)
|
||||
|
||||
summary = truth["summary"]
|
||||
assert summary["check_mode_total"] == 1
|
||||
assert summary["apply_total"] == 1
|
||||
assert summary["applied"] is True
|
||||
assert summary["controlled_apply"] is True
|
||||
assert summary["latest_actor"] == "ansible_controlled_apply_worker"
|
||||
assert summary["latest_execution_mode"] == "controlled_apply"
|
||||
assert summary["latest_apply_executed"] is True
|
||||
|
||||
|
||||
def test_ansible_truth_keeps_catalog_hint_separate_from_runtime_use() -> None:
|
||||
truth = build_ansible_truth(
|
||||
[],
|
||||
@@ -1548,6 +1592,16 @@ def test_ansible_apply_receipt_backfill_includes_verifier_and_km_gaps() -> None:
|
||||
assert "_record_post_apply_verifier_and_learning" in source
|
||||
|
||||
|
||||
def test_ansible_live_controlled_apply_sends_telegram_receipt_but_backfill_does_not() -> None:
|
||||
live_source = inspect.getsource(run_controlled_apply_for_claim)
|
||||
backfill_source = inspect.getsource(backfill_missing_auto_repair_execution_receipts_once)
|
||||
|
||||
assert "_send_controlled_apply_telegram_receipt" in live_source
|
||||
assert "telegram_receipt_sent" in live_source
|
||||
assert "_send_controlled_apply_telegram_receipt" not in backfill_source
|
||||
assert inspect.iscoroutinefunction(_send_controlled_apply_telegram_receipt)
|
||||
|
||||
|
||||
def test_ansible_post_apply_verifier_helpers_are_deterministic() -> None:
|
||||
assert _post_apply_km_path_type("03ca6836-1b76-4da2-8e3e-6d3b6df9254a") == (
|
||||
"ansible_apply_receipt:03ca6836"
|
||||
|
||||
@@ -1030,6 +1030,105 @@ def test_awooop_status_chain_lines_show_ansible_apply_proof() -> None:
|
||||
assert ansible["approval_source"] == "user_chat_approved_continue"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_controlled_apply_result_receipt_marks_callback_reply_evidence(monkeypatch) -> None:
|
||||
gateway = TelegramGateway()
|
||||
sent_requests = []
|
||||
|
||||
async def fake_send_request(method, payload):
|
||||
sent_requests.append((method, payload))
|
||||
return {"ok": True, "result": {"message_id": 12345}}
|
||||
|
||||
async def fake_fetch_truth_chain(source_id, project_id):
|
||||
assert source_id == "INC-20260627-64472B"
|
||||
assert project_id == "awoooi"
|
||||
return {
|
||||
"truth_status": {
|
||||
"current_stage": "execution_succeeded",
|
||||
"stage_status": "success",
|
||||
"needs_human": False,
|
||||
"blockers": [],
|
||||
},
|
||||
"automation_quality": {
|
||||
"verdict": "auto_repaired_verified",
|
||||
"facts": {
|
||||
"auto_repair_execution_records": 1,
|
||||
"automation_operation_records": 3,
|
||||
"effective_execution_records": 2,
|
||||
"verification_result": "success",
|
||||
"mcp_gateway_total": 8,
|
||||
"knowledge_entries": 1,
|
||||
},
|
||||
"blockers": [],
|
||||
},
|
||||
"execution": {
|
||||
"automation_operation_log": [
|
||||
{
|
||||
"operation_type": "ansible_apply_executed",
|
||||
"status": "success",
|
||||
"actor": "ansible_controlled_apply_worker",
|
||||
"input_catalog_id": "ansible:188-momo-backup-user",
|
||||
"input_execution_mode": "controlled_apply",
|
||||
"input_playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
|
||||
}
|
||||
],
|
||||
"ansible": {
|
||||
"considered": True,
|
||||
"records": [
|
||||
{
|
||||
"operation_type": "ansible_apply_executed",
|
||||
"status": "success",
|
||||
"actor": "ansible_controlled_apply_worker",
|
||||
"catalog_id": "ansible:188-momo-backup-user",
|
||||
"playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
|
||||
"execution_mode": "controlled_apply",
|
||||
"apply_executed": True,
|
||||
"returncode": 0,
|
||||
"tags": ["ansible", "controlled_apply", "ai_agent_auto_execution"],
|
||||
}
|
||||
],
|
||||
"candidate_catalog": {"candidates": []},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
monkeypatch.setattr(TelegramGateway, "alert_chat_id", property(lambda _self: "chat"))
|
||||
monkeypatch.setattr(gateway, "_send_request", fake_send_request)
|
||||
monkeypatch.setattr(
|
||||
"src.services.awooop_truth_chain_service.fetch_truth_chain",
|
||||
fake_fetch_truth_chain,
|
||||
)
|
||||
|
||||
result = await gateway.send_controlled_apply_result_receipt(
|
||||
incident_id="INC-20260627-64472B",
|
||||
catalog_id="ansible:188-momo-backup-user",
|
||||
apply_op_id="73b7a95c-3652-4c0d-bb4c-729e500acedb",
|
||||
playbook_path="infra/ansible/playbooks/188-momo-backup-user.yml",
|
||||
verification_result="success",
|
||||
returncode=0,
|
||||
duration_ms=7727,
|
||||
verifier_written=True,
|
||||
learning_written=True,
|
||||
project_id="awoooi",
|
||||
)
|
||||
|
||||
assert result["ok"] is True
|
||||
assert sent_requests
|
||||
method, payload = sent_requests[0]
|
||||
assert method == "sendMessage"
|
||||
assert "CONTROLLED APPLY RESULT" in payload["text"]
|
||||
assert "INC-20260627-64472B" in payload["text"]
|
||||
assert "ansible:188-momo-backup-user" in payload["text"]
|
||||
source_extra = payload["_awooop_source_envelope_extra"]
|
||||
assert source_extra["callback_reply"]["action"] == "controlled_apply_result"
|
||||
assert source_extra["callback_reply"]["status"] == "callback_reply_sent"
|
||||
assert source_extra["source_refs"]["incident_ids"] == ["INC-20260627-64472B"]
|
||||
snapshot = source_extra["awooop_status_chain"]
|
||||
assert snapshot["repair_state"] == "auto_repaired_verified"
|
||||
assert snapshot["operator_outcome"]["state"] == "completed_verified"
|
||||
assert snapshot["execution"]["ansible"]["controlled_apply"] is True
|
||||
|
||||
|
||||
def test_callback_reply_awooop_status_chain_snapshot_marks_manual_gate() -> None:
|
||||
"""Callback evidence 要保存當下 AwoooP 狀態鏈,不只保存 live query 結果。"""
|
||||
snapshot = telegram_gateway_module._callback_reply_awooop_status_chain_snapshot(
|
||||
|
||||
Reference in New Issue
Block a user