fix(awooop): send controlled apply telegram receipts
Some checks failed
CD Pipeline / tests (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
Code Review / ai-code-review (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-27 20:49:32 +08:00
parent de609a79ae
commit 4e4c56cae5
5 changed files with 310 additions and 1 deletions

View File

@@ -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"

View File

@@ -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(