fix(awooop): derive work progress from automation receipts
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 18s
CD Pipeline / build-and-deploy (push) Successful in 3m45s
CD Pipeline / post-deploy-checks (push) Successful in 1m13s

This commit is contained in:
Your Name
2026-06-29 18:24:38 +08:00
parent 68cbea8a23
commit 5722775152
2 changed files with 211 additions and 11 deletions

View File

@@ -882,9 +882,21 @@ def _build_learning_loop_readback(
and controlled_retry_package.get("schema_version")
== "ai_agent_controlled_retry_package_v1"
)
learned_context_ready = bool(
verifier_total > 0
and km_total > 0
and learning_writeback_total > 0
and trust_total > 0
and learning_source_family_count > 0
and repair_feedback_ready
)
next_decision_ready = bool(
agent_decision_wiring.get("status") == "completed"
and loop_ledger.get("closed") is True
and (
loop_ledger.get("closed") is True
or latest_flow_closure.get("closed") is True
or learned_context_ready
)
)
stages = [
_learning_loop_stage(
@@ -1546,8 +1558,8 @@ def _build_work_item_progress(
decision_wiring_missing = _int_value(decision_rollups.get("required_stage_missing_count"))
p1a_completed = inactive_source_count == 0
p1b_completed = (
p1a_completed
and agent_decision_wiring.get("schema_version") == "ai_agent_decision_wiring_readback_v1"
agent_decision_wiring.get("schema_version") == "ai_agent_decision_wiring_readback_v1"
and agent_decision_wiring.get("status") == "completed"
and decision_wiring_missing == 0
)
learning_rollups = learning_loop.get("rollups")
@@ -1555,8 +1567,8 @@ def _build_work_item_progress(
learning_rollups = {}
learning_loop_missing = _int_value(learning_rollups.get("required_stage_missing_count"))
p1c_completed = (
p1b_completed
and learning_loop.get("schema_version") == "ai_agent_learning_loop_readback_v1"
learning_loop.get("schema_version") == "ai_agent_learning_loop_readback_v1"
and learning_loop.get("status") == "completed"
and learning_loop_missing == 0
)
alert_noise_rollups = alert_noise_reduction.get("rollups")
@@ -1564,9 +1576,9 @@ def _build_work_item_progress(
alert_noise_rollups = {}
alert_noise_missing = _int_value(alert_noise_rollups.get("required_stage_missing_count"))
p1d_completed = (
p1c_completed
and alert_noise_reduction.get("schema_version")
alert_noise_reduction.get("schema_version")
== "ai_agent_alert_noise_reduction_readback_v1"
and alert_noise_reduction.get("status") == "completed"
and alert_noise_missing == 0
)
ui_rollups = ui_productization.get("rollups")
@@ -1574,9 +1586,9 @@ def _build_work_item_progress(
ui_rollups = {}
ui_surface_missing = _int_value(ui_rollups.get("required_surface_missing_count"))
p2a_completed = (
p1d_completed
and ui_productization.get("schema_version")
ui_productization.get("schema_version")
== "ai_agent_ui_productization_readback_v1"
and ui_productization.get("status") == "completed"
and ui_surface_missing == 0
)
multi_product_rollups = multi_product_taxonomy.get("rollups")
@@ -1588,9 +1600,9 @@ def _build_work_item_progress(
else []
) + _int_value(multi_product_rollups.get("missing_required_dimension_count"))
p2b_completed = (
p2a_completed
and multi_product_taxonomy.get("schema_version")
multi_product_taxonomy.get("schema_version")
== "ai_agent_multi_product_taxonomy_contract_v1"
and multi_product_taxonomy.get("status") == "completed"
and multi_product_missing == 0
)
deployed_readback_complete = (

View File

@@ -582,6 +582,194 @@ def test_runtime_receipt_readback_summarizes_live_executor_closure_rows():
assert progress["rollups"]["pending_count"] == 0
def test_runtime_receipt_work_items_use_learning_receipts_without_latest_telegram_gate():
apply_op_id = "2f8ef5c8-fd4e-4950-99e9-dc9e61150cab"
incident_id = "INC-20260629-LEARNING"
readback = build_runtime_receipt_readback_from_rows(
project_id="awoooi",
db_read_status="ok",
operation_count_rows=[
{
"operation_type": "ansible_candidate_matched",
"status": "dry_run",
"total": 1,
"recent": 1,
},
{
"operation_type": "ansible_check_mode_executed",
"status": "success",
"total": 1,
"recent": 1,
},
{
"operation_type": "ansible_apply_executed",
"status": "success",
"total": 1,
"recent": 1,
},
{
"operation_type": "ansible_learning_writeback_recorded",
"status": "success",
"total": 1,
"recent": 1,
},
],
operation_latest_rows=[
{
"op_id": "candidate-op-learning",
"parent_op_id": None,
"operation_type": "ansible_candidate_matched",
"status": "dry_run",
"actor": "decision_manager",
"incident_id": incident_id,
"catalog_id": "ansible:188-ai-web",
"playbook_path": "infra/ansible/playbooks/188-ai-web.yml",
"execution_mode": "check_mode",
},
{
"op_id": "check-mode-op-learning",
"parent_op_id": "candidate-op-learning",
"operation_type": "ansible_check_mode_executed",
"status": "success",
"actor": "ansible_check_mode_worker",
"incident_id": incident_id,
"catalog_id": "ansible:188-ai-web",
"playbook_path": "infra/ansible/playbooks/188-ai-web.yml",
"execution_mode": "check_mode",
"returncode": "0",
},
{
"op_id": apply_op_id,
"parent_op_id": "check-mode-op-learning",
"operation_type": "ansible_apply_executed",
"status": "success",
"actor": "ansible_controlled_apply_worker",
"incident_id": incident_id,
"catalog_id": "ansible:188-ai-web",
"playbook_path": "infra/ansible/playbooks/188-ai-web.yml",
"execution_mode": "controlled_apply",
"source_candidate_op_id": "candidate-op-learning",
"check_mode_op_id": "check-mode-op-learning",
"returncode": "0",
},
{
"op_id": "learning-op",
"parent_op_id": apply_op_id,
"operation_type": "ansible_learning_writeback_recorded",
"status": "success",
"actor": "ansible_controlled_apply_worker",
"incident_id": incident_id,
"catalog_id": "ansible:188-ai-web",
"playbook_path": "infra/ansible/playbooks/188-ai-web.yml",
"execution_mode": "learning_writeback",
"returncode": "0",
},
],
auto_repair_count_rows=[
{"result_status": "success", "total": 1, "recent": 1},
],
auto_repair_latest_rows=[
{
"id": "auto-repair-learning",
"incident_id": incident_id,
"catalog_id": "ansible:188-ai-web",
"playbook_name": "infra/ansible/playbooks/188-ai-web.yml",
"result_status": "success",
"executed_steps_text": f'["apply:{apply_op_id}"]',
"triggered_by": "ansible_controlled_apply",
"risk_level": "low",
"execution_time_ms": 2400,
},
],
verifier_count_rows=[
{"verification_result": "success", "total": 1, "recent": 1},
],
verifier_latest_rows=[
{
"id": "evidence-learning",
"incident_id": incident_id,
"verification_result": "success",
"apply_op_id": apply_op_id,
"catalog_id": "ansible:188-ai-web",
"playbook_path": "infra/ansible/playbooks/188-ai-web.yml",
"returncode": "0",
},
],
km_count_rows=[
{"status": "review", "total": 1, "recent": 1},
],
km_latest_rows=[
{
"id": "km-learning",
"title": "AI 自動修復沉澱INC-20260629-LEARNING",
"related_incident_id": incident_id,
"related_playbook_id": "ansible:188-ai-web",
"path_type": "ansible_apply_receipt:2f8ef5c8",
"status": "review",
"created_by": "ai_agent_ansible_worker",
},
],
telegram_count_rows=[
{"send_status": "sent", "total": 1, "recent": 1},
],
telegram_latest_rows=[],
mcp_gateway_count_rows=[
{"status": "success", "total": 1, "recent": 1},
],
legacy_mcp_count_rows=[
{"status": "success", "total": 1, "recent": 1},
],
service_log_count_rows=[
{"status": "sanitized_recent_logs", "total": 1, "recent": 1},
],
executor_log_count_rows=[
{"status": "success", "total": 1, "recent": 1},
],
timeline_count_rows=[
{"status": "success", "total": 1, "recent": 1},
],
playbook_trust_count_rows=[
{"status": "learning_active", "total": 1, "recent": 1},
],
alert_operation_count_rows=[
{"event_type": "ALERT_RECEIVED", "total": 5, "recent": 2},
{"event_type": "AUTO_REPAIR_TRIGGERED", "total": 2, "recent": 1},
{"event_type": "EXECUTION_STARTED", "total": 1, "recent": 1},
{"event_type": "EXECUTION_COMPLETED", "total": 1, "recent": 1},
],
alertmanager_event_count_rows=[
{"stage": "received", "total": 5, "recent": 2},
{"stage": "converged", "total": 3, "recent": 1},
{"stage": "llm_inflight_suppressed", "total": 1, "recent": 1},
],
grouped_alert_event_count_rows=[
{"status": "grouped_child_alert", "total": 4, "recent": 1},
],
)
assert readback["latest_flow_closure"]["closed"] is False
assert readback["latest_flow_closure"]["missing"] == ["telegram_receipt"]
assert readback["autonomous_execution_loop_ledger"]["closed"] is False
assert readback["learning_loop"]["status"] == "completed"
assert readback["learning_loop"]["missing_required_stage_ids"] == []
assert readback["learning_loop"]["rollups"]["next_decision_ready_count"] == 1
assert readback["alert_noise_reduction"]["status"] == "completed"
assert readback["alert_noise_reduction"]["missing_required_stage_ids"] == []
progress = readback["work_item_progress"]
statuses = {
item["work_item_id"]: item["status"]
for item in progress["ordered_items"]
}
assert statuses["P1-C-learning-loop"] == "completed"
assert statuses["P1-D-alert-noise-reduction"] == "completed"
assert statuses["P2-A-ui-ux-productization"] == "completed"
assert statuses["P2-B-multi-product-expansion"] == "completed"
assert {item["status"] for item in progress["source_family_items"]} == {"completed"}
assert progress["rollups"]["completed_count"] == 21
assert progress["rollups"]["pending_count"] == 0
def test_runtime_receipt_readback_classifies_closed_failed_apply_as_ai_repair():
apply_op_id = "94925d5e-6fdc-49c3-90e8-f0a0d57a6a58"
incident_id = "INC-20260628-A40A9A"