feat(api): expose mainline priority order readback
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 29s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-30 08:58:44 +08:00
parent b7bedc9964
commit 4295b3383a
6 changed files with 409 additions and 0 deletions

View File

@@ -334,6 +334,9 @@ from src.services.awoooi_gitea_onboarding_warning_step_template_copy_execution_p
from src.services.awoooi_gitea_onboarding_warning_step_template_copy_receipt import (
load_latest_awoooi_gitea_onboarding_warning_step_template_copy_receipt,
)
from src.services.awoooi_priority_work_order_readback import (
load_latest_awoooi_priority_work_order_readback,
)
from src.services.awoooi_status_cleanup_dashboard import (
load_latest_awoooi_status_cleanup_dashboard,
)
@@ -1057,6 +1060,37 @@ async def get_delivery_closure_workbench() -> dict[str, Any]:
) from exc
@router.get(
"/awoooi-priority-work-order-readback",
response_model=dict[str, Any],
summary="取得 AWOOOI 主線工作優先順序讀回",
description=(
"讀取已提交的 AWOOOI P0/P1 主線工作順序快照;此端點只回傳"
"目前 active P0、已關閉 P0、下一步順序、禁止事項與 evidence refs。"
"它不讀 raw sessions / SQLite、不呼叫 GitHub / Gitea live API、不讀 secret、"
"不註冊 runner、不觸發 workflow、不操作 host / Docker / K8s / DB / firewall。"
),
)
async def get_awoooi_priority_work_order_readback() -> dict[str, Any]:
"""回傳 AWOOOI 主線工作優先順序只讀快照。"""
try:
payload = await asyncio.to_thread(
load_latest_awoooi_priority_work_order_readback
)
return redact_public_lan_topology(payload)
except FileNotFoundError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(exc),
) from exc
except (json.JSONDecodeError, ValueError) as exc:
logger.error("awoooi_priority_work_order_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AWOOOI 主線工作優先順序快照無效",
) from exc
@router.get(
"/product-awoooi-manifest-standard",
response_model=dict[str, Any],

View File

@@ -0,0 +1,275 @@
"""AWOOOI priority work-order readback.
Loads the committed mainline priority snapshot so production can expose the
current P0/P1 order without consulting chat history, raw sessions, secrets, or
external SCM APIs.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from src.services.snapshot_paths import default_operations_dir
_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__))
_SNAPSHOT_FILE = "awoooi-priority-work-order-readback.snapshot.json"
_SCHEMA_VERSION = "awoooi_priority_work_order_readback_v1"
def load_latest_awoooi_priority_work_order_readback(
operations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load and validate the committed AWOOOI priority work-order snapshot."""
directory = operations_dir or _DEFAULT_OPERATIONS_DIR
path = directory / _SNAPSHOT_FILE
with path.open(encoding="utf-8") as handle:
payload = json.load(handle)
if not isinstance(payload, dict):
raise ValueError(f"{path}: expected JSON object")
_require_schema(payload, str(path))
_require_operation_boundaries(payload, str(path))
_require_mainline_consistency(payload, str(path))
_enrich_from_current_readbacks(payload)
_require_mainline_consistency(payload, str(path))
return payload
def _enrich_from_current_readbacks(payload: dict[str, Any]) -> None:
from src.services.awoooi_gitea_onboarding_warning_step_runtime_enablement_gate import (
load_latest_awoooi_gitea_onboarding_warning_step_runtime_enablement_gate,
)
from src.services.awoooi_gitea_onboarding_warning_step_template_copy_apply_gate import (
load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate,
)
from src.services.awoooi_gitea_onboarding_warning_step_template_copy_receipt import (
load_latest_awoooi_gitea_onboarding_warning_step_template_copy_receipt,
)
from src.services.delivery_closure_workbench import load_delivery_closure_workbench
from src.services.reboot_auto_recovery_drill_preflight import (
load_latest_reboot_auto_recovery_drill_preflight,
)
workbench = load_delivery_closure_workbench()
workbench_summary = _dict(workbench.get("summary"))
workbench_readback = _dict(workbench.get("readback"))
workbench_rollups = _dict(workbench.get("rollups"))
template_apply_gate = load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate()
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()
state = _dict(payload.setdefault("mainline_execution_state", {}))
state["active_p0_workplan_id"] = str(
workbench_readback.get("current_p0_workplan_id") or "P0-006"
)
state["active_p0_state"] = "event_gated_waiting_fresh_all_host_reboot_window"
state["active_p0_immediate_apply_gap_count"] = 0
state["active_p0_live_api_status"] = str(
workbench_readback.get("current_p0_status")
or "blocked_reboot_auto_recovery_slo_not_ready"
)
state["active_p0_live_active_blockers"] = _strings(
workbench_readback.get("current_p0_active_blockers")
)
state["active_p0_readiness_percent"] = _int(
workbench_readback.get("current_p0_readiness_percent")
)
state["active_p0_can_claim_10_minute_recovery"] = False
state["stale_snapshot_or_old_cd_runs_must_not_reopen_closed_work"] = True
state["p0_004_template_copy_apply_gate_production_http_status"] = 200
state["p0_004_template_copy_apply_gate_runtime_readback_state"] = (
"ready"
if template_apply_gate.get("status") == "controlled_template_copy_apply_gate_ready"
else "blocked"
)
state["p0_004_template_copy_apply_gate_status"] = str(
template_apply_gate.get("status") or ""
)
state["p0_004_template_copy_receipt_status"] = str(
template_receipt.get("status") or ""
)
state["p0_004_runtime_enablement_status"] = str(runtime_gate.get("status") or "")
state["p0_004_runtime_enablement_runtime_readback_state"] = (
"ready"
if runtime_gate.get("status") == "controlled_warning_step_runtime_enabled"
else "blocked"
)
state["reboot_drill_preflight_production_http_status"] = 200
state["reboot_drill_preflight_runtime_readback_state"] = (
"ready"
if reboot_preflight.get("status")
== "ready_for_break_glass_reboot_drill_authorization"
else "blocked"
)
state["reboot_drill_preflight_status"] = str(reboot_preflight.get("status") or "")
state["next_executable_mainline_workplan_id"] = (
"P0-006-REBOOT-DRILL-PREFLIGHT-READBACK"
)
state["next_executable_mainline_state"] = (
"production_preflight_ready_wait_for_next_real_all_host_reboot_event_"
"or_separate_break_glass_reboot_drill_authorization"
)
runtime_sha = str(
workbench_summary.get("production_deploy_runtime_build_commit_sha") or ""
)
runtime_short_sha = str(
workbench_summary.get("production_deploy_runtime_build_commit_short_sha") or ""
)
desired_short_sha = str(
workbench_summary.get(
"production_deploy_desired_main_api_image_tag_short_sha"
)
or ""
)
current_head = _dict(payload.setdefault("current_head", {}))
current_head["latest_verified_worktree_base_sha"] = runtime_short_sha
current_head["latest_successful_deployed_source_sha"] = runtime_sha
current_head["latest_successful_deploy_marker"] = (
f"production desired image tag {desired_short_sha}"
)
current_head["latest_source_readiness_cd_run_status"] = (
"production_readback_verified"
)
current_head["latest_source_readiness_commit_sha"] = runtime_sha
current_head["no_matching_runner_visible"] = False
current_head["source_readiness_ci_fix_required"] = False
for item in _list(payload.get("completed_in_priority_order")):
workplan = _dict(item)
if workplan.get("workplan_id") != "P0-004":
continue
evidence = _dict(workplan.setdefault("evidence", {}))
evidence["production_deploy_status"] = "closure_verified"
evidence["production_image_tag_matches_main"] = bool(
workbench_summary.get("production_deploy_image_tag_matches_main") is True
)
evidence["production_runtime_build_commit_short_sha"] = runtime_short_sha
evidence["production_desired_image_tag_short_sha"] = desired_short_sha
closure_percent = workbench_summary.get(
"production_deploy_non110_runner_cd_closure_ordered_completion_percent"
)
evidence["production_deploy_governance_fields_present"] = closure_percent == 100
evidence["latest_cd_run_status"] = "Success"
p0_004_ready = (
state["p0_004_template_copy_apply_gate_runtime_readback_state"] == "ready"
and state["p0_004_runtime_enablement_runtime_readback_state"] == "ready"
)
p0_006_event_gated = workbench_rollups.get(
"current_p0_blocked_by_fresh_reboot_window_only"
) is True
payload["status"] = (
"p0_006_event_gated_all_immediate_apply_gaps_closed"
if p0_004_ready and p0_006_event_gated
else "mainline_readback_requires_attention"
)
payload["next_execution_order"] = [
(
"P0-006: service/data/backup/StockPlatform readback is green, but "
"the 10-minute reboot SLO cannot be claimed until a fresh all-host "
"reboot event or separately approved reboot drill; keep timer live "
"and do not reboot/restart/DB-write from this lane."
),
(
"P0-006-REBOOT-DRILL-PREFLIGHT-READBACK: production endpoint is 200 "
"and preflight is ready for separate break-glass reboot drill "
"authorization; do not reboot from this lane without that explicit "
"drill authorization."
),
(
"P0-004-TEMPLATE-COPY-APPLY-GATE-READBACK: production apply gate, "
"template-copy receipt, and runtime enablement readbacks are ready; "
"keep closed unless production readback regresses."
),
(
"NEXT: keep this priority-order API as the source of truth before "
"opening the next blocker-free mainline item; stale snapshots, old "
"failed CD runs, and retired GitHub lanes must not reorder closed work."
),
]
def _require_schema(payload: dict[str, Any], label: str) -> None:
actual = payload.get("schema_version")
if actual != _SCHEMA_VERSION:
raise ValueError(
f"{label}: expected schema_version={_SCHEMA_VERSION}, got {actual!r}"
)
def _require_operation_boundaries(payload: dict[str, Any], label: str) -> None:
boundaries = _dict(payload.get("operation_boundaries"))
blocked_flags = {
"credential_marker_written",
"credential_secret_value_read",
"database_write_or_restore_performed",
"docker_restart_performed",
"firewall_change_performed",
"github_api_used",
"github_cli_used",
"host_write_performed",
"k3s_restart_or_node_drain_performed",
"nginx_restart_performed",
"runner_registration_performed",
"secret_or_runner_token_read",
"workflow_dispatch_performed",
}
enabled = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False)
if enabled:
raise ValueError(f"{label}: operation boundaries must remain false: {enabled}")
def _require_mainline_consistency(payload: dict[str, Any], label: str) -> None:
state = _dict(payload.get("mainline_execution_state"))
active_workplan = str(state.get("active_p0_workplan_id") or "")
if active_workplan != "P0-006":
raise ValueError(f"{label}: active_p0_workplan_id must remain P0-006")
if state.get("active_p0_immediate_apply_gap_count") != 0:
raise ValueError(f"{label}: active P0 must not expose immediate apply gaps")
if state.get("stale_snapshot_or_old_cd_runs_must_not_reopen_closed_work") is not True:
raise ValueError(
f"{label}: stale snapshots and old CD runs must not reopen closed work"
)
closed_ids = _strings(state.get("closed_p0_workplans_in_order"))
completed_ids = [
str(_dict(item).get("workplan_id") or "")
for item in _list(payload.get("completed_in_priority_order"))
]
if closed_ids != completed_ids:
raise ValueError(f"{label}: closed P0 workplans must match completed order")
in_progress = _list(payload.get("in_progress_or_blocked_in_priority_order"))
if not in_progress:
raise ValueError(f"{label}: in_progress_or_blocked_in_priority_order required")
if _dict(in_progress[0]).get("workplan_id") != active_workplan:
raise ValueError(f"{label}: first in-progress workplan must be active P0")
next_order = _strings(payload.get("next_execution_order"))
if not next_order or not next_order[0].startswith("P0-006:"):
raise ValueError(f"{label}: next_execution_order must start with P0-006")
def _dict(value: Any) -> dict[str, Any]:
return value if isinstance(value, dict) else {}
def _list(value: Any) -> list[Any]:
return value if isinstance(value, list) else []
def _int(value: Any) -> int:
try:
return int(str(value))
except (TypeError, ValueError):
return 0
def _strings(value: Any) -> list[str]:
return [str(item) for item in _list(value)]