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
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:
@@ -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 = (
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user