From e5417e383f52422113b6ffad80e8e87625d225ee Mon Sep 17 00:00:00 2001 From: ogt Date: Fri, 10 Jul 2026 18:28:02 +0800 Subject: [PATCH] fix(agent): advance program ledger from runtime truth --- apps/api/src/api/v1/agents.py | 17 ++-- .../awoooi_priority_work_order_readback.py | 87 ++++++++++++++++++- ...awoooi_priority_work_order_readback_api.py | 44 ++++++++++ 3 files changed, 141 insertions(+), 7 deletions(-) diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 54f971c21..f31decf99 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -1222,13 +1222,17 @@ async def get_awoooi_priority_work_order_readback() -> dict[str, Any]: async def _build_awoooi_priority_work_order_readback_with_live_overlays() -> dict[str, Any]: - gitea_bundle_readback = await asyncio.to_thread( - load_latest_gitea_repo_bundle_backup_readback + ( + gitea_bundle_readback, + stockplatform_runtime, + reboot_slo, + autonomous_runtime_control, + ) = await asyncio.gather( + asyncio.to_thread(load_latest_gitea_repo_bundle_backup_readback), + asyncio.to_thread(load_latest_stockplatform_public_api_runtime_readback), + asyncio.to_thread(load_latest_reboot_auto_recovery_slo_scorecard), + build_ai_agent_autonomous_runtime_control_with_live_readback(), ) - stockplatform_runtime = await asyncio.to_thread( - load_latest_stockplatform_public_api_runtime_readback - ) - reboot_slo = await asyncio.to_thread(load_latest_reboot_auto_recovery_slo_scorecard) apply_gitea_repo_bundle_backup_readback(reboot_slo, gitea_bundle_readback) apply_stockplatform_runtime_readback(reboot_slo, stockplatform_runtime) payload = await asyncio.to_thread( @@ -1279,6 +1283,7 @@ async def _build_awoooi_priority_work_order_readback_with_live_overlays() -> dic verifier_readback=post_write_verifier, consumer_readback=consumer_readback, telegram_alert_context_verifier_readback=telegram_alert_context_verifier, + autonomous_runtime_control=autonomous_runtime_control, ) return payload diff --git a/apps/api/src/services/awoooi_priority_work_order_readback.py b/apps/api/src/services/awoooi_priority_work_order_readback.py index 243aa8f23..e5c89941c 100644 --- a/apps/api/src/services/awoooi_priority_work_order_readback.py +++ b/apps/api/src/services/awoooi_priority_work_order_readback.py @@ -6923,6 +6923,44 @@ def _apply_ai_automation_program_ledger(payload: dict[str, Any]) -> None: ) work_items = copy.deepcopy(_AI_AUTOMATION_PROGRAM_WORK_ITEMS) + strict_runtime_closed = ( + summary.get("ai_agent_autonomous_runtime_strict_closed") is True + ) + if strict_runtime_closed: + runtime_work_item = next( + item for item in work_items if item["id"] == "AIA-P0-001" + ) + runtime_work_item["status"] = "done" + runtime_work_item["completion_evidence"] = { + "source": "agent-autonomous-runtime-control", + "generated_at": str( + summary.get("ai_agent_autonomous_runtime_generated_at") or "" + ), + "automation_run_id": str( + summary.get("ai_agent_autonomous_runtime_automation_run_id") or "" + ), + "completion_percent": _int( + summary.get("ai_agent_autonomous_runtime_completion_percent") + ), + "required_stage_count": _int( + summary.get("ai_agent_autonomous_runtime_required_stage_count") + ), + "present_stage_count": _int( + summary.get("ai_agent_autonomous_runtime_present_stage_count") + ), + "same_run_correlation": ( + summary.get("ai_agent_autonomous_runtime_same_run_correlation") + is True + ), + "latest_flow_closed": ( + summary.get("ai_agent_autonomous_runtime_latest_flow_closed") + is True + ), + "latest_loop_closed": ( + summary.get("ai_agent_autonomous_runtime_latest_loop_closed") + is True + ), + } status_counts = { status: sum(1 for item in work_items if item["status"] == status) for status in ("done", "in_progress", "pending", "blocked", "deferred") @@ -6957,7 +6995,11 @@ def _apply_ai_automation_program_ledger(payload: dict[str, Any]) -> None: program = { "schema_version": _AI_AUTOMATION_PROGRAM_SCHEMA_VERSION, - "status": "runtime_closure_in_progress", + "status": ( + "p0_001_closed_next_priority_active" + if strict_runtime_closed + else "runtime_closure_in_progress" + ), "authoritative_for_execution_order": True, "program_goal": "讓所有受管主機、服務、產品、網站、工具、套件、資料與可觀測表面都由專業 AI Agent 完成可驗證、可回滾、可學習的自主閉環。", "source": _AI_AUTOMATION_PROGRAM_SOURCE, @@ -7157,12 +7199,55 @@ def apply_ai_automation_live_closure_readbacks( verifier_readback: dict[str, Any] | None = None, consumer_readback: dict[str, Any] | None = None, telegram_alert_context_verifier_readback: dict[str, Any] | None = None, + autonomous_runtime_control: dict[str, Any] | None = None, ) -> None: """Overlay live AI-loop executor/verifier/consumer readbacks onto receipts.""" summary = _dict(payload.setdefault("summary", {})) rollups = _dict(payload.setdefault("rollups", {})) state = _dict(payload.setdefault("mainline_execution_state", {})) + runtime_control = _dict(autonomous_runtime_control) + strict_runtime = _dict(runtime_control.get("strict_runtime_completion")) + strict_runtime_closed = bool( + strict_runtime.get("closed") is True + and strict_runtime.get("same_run_correlation") is True + and strict_runtime.get("latest_flow_closed") is True + and strict_runtime.get("latest_loop_closed") is True + and _int(strict_runtime.get("present_stage_count")) + == _int(strict_runtime.get("required_stage_count")) + and _int(strict_runtime.get("required_stage_count")) > 0 + ) + if runtime_control: + summary["ai_agent_autonomous_runtime_generated_at"] = str( + runtime_control.get("generated_at") or "" + ) + summary["ai_agent_autonomous_runtime_automation_run_id"] = str( + strict_runtime.get("automation_run_id") or "" + ) + summary["ai_agent_autonomous_runtime_completion_percent"] = _int( + strict_runtime.get("completion_percent") + ) + summary["ai_agent_autonomous_runtime_required_stage_count"] = _int( + strict_runtime.get("required_stage_count") + ) + summary["ai_agent_autonomous_runtime_present_stage_count"] = _int( + strict_runtime.get("present_stage_count") + ) + summary["ai_agent_autonomous_runtime_same_run_correlation"] = ( + strict_runtime.get("same_run_correlation") is True + ) + summary["ai_agent_autonomous_runtime_latest_flow_closed"] = ( + strict_runtime.get("latest_flow_closed") is True + ) + summary["ai_agent_autonomous_runtime_latest_loop_closed"] = ( + strict_runtime.get("latest_loop_closed") is True + ) + summary["ai_agent_autonomous_runtime_strict_closed"] = ( + strict_runtime_closed + ) + rollups["ai_agent_autonomous_runtime_strict_closed_count"] = ( + 1 if strict_runtime_closed else 0 + ) executor = _dict(executor_readback) executor_rollups = _dict(executor.get("rollups")) diff --git a/apps/api/tests/test_awoooi_priority_work_order_readback_api.py b/apps/api/tests/test_awoooi_priority_work_order_readback_api.py index 7fee6dbaa..5be858528 100644 --- a/apps/api/tests/test_awoooi_priority_work_order_readback_api.py +++ b/apps/api/tests/test_awoooi_priority_work_order_readback_api.py @@ -82,6 +82,28 @@ def _gitea_bundle_ready() -> dict: } +def _autonomous_runtime_control_closed() -> dict: + return { + "generated_at": "2026-07-10T10:22:56+00:00", + "strict_runtime_completion": { + "schema_version": "ai_agent_strict_runtime_completion_v1", + "completion_percent": 100, + "required_stage_count": 7, + "present_stage_count": 7, + "missing_stage_ids": [], + "latest_flow_closed": True, + "latest_loop_closed": True, + "same_run_correlation": True, + "automation_run_id": "run-p0-001-closed", + "closed": True, + }, + } + + +async def _autonomous_runtime_control_closed_async() -> dict: + return _autonomous_runtime_control_closed() + + def test_awoooi_priority_work_order_readback_loader_returns_mainline_order(): payload = load_latest_awoooi_priority_work_order_readback() @@ -1758,6 +1780,7 @@ def test_ai_automation_live_closure_readbacks_close_node_receipts(): verifier_readback=verifier, consumer_readback=_consumer_readback_ready(), telegram_alert_context_verifier_readback=_telegram_alert_context_verifier_ready(), + autonomous_runtime_control=_autonomous_runtime_control_closed(), ) receipts_by_id = { @@ -1797,6 +1820,22 @@ def test_ai_automation_live_closure_readbacks_close_node_receipts(): assert payload["summary"][ "ai_automation_telegram_alert_post_apply_verifier_ready" ] is True + program = payload["ai_automation_program_ledger"] + program_items = {item["id"]: item for item in program["work_items"]} + assert program["summary"]["next_work_item_id"] == "AIA-P0-002" + assert program["summary"]["completion_percent"] == 12 + assert program_items["AIA-P0-001"]["status"] == "done" + assert program_items["AIA-P0-001"]["completion_evidence"] == { + "source": "agent-autonomous-runtime-control", + "generated_at": "2026-07-10T10:22:56+00:00", + "automation_run_id": "run-p0-001-closed", + "completion_percent": 100, + "required_stage_count": 7, + "present_stage_count": 7, + "same_run_correlation": True, + "latest_flow_closed": True, + "latest_loop_closed": True, + } assert payload["summary"][ "ai_automation_telegram_alert_post_apply_verified_context_receipt_count" ] == 6 @@ -1835,6 +1874,11 @@ def test_awoooi_priority_work_order_readback_endpoint_returns_snapshot( "load_latest_telegram_alert_learning_context_post_apply_verifier", _telegram_alert_context_verifier_ready_async, ) + monkeypatch.setattr( + agents, + "build_ai_agent_autonomous_runtime_control_with_live_readback", + _autonomous_runtime_control_closed_async, + ) app = FastAPI() app.include_router(router, prefix="/api/v1") client = TestClient(app)