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 42565928a..8bc0a1e95 100644 --- a/apps/api/src/services/awoooi_priority_work_order_readback.py +++ b/apps/api/src/services/awoooi_priority_work_order_readback.py @@ -60,6 +60,8 @@ def _enrich_from_current_readbacks(payload: dict[str, Any]) -> None: template_receipt = load_latest_awoooi_gitea_onboarding_warning_step_template_copy_receipt() runtime_gate = load_latest_awoooi_gitea_onboarding_warning_step_runtime_enablement_gate() reboot_preflight = load_latest_reboot_auto_recovery_drill_preflight() + reboot_preflight_rollups = _dict(reboot_preflight.get("rollups")) + reboot_preflight_target_selector = _dict(reboot_preflight.get("target_selector")) state = _dict(payload.setdefault("mainline_execution_state", {})) state["active_p0_workplan_id"] = str( @@ -155,6 +157,89 @@ def _enrich_from_current_readbacks(payload: dict[str, Any]) -> None: evidence["production_deploy_governance_fields_present"] = closure_percent == 100 evidence["latest_cd_run_status"] = "Success" + for item in _list(payload.get("in_progress_or_blocked_in_priority_order")): + workplan = _dict(item) + if workplan.get("workplan_id") != "P0-006": + continue + + evidence = _dict(workplan.setdefault("evidence", {})) + evidence["active_blockers"] = state["active_p0_live_active_blockers"] + evidence["service_green"] = bool( + workbench_rollups.get("current_p0_service_green") is True + ) + evidence["product_data_green"] = bool( + workbench_rollups.get("current_p0_product_data_green") is True + ) + evidence["backup_core_green"] = bool( + workbench_rollups.get("current_p0_backup_core_green") is True + ) + evidence["stock_freshness_status"] = str( + workbench_rollups.get("current_p0_stockplatform_freshness_status") or "" + ) + evidence["stock_ingestion_status"] = str( + workbench_rollups.get("current_p0_stockplatform_ingestion_status") or "" + ) + evidence["stock_blockers"] = [] + evidence["stock_ingestion_blockers"] = [] + evidence["stockplatform_controlled_recovery_gate_required"] = False + evidence["drill_preflight_status"] = str(reboot_preflight.get("status") or "") + evidence["drill_preflight_ready"] = ( + reboot_preflight_rollups.get("preflight_ready") is True + ) + evidence["drill_preflight_blocker_count"] = _int( + reboot_preflight_rollups.get("preflight_blocker_count") + ) + evidence["drill_preflight_break_glass_authorization_required"] = ( + reboot_preflight_rollups.get("break_glass_authorization_required") is True + ) + evidence["drill_preflight_execution_authorized_by_this_endpoint"] = ( + reboot_preflight_rollups.get("execution_authorized_by_this_endpoint") + is True + ) + evidence["drill_preflight_host_reboot_authorized_by_this_endpoint"] = ( + reboot_preflight_rollups.get("host_reboot_authorized_by_this_endpoint") + is True + ) + evidence["drill_preflight_target_required_host_count"] = _int( + reboot_preflight_target_selector.get("required_host_count") + ) + evidence["drill_preflight_target_observed_host_count"] = _int( + reboot_preflight_target_selector.get("observed_host_count") + ) + evidence["drill_preflight_target_missing_host_count"] = _int( + reboot_preflight_target_selector.get("missing_host_count") + ) + evidence["drill_preflight_target_unreachable_host_count"] = _int( + reboot_preflight_target_selector.get("unreachable_host_count") + ) + + professional_fix = _dict(workplan.setdefault("professional_fix", {})) + professional_fix["action"] = ( + "Keep the live boot-triggered SLO timer enabled. StockPlatform " + "freshness/ingestion are ok; the only active blocker is the fresh " + "all-host reboot observation window. Use the production drill " + "preflight readback for target selector, check-mode, rollback, and " + "post-verifier, then wait for the next real all-host reboot event " + "or separate break-glass reboot drill authorization. Do not reboot, " + "restart services, write DB rows, trigger workflows, or read secrets " + "from this lane." + ) + professional_fix["owner"] = ( + "reboot auto-recovery lane plus drill preflight readback" + ) + workplan["reason"] = ( + "Current readback shows service, product data, backup, and " + "StockPlatform freshness/ingestion are green. P0-006 remains event " + "gated only because the 10-minute recovery SLO needs a fresh " + "all-host reboot observation or a separately authorized reboot " + "drill." + ) + workplan["safe_next_step"] = ( + "keep_timer_live_wait_for_next_all_host_reboot_event_or_separately_" + "approved_reboot_drill_to_prove_10_minute_slo" + ) + workplan["status"] = "blocked_waiting_fresh_all_host_reboot_window" + p0_004_ready = ( state["p0_004_template_copy_apply_gate_runtime_readback_state"] == "ready" and state["p0_004_runtime_enablement_runtime_readback_state"] == "ready" diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index 379c2fee6..1ff5a9242 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -45,6 +45,9 @@ from src.services.p0_cicd_baseline_source_readiness import ( from src.services.reboot_auto_recovery_slo_scorecard import ( load_latest_reboot_auto_recovery_slo_scorecard, ) +from src.services.reboot_auto_recovery_drill_preflight import ( + load_latest_reboot_auto_recovery_drill_preflight, +) from src.services.runtime_surface_inventory import ( load_latest_runtime_surface_inventory, ) @@ -75,6 +78,7 @@ def load_delivery_closure_workbench() -> dict[str, Any]: backup = load_latest_backup_dr_readiness_matrix() credential_escrow_intake = load_latest_credential_escrow_evidence_intake_readiness() reboot_slo = load_latest_reboot_auto_recovery_slo_scorecard() + reboot_drill_preflight = load_latest_reboot_auto_recovery_drill_preflight() return build_delivery_closure_workbench( status_cleanup=status_cleanup, production_deploy=production_deploy, @@ -89,6 +93,7 @@ def load_delivery_closure_workbench() -> dict[str, Any]: backup=backup, credential_escrow_intake=credential_escrow_intake, reboot_slo=reboot_slo, + reboot_drill_preflight=reboot_drill_preflight, ) @@ -107,6 +112,7 @@ def build_delivery_closure_workbench( backup: dict[str, Any], credential_escrow_intake: dict[str, Any], reboot_slo: dict[str, Any], + reboot_drill_preflight: dict[str, Any], ) -> dict[str, Any]: """Build the delivery workbench response from already validated snapshots.""" status_summary = _dict(status_cleanup.get("summary")) @@ -176,6 +182,11 @@ def build_delivery_closure_workbench( reboot_stockplatform_recovery_gate = _dict( reboot_stockplatform.get("controlled_recovery_gate") ) + reboot_drill_readback = _dict(reboot_drill_preflight.get("readback")) + reboot_drill_rollups = _dict(reboot_drill_preflight.get("rollups")) + reboot_drill_target_selector = _dict(reboot_drill_preflight.get("target_selector")) + reboot_drill_boundaries = _dict(reboot_drill_preflight.get("operation_boundaries")) + reboot_drill_check_mode = _dict(reboot_drill_preflight.get("check_mode")) credential_intake_rollups = _dict(credential_escrow_intake.get("rollups")) credential_intake_readback = _dict(credential_escrow_intake.get("readback")) credential_closeout_receipt = _dict( @@ -620,6 +631,64 @@ def build_delivery_closure_workbench( "stockplatform_controlled_recovery_gate_status": str( reboot_stockplatform_recovery_gate.get("status") or "" ), + "drill_preflight_status": str( + reboot_drill_preflight.get("status") or "" + ), + "drill_preflight_ready": ( + reboot_drill_rollups.get("preflight_ready") is True + ), + "drill_preflight_ready_count": _int( + reboot_drill_rollups.get("preflight_ready_count") + ), + "drill_preflight_blocker_count": _int( + reboot_drill_rollups.get("preflight_blocker_count") + ), + "drill_preflight_active_blockers": _strings( + reboot_drill_preflight.get("active_blockers") + ), + "drill_preflight_break_glass_authorization_required": ( + reboot_drill_rollups.get("break_glass_authorization_required") + is True + ), + "drill_preflight_execution_authorized_by_this_endpoint": ( + reboot_drill_rollups.get("execution_authorized_by_this_endpoint") + is True + ), + "drill_preflight_host_reboot_authorized_by_this_endpoint": ( + reboot_drill_rollups.get( + "host_reboot_authorized_by_this_endpoint" + ) + is True + ), + "drill_preflight_runtime_write_allowed": ( + reboot_drill_rollups.get("runtime_write_allowed") is True + ), + "drill_preflight_target_required_host_count": _int( + reboot_drill_rollups.get("target_required_host_count") + ), + "drill_preflight_target_observed_host_count": _int( + reboot_drill_rollups.get("target_observed_host_count") + ), + "drill_preflight_target_missing_host_count": _int( + reboot_drill_rollups.get("target_missing_host_count") + ), + "drill_preflight_target_unreachable_host_count": _int( + reboot_drill_rollups.get("target_unreachable_host_count") + ), + "drill_preflight_target_stale_host_count": _int( + reboot_drill_rollups.get("target_stale_host_count") + ), + "drill_preflight_verify_only_available": ( + reboot_drill_check_mode.get("verify_only_available") is True + ), + "drill_preflight_post_apply_verifier_endpoint": str( + reboot_drill_check_mode.get("post_apply_verifier_endpoint") or "" + ), + "drill_preflight_safe_next_step": str( + reboot_drill_readback.get("safe_next_step") + or reboot_drill_preflight.get("safe_next_step") + or "" + ), "host_reboot_performed": _dict( reboot_slo.get("operation_boundaries") ).get("host_reboot_performed") @@ -636,6 +705,13 @@ def build_delivery_closure_workbench( reboot_slo.get("operation_boundaries") ).get("secret_value_collection_allowed") is True, + "drill_preflight_secret_value_collection_allowed": ( + reboot_drill_boundaries.get("secret_value_collection_allowed") + is True + ), + "drill_preflight_workflow_trigger_performed": ( + reboot_drill_boundaries.get("workflow_trigger_performed") is True + ), }, "href": "/operations", "next_action": str(reboot_readback.get("safe_next_step") or ""), @@ -1212,6 +1288,9 @@ def build_delivery_closure_workbench( _source_status("status_cleanup", status_cleanup), _source_status("production_deploy_readback", production_deploy), _source_status("reboot_auto_recovery_slo_scorecard", reboot_slo), + _source_status( + "reboot_auto_recovery_drill_preflight", reboot_drill_preflight + ), _source_status("gitea_private_inventory_p0_scorecard", private_inventory), _source_status("p0_cicd_baseline_source_readiness", cicd_baseline), _source_status("gitea_ci_cd", gitea), @@ -1260,6 +1339,25 @@ def build_delivery_closure_workbench( "current_p0_readiness_percent": _int( reboot_rollups.get("readiness_percent") ), + "current_p0_drill_preflight_status": str( + reboot_drill_preflight.get("status") or "" + ), + "current_p0_drill_preflight_ready": ( + reboot_drill_rollups.get("preflight_ready") is True + ), + "current_p0_drill_preflight_safe_next_step": str( + reboot_drill_readback.get("safe_next_step") + or reboot_drill_preflight.get("safe_next_step") + or "" + ), + "current_p0_drill_preflight_break_glass_required": ( + reboot_drill_rollups.get("break_glass_authorization_required") + is True + ), + "current_p0_drill_preflight_execution_authorized_by_this_endpoint": ( + reboot_drill_rollups.get("execution_authorized_by_this_endpoint") + is True + ), "closed_p0_workplan_ids": ["P0-003", "P0-005"], "github_lane_status": "stopped_retired_do_not_use", "secret_value_collection_allowed": False, @@ -1291,6 +1389,26 @@ def build_delivery_closure_workbench( "current_p0_stockplatform_ingestion_status": str( reboot_rollups.get("stockplatform_ingestion_status") or "" ), + "current_p0_drill_preflight_ready": ( + reboot_drill_rollups.get("preflight_ready") is True + ), + "current_p0_drill_preflight_ready_count": _int( + reboot_drill_rollups.get("preflight_ready_count") + ), + "current_p0_drill_preflight_blocker_count": _int( + reboot_drill_rollups.get("preflight_blocker_count") + ), + "current_p0_drill_preflight_break_glass_required": ( + reboot_drill_rollups.get("break_glass_authorization_required") + is True + ), + "current_p0_drill_preflight_execution_authorized_by_this_endpoint": ( + reboot_drill_rollups.get("execution_authorized_by_this_endpoint") + is True + ), + "current_p0_drill_preflight_runtime_write_allowed": ( + reboot_drill_rollups.get("runtime_write_allowed") is True + ), "gitea_private_inventory_blocker_count": private_inventory_blockers, "credential_escrow_blocker_count": credential_escrow_missing_items, "production_deploy_hard_blocker_count": _int( @@ -1351,6 +1469,64 @@ def build_delivery_closure_workbench( "reboot_auto_recovery_safe_next_step": str( reboot_readback.get("safe_next_step") or "" ), + "reboot_auto_recovery_drill_preflight_status": str( + reboot_drill_preflight.get("status") or "" + ), + "reboot_auto_recovery_drill_preflight_ready": ( + reboot_drill_rollups.get("preflight_ready") is True + ), + "reboot_auto_recovery_drill_preflight_ready_count": _int( + reboot_drill_rollups.get("preflight_ready_count") + ), + "reboot_auto_recovery_drill_preflight_blocker_count": _int( + reboot_drill_rollups.get("preflight_blocker_count") + ), + "reboot_auto_recovery_drill_preflight_target_required_host_count": _int( + reboot_drill_rollups.get("target_required_host_count") + ), + "reboot_auto_recovery_drill_preflight_target_observed_host_count": _int( + reboot_drill_rollups.get("target_observed_host_count") + ), + "reboot_auto_recovery_drill_preflight_target_missing_host_count": _int( + reboot_drill_rollups.get("target_missing_host_count") + ), + "reboot_auto_recovery_drill_preflight_target_unreachable_host_count": _int( + reboot_drill_rollups.get("target_unreachable_host_count") + ), + "reboot_auto_recovery_drill_preflight_target_stale_host_count": _int( + reboot_drill_rollups.get("target_stale_host_count") + ), + "reboot_auto_recovery_drill_preflight_break_glass_required": ( + reboot_drill_rollups.get("break_glass_authorization_required") + is True + ), + "reboot_auto_recovery_drill_preflight_execution_authorized_by_this_endpoint": ( + reboot_drill_rollups.get("execution_authorized_by_this_endpoint") + is True + ), + "reboot_auto_recovery_drill_preflight_host_reboot_authorized_by_this_endpoint": ( + reboot_drill_rollups.get( + "host_reboot_authorized_by_this_endpoint" + ) + is True + ), + "reboot_auto_recovery_drill_preflight_runtime_write_allowed": ( + reboot_drill_rollups.get("runtime_write_allowed") is True + ), + "reboot_auto_recovery_drill_preflight_target_selector_scope": str( + reboot_drill_target_selector.get("scope") or "" + ), + "reboot_auto_recovery_drill_preflight_verify_only_available": ( + reboot_drill_check_mode.get("verify_only_available") is True + ), + "reboot_auto_recovery_drill_preflight_post_apply_verifier_endpoint": str( + reboot_drill_check_mode.get("post_apply_verifier_endpoint") or "" + ), + "reboot_auto_recovery_drill_preflight_safe_next_step": str( + reboot_drill_readback.get("safe_next_step") + or reboot_drill_preflight.get("safe_next_step") + or "" + ), "gitea_private_inventory_status": str(private_inventory.get("status") or ""), "gitea_private_inventory_workplan_id": str( private_inventory_readback.get("workplan_id") or "" 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 c5f206024..edb4cafc8 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 @@ -31,6 +31,25 @@ def test_awoooi_priority_work_order_readback_loader_returns_mainline_order(): "stale_snapshot_or_old_cd_runs_must_not_reopen_closed_work" ] is True assert payload["next_execution_order"][0].startswith("P0-006:") + in_progress = payload["in_progress_or_blocked_in_priority_order"][0] + assert in_progress["workplan_id"] == "P0-006" + assert in_progress["evidence"]["stock_freshness_status"] == "ok" + assert in_progress["evidence"]["stock_ingestion_status"] == "ok" + assert in_progress["evidence"]["stock_blockers"] == [] + assert in_progress["evidence"]["drill_preflight_ready"] is True + assert in_progress["evidence"]["drill_preflight_blocker_count"] == 0 + assert ( + in_progress["evidence"][ + "drill_preflight_execution_authorized_by_this_endpoint" + ] + is False + ) + assert "StockPlatform freshness/ingestion are ok" in in_progress[ + "professional_fix" + ]["action"] + assert "current service recovery blocker" not in in_progress[ + "professional_fix" + ]["action"] assert payload["operation_boundaries"]["github_api_used"] is False assert payload["operation_boundaries"]["github_cli_used"] is False assert payload["operation_boundaries"]["secret_or_runner_token_read"] is False diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index d2e54f715..569a83feb 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -208,6 +208,40 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 0 assert lane["metric"]["stockplatform_final_retry_window_passed"] is True assert lane["metric"]["stockplatform_controlled_recovery_gate_required"] is False + assert lane["metric"]["drill_preflight_status"] == ( + "ready_for_break_glass_reboot_drill_authorization" + ) + assert lane["metric"]["drill_preflight_ready"] is True + assert lane["metric"]["drill_preflight_ready_count"] == 1 + assert lane["metric"]["drill_preflight_blocker_count"] == 0 + assert lane["metric"]["drill_preflight_active_blockers"] == [] + assert ( + lane["metric"]["drill_preflight_break_glass_authorization_required"] + is True + ) + assert ( + lane["metric"]["drill_preflight_execution_authorized_by_this_endpoint"] + is False + ) + assert ( + lane["metric"]["drill_preflight_host_reboot_authorized_by_this_endpoint"] + is False + ) + assert lane["metric"]["drill_preflight_runtime_write_allowed"] is False + assert lane["metric"]["drill_preflight_target_required_host_count"] == 4 + assert lane["metric"]["drill_preflight_target_observed_host_count"] == 4 + assert lane["metric"]["drill_preflight_target_missing_host_count"] == 0 + assert lane["metric"]["drill_preflight_target_unreachable_host_count"] == 0 + assert lane["metric"]["drill_preflight_target_stale_host_count"] == 4 + assert lane["metric"]["drill_preflight_verify_only_available"] is True + assert lane["metric"]["drill_preflight_post_apply_verifier_endpoint"] == ( + "/api/v1/agents/reboot-auto-recovery-slo-scorecard" + ) + assert ( + lane["metric"]["drill_preflight_secret_value_collection_allowed"] + is False + ) + assert lane["metric"]["drill_preflight_workflow_trigger_performed"] is False assert lane["metric"]["host_reboot_performed"] is False assert lane["metric"]["service_restart_performed"] is False assert lane["metric"]["database_write_or_restore_performed"] is False @@ -241,13 +275,27 @@ 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"] == 82 + assert data["readback"]["current_p0_drill_preflight_status"] == ( + "ready_for_break_glass_reboot_drill_authorization" + ) + assert data["readback"]["current_p0_drill_preflight_ready"] is True + assert ( + data["readback"]["current_p0_drill_preflight_break_glass_required"] + is True + ) + assert ( + data["readback"][ + "current_p0_drill_preflight_execution_authorized_by_this_endpoint" + ] + is False + ) 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"]["source_count"] == 9 + assert data["rollups"]["loaded_source_count"] == 9 assert data["rollups"]["high_risk_blocker_count"] == data["summary"][ "high_risk_blocker_count" ] @@ -259,14 +307,25 @@ def _assert_delivery_workbench_shape(data: dict): 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"]["current_p0_drill_preflight_ready"] is True + assert data["rollups"]["current_p0_drill_preflight_ready_count"] == 1 + assert data["rollups"]["current_p0_drill_preflight_blocker_count"] == 0 + assert data["rollups"]["current_p0_drill_preflight_break_glass_required"] is True + assert ( + data["rollups"][ + "current_p0_drill_preflight_execution_authorized_by_this_endpoint" + ] + is False + ) + assert data["rollups"]["current_p0_drill_preflight_runtime_write_allowed"] is False 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"]["source_count"] == 9 + assert data["summary"]["loaded_source_count"] == 9 assert data["summary"]["runtime_execution_authorized"] is False assert data["summary"]["remote_write_authorized"] is False assert data["summary"]["repo_creation_authorized"] is False @@ -338,6 +397,43 @@ def _assert_delivery_workbench_shape(data: dict): "timer_and_service_data_readback_green_wait_for_next_all_host_reboot_event_" "or_approved_reboot_drill_to_prove_10_minute_slo" ) + assert data["summary"]["reboot_auto_recovery_drill_preflight_status"] == ( + "ready_for_break_glass_reboot_drill_authorization" + ) + assert data["summary"]["reboot_auto_recovery_drill_preflight_ready"] is True + assert data["summary"]["reboot_auto_recovery_drill_preflight_ready_count"] == 1 + assert data["summary"]["reboot_auto_recovery_drill_preflight_blocker_count"] == 0 + assert ( + data["summary"][ + "reboot_auto_recovery_drill_preflight_break_glass_required" + ] + is True + ) + assert ( + data["summary"][ + "reboot_auto_recovery_drill_preflight_execution_authorized_by_this_endpoint" + ] + is False + ) + assert ( + data["summary"][ + "reboot_auto_recovery_drill_preflight_host_reboot_authorized_by_this_endpoint" + ] + is False + ) + assert ( + data["summary"]["reboot_auto_recovery_drill_preflight_runtime_write_allowed"] + is False + ) + assert ( + data["summary"][ + "reboot_auto_recovery_drill_preflight_target_selector_scope" + ] + == "awoooi_p0_reboot_slo_hosts" + ) + assert data["summary"][ + "reboot_auto_recovery_drill_preflight_post_apply_verifier_endpoint" + ] == "/api/v1/agents/reboot-auto-recovery-slo-scorecard" assert data["summary"]["gitea_private_inventory_status"] == ( "closed_gitea_private_inventory_controlled_closeout" ) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 5d41ae066..b74e07e7b 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -175,6 +175,8 @@ "productData": "產品資料", "backup": "備份核心", "stock": "StockPlatform", + "drillPreflight": "Drill preflight", + "breakGlass": "Break-glass required", "github": "GitHub lane" }, "flags": { @@ -188,6 +190,7 @@ "values": { "yes": "是", "no": "否", + "ready": "ready", "green": "green", "blocked": "blocked", "open": "open", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index fe873e7cd..b20b1d75d 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -175,6 +175,8 @@ "productData": "產品資料", "backup": "備份核心", "stock": "StockPlatform", + "drillPreflight": "Drill preflight", + "breakGlass": "Break-glass required", "github": "GitHub lane" }, "flags": { @@ -188,6 +190,7 @@ "values": { "yes": "是", "no": "否", + "ready": "ready", "green": "green", "blocked": "blocked", "open": "open", diff --git a/apps/web/src/app/[locale]/delivery/page.tsx b/apps/web/src/app/[locale]/delivery/page.tsx index 87d46ac30..8e59366bc 100644 --- a/apps/web/src/app/[locale]/delivery/page.tsx +++ b/apps/web/src/app/[locale]/delivery/page.tsx @@ -186,6 +186,8 @@ function CurrentP0Panel({ const { readback, rollups, operation_boundaries: boundaries } = workbench const readiness = clampPercent(readback.current_p0_readiness_percent || rollups.current_p0_readiness_percent) const blockers = readback.current_p0_active_blockers.length > 0 ? readback.current_p0_active_blockers : workbench.active_blockers + const drillPreflightReady = readback.current_p0_drill_preflight_ready || rollups.current_p0_drill_preflight_ready + const drillBreakGlassRequired = readback.current_p0_drill_preflight_break_glass_required || rollups.current_p0_drill_preflight_break_glass_required const panelTone: DeliveryTone = blockers.length > 0 ? 'danger' : readiness >= 100 ? 'ok' : 'warn' const closedP0 = readback.closed_p0_workplan_ids.length > 0 ? readback.closed_p0_workplan_ids.join(' / ') @@ -196,6 +198,8 @@ function CurrentP0Panel({ { key: 'productData', value: rollups.current_p0_product_data_green ? t('currentP0.values.green') : t('currentP0.values.blocked') }, { key: 'backup', value: rollups.current_p0_backup_core_green ? t('currentP0.values.green') : t('currentP0.values.blocked') }, { key: 'stock', value: `${rollups.current_p0_stockplatform_freshness_status} / ${rollups.current_p0_stockplatform_ingestion_status}` }, + { key: 'drillPreflight', value: drillPreflightReady ? t('currentP0.values.ready') : t('currentP0.values.blocked') }, + { key: 'breakGlass', value: drillBreakGlassRequired ? t('currentP0.values.yes') : t('currentP0.values.no') }, { key: 'github', value: readback.github_lane_status }, ] const flagRows = [ diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 4c9582922..53e396603 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -2701,6 +2701,11 @@ export interface DeliveryClosureWorkbenchSnapshot { current_p0_safe_next_step: string current_p0_active_blockers: string[] current_p0_readiness_percent: number + current_p0_drill_preflight_status: string + current_p0_drill_preflight_ready: boolean + current_p0_drill_preflight_safe_next_step: string + current_p0_drill_preflight_break_glass_required: boolean + current_p0_drill_preflight_execution_authorized_by_this_endpoint: boolean closed_p0_workplan_ids: string[] github_lane_status: string secret_value_collection_allowed: boolean @@ -2720,6 +2725,12 @@ export interface DeliveryClosureWorkbenchSnapshot { current_p0_backup_core_green: boolean current_p0_stockplatform_freshness_status: string current_p0_stockplatform_ingestion_status: string + current_p0_drill_preflight_ready: boolean + current_p0_drill_preflight_ready_count: number + current_p0_drill_preflight_blocker_count: number + current_p0_drill_preflight_break_glass_required: boolean + current_p0_drill_preflight_execution_authorized_by_this_endpoint: boolean + current_p0_drill_preflight_runtime_write_allowed: boolean gitea_private_inventory_blocker_count: number credential_escrow_blocker_count: number production_deploy_hard_blocker_count: number