diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 82ea2f477..2f4d1c303 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -7,33 +7,27 @@ from src.api.v1.agents import router from src.services.delivery_closure_workbench import load_delivery_closure_workbench REBOOT_SAFE_NEXT_STEP = ( - "collect_windows99_vmware_autostart_verify_readback_then_rerun_all_host_" - "reboot_scorecard_no_secret_no_reboot" + "restore_windows99_no_secret_management_channel_or_collect_local_console_" + "verify_readback_then_rerun_reboot_scorecard_no_reboot" ) REBOOT_ACTIVE_BLOCKERS = [ "all_required_hosts_not_in_10_minute_reboot_window", "backup_core_green_not_1", - "host_188_service_green_not_1", "host_boot_observation_older_than_target_window", "host_unreachable_after_reboot", "host_uptime_unknown", - "local_disk_free_below_minimum", "post_start_blocked_not_zero", - "product_data_green_not_1", "reboot_event_required_host_unreachable", "service_green_not_1", "wazuh_dashboard_degraded", + "windows99_remote_execution_channel_unavailable", "windows99_vmware_autostart_readback_missing", ] DRILL_ACTIVE_BLOCKERS = [ "service_green_not_ready", - "product_data_green_not_ready", "backup_core_green_not_ready", - "host_188_service_green_not_ready", "all_required_hosts_reachable_not_ready", "latest_verify_only_metric_present_not_ready", - "stockplatform_freshness_ok_not_ready", - "stockplatform_ingestion_ok_not_ready", "blocked_only_by_fresh_reboot_window_not_ready", ] @@ -259,8 +253,8 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["source_id"] == "reboot_auto_recovery_slo_scorecard" assert lane["status"] == "blocked_reboot_auto_recovery_slo_not_ready" - assert lane["blocker_count"] == 13 - assert lane["completion_percent"] == 15 + assert lane["blocker_count"] == 11 + assert lane["completion_percent"] == 43 assert lane["metric"]["kind"] == "reboot_auto_recovery_slo" assert lane["metric"]["workplan_id"] == "P0-006" assert lane["metric"]["target_minutes"] == 10 @@ -270,12 +264,12 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["unreachable_host_count"] == 1 assert lane["metric"]["stale_host_count"] == 5 assert lane["metric"]["service_green"] is False - assert lane["metric"]["product_data_green"] is False + assert lane["metric"]["product_data_green"] is True assert lane["metric"]["backup_core_green"] is False - assert lane["metric"]["stockplatform_freshness_status"] == "not_configured" - assert lane["metric"]["stockplatform_ingestion_status"] == "not_configured" - assert lane["metric"]["stockplatform_freshness_blocker_count"] == 1 - assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 1 + assert lane["metric"]["stockplatform_freshness_status"] == "ok" + assert lane["metric"]["stockplatform_ingestion_status"] == "ok" + assert lane["metric"]["stockplatform_freshness_blocker_count"] == 0 + assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 0 assert lane["metric"]["stockplatform_final_retry_window_passed"] is False assert lane["metric"]["stockplatform_controlled_recovery_gate_required"] is False assert lane["metric"]["drill_preflight_status"] == ( @@ -283,7 +277,7 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): ) assert lane["metric"]["drill_preflight_ready"] is False assert lane["metric"]["drill_preflight_ready_count"] == 0 - assert lane["metric"]["drill_preflight_blocker_count"] == 9 + assert lane["metric"]["drill_preflight_blocker_count"] == 5 assert lane["metric"]["drill_preflight_active_blockers"] == DRILL_ACTIVE_BLOCKERS assert ( lane["metric"]["drill_preflight_break_glass_authorization_required"] @@ -334,7 +328,7 @@ def _assert_delivery_workbench_shape(data: dict): ) assert data["readback"]["current_p0_safe_next_step"] == data["safe_next_step"] assert data["readback"]["current_p0_active_blockers"] == data["active_blockers"] - assert data["readback"]["current_p0_readiness_percent"] == 15 + assert data["readback"]["current_p0_readiness_percent"] == 43 assert data["readback"]["current_p0_drill_preflight_status"] == ( "blocked_reboot_drill_preflight_not_ready" ) @@ -359,23 +353,23 @@ def _assert_delivery_workbench_shape(data: dict): assert data["rollups"]["high_risk_blocker_count"] == data["summary"][ "high_risk_blocker_count" ] - assert data["rollups"]["current_p0_blocker_count"] == 13 - assert data["rollups"]["current_p0_readiness_percent"] == 15 + assert data["rollups"]["current_p0_blocker_count"] == 11 + assert data["rollups"]["current_p0_readiness_percent"] == 43 assert data["rollups"]["current_p0_blocked_by_fresh_reboot_window_only"] is False assert data["rollups"]["current_p0_service_green"] is False - assert data["rollups"]["current_p0_product_data_green"] is False + assert data["rollups"]["current_p0_product_data_green"] is True assert data["rollups"]["current_p0_backup_core_green"] is False assert ( data["rollups"]["current_p0_stockplatform_freshness_status"] - == "not_configured" + == "ok" ) assert ( data["rollups"]["current_p0_stockplatform_ingestion_status"] - == "not_configured" + == "ok" ) assert data["rollups"]["current_p0_drill_preflight_ready"] is False assert data["rollups"]["current_p0_drill_preflight_ready_count"] == 0 - assert data["rollups"]["current_p0_drill_preflight_blocker_count"] == 9 + assert data["rollups"]["current_p0_drill_preflight_blocker_count"] == 5 assert data["rollups"]["current_p0_drill_preflight_break_glass_required"] is True assert ( data["rollups"][ @@ -432,20 +426,20 @@ def _assert_delivery_workbench_shape(data: dict): "blocked_reboot_auto_recovery_slo_not_ready" ) assert data["summary"]["reboot_auto_recovery_workplan_id"] == "P0-006" - assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 15 - assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 13 + assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 43 + assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 11 assert data["summary"]["reboot_auto_recovery_can_claim_slo"] is False assert data["summary"]["reboot_auto_recovery_service_green"] is False - assert data["summary"]["reboot_auto_recovery_product_data_green"] is False + assert data["summary"]["reboot_auto_recovery_product_data_green"] is True assert data["summary"]["reboot_auto_recovery_observed_host_count"] == 7 assert data["summary"]["reboot_auto_recovery_stale_host_count"] == 5 assert ( data["summary"]["reboot_auto_recovery_stockplatform_freshness_status"] - == "not_configured" + == "ok" ) assert ( data["summary"]["reboot_auto_recovery_stockplatform_ingestion_status"] - == "not_configured" + == "ok" ) assert ( data["summary"][ @@ -465,7 +459,7 @@ def _assert_delivery_workbench_shape(data: dict): ) assert data["summary"]["reboot_auto_recovery_drill_preflight_ready"] is False assert data["summary"]["reboot_auto_recovery_drill_preflight_ready_count"] == 0 - assert data["summary"]["reboot_auto_recovery_drill_preflight_blocker_count"] == 9 + assert data["summary"]["reboot_auto_recovery_drill_preflight_blocker_count"] == 5 assert ( data["summary"][ "reboot_auto_recovery_drill_preflight_break_glass_required" diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index e6b5c3c4f..ed10059da 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -52881,3 +52881,25 @@ production browser smoke: **下一步**: - commit / push 到 Gitea main 後讀回 CD;deploy 後驗證 `/api/v1/agents/awoooi-priority-work-order-readback` 與 `/zh-TW/awooop/work-items` 顯示 55 個插入工作項與新增 spotlight。 + +## 2026-07-02 — P0 reboot Windows99 management channel CD #4402 workbench contract 修正 + +**完成內容**: +- Gitea public queue 讀回 `aaa617f00 fix(reboot): expose windows99 management channel readback` 的 `cd.yaml` run `#4402` 狀態為 `Failure`;production API 仍停在舊版,不能宣稱已部署。 +- 公開 tests log 定位失敗點:`apps/api/tests/test_delivery_closure_workbench_api.py::test_delivery_closure_workbench_endpoint_returns_product_summary` 仍硬期待舊 safe next step `collect_windows99_vmware_autostart_verify_readback_then_rerun_all_host_reboot_scorecard_no_secret_no_reboot`。 +- 已同步 delivery closure workbench 測試 contract 到新版 reboot scorecard truth:safe next step 改為 `restore_windows99_no_secret_management_channel_or_collect_local_console_verify_readback_then_rerun_reboot_scorecard_no_reboot`,active blockers 從 `13` 收斂到 `11`,readiness 從 `15` 升為 `43`,`product_data_green=true`,StockPlatform freshness / ingestion 為 `ok`,drill preflight blockers 從 `9` 收斂到 `5`。 +- 修正重點是測試與 readback contract 補齊,不是放寬 SLO;`windows99_remote_execution_channel_unavailable` 與 `windows99_vmware_autostart_readback_missing` 仍為 P0 blocker。 + +**本地驗證結果**: +- `DATABASE_URL=postgresql+asyncpg://test:test@localhost/test PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_delivery_closure_workbench_api.py -q -p no:cacheprovider`:`6 passed`。 +- `DATABASE_URL=postgresql+asyncpg://test:test@localhost/test PYTHONPATH=apps/api python3.11 -m pytest ops/runner/test_cd_controlled_runtime_profile.py scripts/reboot-recovery/tests/test_reboot_auto_recovery_slo_scorecard.py apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py apps/api/tests/test_awoooi_priority_work_order_readback_api.py apps/api/tests/test_delivery_closure_workbench_api.py -q -p no:cacheprovider`:`79 passed`。 +- `python3.11 -m py_compile apps/api/tests/test_delivery_closure_workbench_api.py scripts/reboot-recovery/windows99-management-channel-probe.py scripts/reboot-recovery/reboot-auto-recovery-slo-scorecard.py apps/api/src/services/reboot_auto_recovery_slo_scorecard.py apps/api/src/services/awoooi_priority_work_order_readback.py apps/api/src/services/delivery_closure_workbench.py`:通過。 +- `git diff --check`:通過。 + +**仍維持**: +- 沒有讀 secret / runner token / `.runner` 內容 / `.env` / raw sessions / SQLite / auth。 +- 沒有使用 GitHub / gh / GitHub API / GitHub Actions。 +- 沒有重啟主機,沒有 Docker / Nginx / K3s / DB / firewall restart,沒有 workflow_dispatch,沒有 DROP / TRUNCATE / restore / prune。 + +**下一步**: +- amend / push 到 Gitea main 後讀回新的 CD;deploy 後驗證 reboot scorecard 與 priority work-order production API 已出現 `windows99_management_channel` 與 `windows99_remote_execution_channel_ready=false`。