diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index f9ffd587c..94bc98410 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -1120,12 +1120,69 @@ def build_delivery_closure_workbench( if _int(lane["blocker_count"]) > 0 or _int(lane["completion_percent"]) < 80 ][:5] + delivery_status = ( + "blocked_delivery_actions_required" if high_risk_blocker_count else "ready" + ) + current_p0_safe_next_step = str(reboot_readback.get("safe_next_step") or "") + current_p0_workplan_id = str(reboot_readback.get("workplan_id") or "P0-006") + current_p0_active_blockers = _strings(reboot_slo.get("active_blockers")) + return { "schema_version": _SCHEMA_VERSION, "generated_at": max(generated_candidates) if generated_candidates else "", - "status": "blocked_delivery_actions_required" - if high_risk_blocker_count - else "ready", + "status": delivery_status, + "safe_next_step": current_p0_safe_next_step, + "active_blockers": current_p0_active_blockers, + "readback": { + "workbench_status": delivery_status, + "current_p0_workplan_id": current_p0_workplan_id, + "current_p0_source_id": "reboot_auto_recovery_slo_scorecard", + "current_p0_status": str(reboot_slo.get("status") or ""), + "current_p0_safe_next_step": current_p0_safe_next_step, + "current_p0_active_blockers": current_p0_active_blockers, + "current_p0_readiness_percent": _int( + reboot_rollups.get("readiness_percent") + ), + "closed_p0_workplan_ids": ["P0-003", "P0-005"], + "github_lane_status": "stopped_retired_do_not_use", + "secret_value_collection_allowed": False, + "workflow_trigger_authorized": False, + "runtime_write_authorized": False, + }, + "rollups": { + "source_count": len(source_statuses), + "loaded_source_count": loaded_source_count, + "average_completion_percent": average_completion, + "high_risk_blocker_count": high_risk_blocker_count, + "current_p0_blocker_count": reboot_blockers, + "current_p0_readiness_percent": _int( + reboot_rollups.get("readiness_percent") + ), + "current_p0_blocked_by_fresh_reboot_window_only": ( + reboot_rollups.get("blocked_by_fresh_reboot_window_only") is True + ), + "current_p0_service_green": reboot_rollups.get("service_green") is True, + "current_p0_product_data_green": ( + reboot_rollups.get("product_data_green") is True + ), + "current_p0_backup_core_green": ( + reboot_rollups.get("backup_core_green") is True + ), + "current_p0_stockplatform_freshness_status": str( + reboot_rollups.get("stockplatform_freshness_status") or "" + ), + "current_p0_stockplatform_ingestion_status": str( + reboot_rollups.get("stockplatform_ingestion_status") or "" + ), + "gitea_private_inventory_blocker_count": private_inventory_blockers, + "credential_escrow_blocker_count": credential_escrow_missing_items, + "production_deploy_hard_blocker_count": _int( + production_deploy_rollups.get("hard_blocker_count") + ), + "secret_values_collected": False, + "workflow_trigger_authorized": False, + "runtime_write_authorized": False, + }, "summary": { "source_count": len(source_statuses), "loaded_source_count": loaded_source_count, diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 9df38d051..7b946e406 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -209,6 +209,48 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): def _assert_delivery_workbench_shape(data: dict): assert data["schema_version"] == "delivery_closure_workbench_v1" + assert data["safe_next_step"] == ( + "timer_and_service_data_readback_green_wait_for_next_all_host_reboot_event_" + "or_approved_reboot_drill_to_prove_10_minute_slo" + ) + assert data["active_blockers"] == [ + "host_boot_observation_older_than_target_window" + ] + assert data["readback"]["workbench_status"] == "blocked_delivery_actions_required" + assert data["readback"]["current_p0_workplan_id"] == "P0-006" + assert data["readback"]["current_p0_source_id"] == ( + "reboot_auto_recovery_slo_scorecard" + ) + assert data["readback"]["current_p0_status"] == ( + "blocked_reboot_auto_recovery_slo_not_ready" + ) + 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"] == 82 + assert data["readback"]["closed_p0_workplan_ids"] == ["P0-003", "P0-005"] + assert data["readback"]["github_lane_status"] == "stopped_retired_do_not_use" + assert data["readback"]["secret_value_collection_allowed"] is False + assert data["readback"]["workflow_trigger_authorized"] is False + assert data["readback"]["runtime_write_authorized"] is False + assert data["rollups"]["source_count"] == 8 + assert data["rollups"]["loaded_source_count"] == 8 + assert data["rollups"]["high_risk_blocker_count"] == data["summary"][ + "high_risk_blocker_count" + ] + assert data["rollups"]["current_p0_blocker_count"] == 1 + assert data["rollups"]["current_p0_readiness_percent"] == 82 + assert data["rollups"]["current_p0_blocked_by_fresh_reboot_window_only"] is True + assert data["rollups"]["current_p0_service_green"] is True + assert data["rollups"]["current_p0_product_data_green"] is True + assert data["rollups"]["current_p0_backup_core_green"] is True + assert data["rollups"]["current_p0_stockplatform_freshness_status"] == "ok" + assert data["rollups"]["current_p0_stockplatform_ingestion_status"] == "ok" + assert data["rollups"]["gitea_private_inventory_blocker_count"] == 0 + assert data["rollups"]["credential_escrow_blocker_count"] == 0 + assert data["rollups"]["production_deploy_hard_blocker_count"] == 0 + assert data["rollups"]["secret_values_collected"] is False + assert data["rollups"]["workflow_trigger_authorized"] is False + assert data["rollups"]["runtime_write_authorized"] is False assert data["summary"]["source_count"] == 8 assert data["summary"]["loaded_source_count"] == 8 assert data["summary"]["runtime_execution_authorized"] is False diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 408ca406b..4c9582922 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -2691,6 +2691,42 @@ export interface DeliveryClosureWorkbenchSnapshot { schema_version: 'delivery_closure_workbench_v1' generated_at: string status: 'blocked_delivery_actions_required' | 'ready' | string + safe_next_step: string + active_blockers: string[] + readback: { + workbench_status: string + current_p0_workplan_id: string + current_p0_source_id: string + current_p0_status: string + current_p0_safe_next_step: string + current_p0_active_blockers: string[] + current_p0_readiness_percent: number + closed_p0_workplan_ids: string[] + github_lane_status: string + secret_value_collection_allowed: boolean + workflow_trigger_authorized: boolean + runtime_write_authorized: boolean + } + rollups: { + source_count: number + loaded_source_count: number + average_completion_percent: number + high_risk_blocker_count: number + current_p0_blocker_count: number + current_p0_readiness_percent: number + current_p0_blocked_by_fresh_reboot_window_only: boolean + current_p0_service_green: boolean + current_p0_product_data_green: boolean + current_p0_backup_core_green: boolean + current_p0_stockplatform_freshness_status: string + current_p0_stockplatform_ingestion_status: string + gitea_private_inventory_blocker_count: number + credential_escrow_blocker_count: number + production_deploy_hard_blocker_count: number + secret_values_collected: boolean + workflow_trigger_authorized: boolean + runtime_write_authorized: boolean + } summary: { source_count: number loaded_source_count: number diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index dcffc5209..1beea9071 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,12 @@ +## 2026-06-30 — 01:26 P0-006 Delivery Workbench mainline readback shortcut + +**照優先順序完成的實作**: +- P0-006 仍是 active P0;P0-005 credential escrow 與 P0-003 Gitea private inventory 維持 production closed,不重開。 +- Production Delivery Workbench 已在 `summary` / lane 內呈現 P0-006 truth,但頂層 `safe_next_step`、`readback`、`rollups`、`active_blockers` 缺席會讓泛用 MCP / RAG / KM / Playbook reader 讀成空值。 +- 本輪將目前 P0 的 workplan、status、safe next step、active blockers、readiness、closed P0 list 與 hard boundary flags 提升為 Delivery Workbench 頂層相容欄位;既有 `summary` / `lanes` contract 不變。 + +**邊界**:不使用 GitHub,不讀 secret / raw session / SQLite,不觸發 workflow,不重啟主機,不 restart service,不寫 DB / K8s / Docker / firewall。 + ## 2026-06-30 — 01:02 P0-006 drill preflight machine-readback shape **照優先順序完成的實作**: