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 f89288907..65abf2958 100644 --- a/apps/api/src/services/awoooi_priority_work_order_readback.py +++ b/apps/api/src/services/awoooi_priority_work_order_readback.py @@ -521,6 +521,19 @@ def apply_ai_loop_current_blocker_execution_queue( post_apply_verifier = str(first_item.get("post_apply_verifier") or "") learning_targets = _strings(first_item.get("learning_writeback_targets")) target_batches = _strings(first_item.get("target_batches")) + local_console_plan = [ + _dict(item) + for item in _list(first_item.get("controlled_local_console_execution_plan")) + ] + local_console_phase_ids = [ + str(item.get("phase_id") or "") + for item in local_console_plan + if item.get("phase_id") + ] + post_recovery_readback_commands = _strings( + first_item.get("post_recovery_readback_commands") + ) + forbidden_runtime_actions = _strings(first_item.get("forbidden_runtime_actions")) external_blocker = str(first_item.get("external_control_path_blocker") or "") runtime_write_gate = str(first_item.get("runtime_write_gate") or "") @@ -573,6 +586,21 @@ def apply_ai_loop_current_blocker_execution_queue( learning_targets ) evidence["ai_loop_current_blocker_target_batches"] = target_batches + evidence["ai_loop_current_blocker_local_console_phase_count"] = len( + local_console_plan + ) + evidence["ai_loop_current_blocker_local_console_phase_ids"] = ( + local_console_phase_ids + ) + evidence["ai_loop_current_blocker_local_console_execution_plan"] = ( + local_console_plan + ) + evidence["ai_loop_current_blocker_post_recovery_readback_commands"] = ( + post_recovery_readback_commands + ) + evidence["ai_loop_current_blocker_forbidden_runtime_actions"] = ( + forbidden_runtime_actions + ) evidence["ai_loop_current_blocker_metadata_only_writeback_ready"] = bool( first_item.get("metadata_only_writeback_ready") is True ) @@ -590,7 +618,7 @@ def apply_ai_loop_current_blocker_execution_queue( "queue item with metadata-only KM/RAG/MCP/PlayBook/AI Agent " "writeback targets. The next action is not generic manual triage; " "it is 110 control-path readback plus the bounded local recovery " - "package and post-apply verifier." + "package, ordered local-console phases, and post-apply verifier." ) professional_fix = _dict(workplan.setdefault("professional_fix", {})) professional_fix["action"] = ( @@ -611,8 +639,8 @@ def apply_ai_loop_current_blocker_execution_queue( ( "P0-006-AI-LOOP-CURRENT-BLOCKER-EXECUTION-QUEUE: execute the " f"{blocker_id} queue item through 110 control-path readback, " - "bounded recovery package check-mode, post-apply verifier, and " - "metadata-only KM/RAG/MCP/PlayBook writeback." + "ordered local-console phases, bounded recovery package check-mode, " + "post-apply verifier, and metadata-only KM/RAG/MCP/PlayBook writeback." ), ( "P0-006-HARBOR-REGISTRY-CONTROLLED-RECOVERY-PREFLIGHT: after the " @@ -636,6 +664,18 @@ def apply_ai_loop_current_blocker_execution_queue( ) summary["ai_loop_current_blocker_post_apply_verifier"] = post_apply_verifier summary["ai_loop_current_blocker_learning_target_count"] = len(learning_targets) + summary["ai_loop_current_blocker_local_console_phase_count"] = len( + local_console_plan + ) + summary["ai_loop_current_blocker_local_console_phase_ids"] = ( + local_console_phase_ids + ) + summary["ai_loop_current_blocker_post_recovery_readback_command_count"] = len( + post_recovery_readback_commands + ) + summary["ai_loop_current_blocker_forbidden_runtime_action_count"] = len( + forbidden_runtime_actions + ) def _harbor_registry_next_state(*, status: str, candidate_packaged: bool) -> str: 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 bfa32e611..123a17639 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 @@ -241,6 +241,26 @@ def test_awoooi_priority_work_order_readback_overlays_ai_loop_current_blocker_qu "ai_agent", } assert len(evidence["ai_loop_current_blocker_target_batches"]) == 6 + assert evidence["ai_loop_current_blocker_local_console_phase_count"] == 5 + assert evidence["ai_loop_current_blocker_local_console_phase_ids"] == [ + "diagnose_ssh_publickey", + "preflight_control_path_and_harbor", + "repair_ssh_metadata_if_check_confirms_metadata_drift", + "repair_harbor_once_if_v2_still_502", + "verify_controlled_cd_lane", + ] + assert evidence["ai_loop_current_blocker_local_console_execution_plan"][0][ + "mode" + ] == "read_only" + assert evidence["ai_loop_current_blocker_local_console_execution_plan"][3][ + "command" + ] == "recover-110-control-path-and-harbor-local.sh --repair-harbor-once" + assert "read-public-gitea-actions-queue.py --json" in evidence[ + "ai_loop_current_blocker_post_recovery_readback_commands" + ] + assert "restart_docker_daemon" in evidence[ + "ai_loop_current_blocker_forbidden_runtime_actions" + ] assert evidence["ai_loop_current_blocker_metadata_only_writeback_ready"] is True assert evidence["ai_loop_current_blocker_runtime_dispatch_performed"] is False assert evidence["ai_loop_current_blocker_raw_payload_required"] is False @@ -250,6 +270,13 @@ def test_awoooi_priority_work_order_readback_overlays_ai_loop_current_blocker_qu assert payload["summary"]["ai_loop_current_blocker_id"] == ( "harbor_110_repair_no_matching_runner" ) + assert payload["summary"]["ai_loop_current_blocker_local_console_phase_count"] == 5 + assert payload["summary"][ + "ai_loop_current_blocker_post_recovery_readback_command_count" + ] == 3 + assert payload["summary"][ + "ai_loop_current_blocker_forbidden_runtime_action_count" + ] >= 5 assert payload["next_execution_order"][0].startswith( "P0-006-AI-LOOP-CURRENT-BLOCKER-EXECUTION-QUEUE" ) diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 522b4dc75..27f61d043 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -50830,3 +50830,18 @@ production browser smoke: **下一步**: - commit / push 後等待 CD;若仍卡 Harbor / 110,主線下一步不變:恢復 110 `awoooi-host` controlled lane 或在 110 local console / root shell 執行 recovery package check-mode,再重讀 Harbor queue、registry `/v2/`、deploy marker,並把 post-apply verifier receipt 回寫到 AI Loop。 + +## 2026-06-30 — 22:52 AI Loop local-console phase 投影到 P0 work-order + +**完成內容**: +- 最新 AI Loop executor 已有 `controlled_local_console_execution_plan`;本輪把這 5 個 local-console phase 投影到 `awoooi-priority-work-order-readback` 的 P0 evidence / summary。 +- P0 work-order 現在可直接看到 phase ids:`diagnose_ssh_publickey`、`preflight_control_path_and_harbor`、`repair_ssh_metadata_if_check_confirms_metadata_drift`、`repair_harbor_once_if_v2_still_502`、`verify_controlled_cd_lane`。 +- 同步投影 `post_recovery_readback_commands` 與 `forbidden_runtime_actions`,讓工作順序頁不只知道 package/verifier,也知道可執行節點、回讀命令與禁止動作。 + +**本地驗證結果**: +- `DATABASE_URL=postgresql+asyncpg://test:test@localhost:5432/test PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_awoooi_priority_work_order_readback_api.py apps/api/tests/test_ai_agent_log_controlled_writeback_executor_readback_api.py -q`:`11 passed`。 +- `ruff check`、`py_compile`、`git diff --check`:通過。 +- 本地 payload 摘要:`status=p0_006_blocked_ai_loop_current_blocker_execution_queue`、`phase_count=5`、`readback_command_count=3`、`forbidden_action_count=6`。 + +**下一步**: +- commit / push 後讀回 Gitea CD;若仍卡 Harbor / 110,P0 runtime 下一步仍是 110 local console / root shell 按 phase 執行 check-mode / bounded repair,並把 verifier receipt 回寫 AI Loop。