fix(reboot): expose runtime scorecard freshness
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 52s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 52s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -7,7 +7,9 @@ need to fan out across five separate endpoints or duplicate blocker math.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from src.services.awoooi_gitea_onboarding_warning_step_runtime_enablement_gate import (
|
||||
load_latest_awoooi_gitea_onboarding_warning_step_runtime_enablement_gate,
|
||||
@@ -1322,14 +1324,28 @@ def build_delivery_closure_workbench(
|
||||
_source_status("runtime_surface", runtime),
|
||||
_source_status("backup_dr", backup),
|
||||
]
|
||||
generated_candidates = [
|
||||
source_generated_candidates = [
|
||||
source["generated_at"] for source in source_statuses if source["generated_at"]
|
||||
]
|
||||
snapshot_generated_candidates = [
|
||||
source["snapshot_generated_at"]
|
||||
for source in source_statuses
|
||||
if source["snapshot_generated_at"]
|
||||
]
|
||||
loaded_source_count = sum(1 for source in source_statuses if source["loaded"])
|
||||
high_risk_blocker_count = sum(_int(lane["blocker_count"]) for lane in lanes)
|
||||
average_completion = _percent(
|
||||
sum(_int(lane["completion_percent"]) for lane in lanes) / max(len(lanes), 1)
|
||||
)
|
||||
source_generated_at = (
|
||||
max(source_generated_candidates) if source_generated_candidates else ""
|
||||
)
|
||||
snapshot_generated_at = (
|
||||
max(snapshot_generated_candidates)
|
||||
if snapshot_generated_candidates
|
||||
else source_generated_at
|
||||
)
|
||||
runtime_readback_generated_at = _taipei_now_iso()
|
||||
next_focus = [
|
||||
{
|
||||
"lane_id": lane["id"],
|
||||
@@ -1350,12 +1366,19 @@ def build_delivery_closure_workbench(
|
||||
|
||||
return {
|
||||
"schema_version": _SCHEMA_VERSION,
|
||||
"generated_at": max(generated_candidates) if generated_candidates else "",
|
||||
"generated_at": runtime_readback_generated_at,
|
||||
"runtime_readback_generated_at": runtime_readback_generated_at,
|
||||
"source_generated_at": source_generated_at,
|
||||
"snapshot_generated_at": snapshot_generated_at,
|
||||
"status": delivery_status,
|
||||
"safe_next_step": current_p0_safe_next_step,
|
||||
"active_blockers": current_p0_active_blockers,
|
||||
"readback": {
|
||||
"workbench_status": delivery_status,
|
||||
"generated_at": runtime_readback_generated_at,
|
||||
"runtime_readback_generated_at": runtime_readback_generated_at,
|
||||
"source_generated_at": source_generated_at,
|
||||
"snapshot_generated_at": snapshot_generated_at,
|
||||
"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 ""),
|
||||
@@ -1442,8 +1465,14 @@ def build_delivery_closure_workbench(
|
||||
"secret_values_collected": False,
|
||||
"workflow_trigger_authorized": False,
|
||||
"runtime_write_authorized": False,
|
||||
"runtime_readback_generated_at_present": True,
|
||||
"source_generated_at_present": bool(source_generated_at),
|
||||
},
|
||||
"summary": {
|
||||
"generated_at": runtime_readback_generated_at,
|
||||
"runtime_readback_generated_at": runtime_readback_generated_at,
|
||||
"source_generated_at": source_generated_at,
|
||||
"snapshot_generated_at": snapshot_generated_at,
|
||||
"source_count": len(source_statuses),
|
||||
"loaded_source_count": loaded_source_count,
|
||||
"average_completion_percent": average_completion,
|
||||
@@ -2266,6 +2295,10 @@ def _source_status(source_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"loaded": not source_missing,
|
||||
"schema_version": str(payload.get("schema_version") or ""),
|
||||
"generated_at": str(payload.get("generated_at") or ""),
|
||||
"runtime_readback_generated_at": str(
|
||||
payload.get("runtime_readback_generated_at") or ""
|
||||
),
|
||||
"snapshot_generated_at": str(payload.get("snapshot_generated_at") or ""),
|
||||
"missing_reason": str(payload.get("missing_reason") or "")
|
||||
if source_missing
|
||||
else "",
|
||||
@@ -2302,6 +2335,10 @@ def _strings(value: Any) -> list[str]:
|
||||
return [str(item) for item in value if item is not None]
|
||||
|
||||
|
||||
def _taipei_now_iso() -> str:
|
||||
return datetime.now(ZoneInfo("Asia/Taipei")).isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def _first_string(value: Any) -> str:
|
||||
if isinstance(value, list) and value:
|
||||
return str(value[0])
|
||||
|
||||
@@ -8,8 +8,10 @@ write StockPlatform data; it only reads the committed JSON scorecard.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from src.services.snapshot_paths import default_operations_dir
|
||||
|
||||
@@ -80,6 +82,8 @@ def _build_payload(scorecard: dict[str, Any], path: Path) -> dict[str, Any]:
|
||||
)
|
||||
if can_claim_slo and not active_blockers:
|
||||
readiness_percent = 100
|
||||
snapshot_generated_at = str(scorecard.get("generated_at") or "")
|
||||
runtime_readback_generated_at = _taipei_now_iso()
|
||||
latest_verify_metric = _dict(scorecard.get("latest_verify_only_metric"))
|
||||
active_blocker_count = len(active_blockers)
|
||||
observed_host_count = len(_strings(host_boot_detection.get("observed_hosts")))
|
||||
@@ -228,10 +232,13 @@ def _build_payload(scorecard: dict[str, Any], path: Path) -> dict[str, Any]:
|
||||
"windows99_rdp_console_reachable": (
|
||||
windows99_management.get("rdp_console_reachable") is True
|
||||
),
|
||||
"runtime_readback_generated_at_present": True,
|
||||
}
|
||||
return {
|
||||
"schema_version": _API_SCHEMA_VERSION,
|
||||
"generated_at": str(scorecard.get("generated_at") or ""),
|
||||
"generated_at": runtime_readback_generated_at,
|
||||
"runtime_readback_generated_at": runtime_readback_generated_at,
|
||||
"snapshot_generated_at": snapshot_generated_at,
|
||||
"priority": "P0-006",
|
||||
"scope": "reboot_auto_recovery_slo_scorecard",
|
||||
"status": str(scorecard.get("status") or "unknown"),
|
||||
@@ -277,6 +284,9 @@ def _build_payload(scorecard: dict[str, Any], path: Path) -> dict[str, Any]:
|
||||
"workplan_id": "P0-006",
|
||||
"workplan_title": "主機重啟自動偵測、自動觸發與 10 分鐘恢復 SLO",
|
||||
"source_scorecard_ref": f"docs/operations/{path.name}",
|
||||
"generated_at": runtime_readback_generated_at,
|
||||
"runtime_readback_generated_at": runtime_readback_generated_at,
|
||||
"snapshot_generated_at": snapshot_generated_at,
|
||||
"target_minutes": _int(scorecard.get("target_minutes")),
|
||||
"current_phase": sop_progress["current_phase"],
|
||||
"eta_or_wait_reason": sop_progress["eta_or_wait_reason"],
|
||||
@@ -734,3 +744,7 @@ def _strings(value: Any) -> list[str]:
|
||||
if not isinstance(value, list):
|
||||
return []
|
||||
return [str(item) for item in value if item is not None]
|
||||
|
||||
|
||||
def _taipei_now_iso() -> str:
|
||||
return datetime.now(ZoneInfo("Asia/Taipei")).isoformat(timespec="seconds")
|
||||
|
||||
Reference in New Issue
Block a user