Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m51s
CD Pipeline / build-and-deploy (push) Successful in 4m24s
CD Pipeline / post-deploy-checks (push) Failing after 1m36s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
626 lines
30 KiB
Python
626 lines
30 KiB
Python
#!/usr/bin/env python3
|
||
"""產生 AWOOOI Status Cleanup dashboard。
|
||
|
||
本工具只整合 status cleanup 的 read-only snapshots,提供未來 AWOOOI
|
||
Workspace / Delivery 頁面單一資料來源:
|
||
- 不修改 project_current_status / memory。
|
||
- 不同步 raw Codex App DB / auth / conversations / sessions。
|
||
- 不 fetch / pull / push。
|
||
- 不呼叫 Wazuh / Kali / SOC runtime。
|
||
- 不修改 workflow、repo、host、backup、restore 或 migration。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
from datetime import datetime, timezone
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
|
||
TARGET_ROUTE = "/workspace/status-cleanup"
|
||
PRIVATE_PROJECT_STATUS_PATH = (
|
||
"/Users/ogt/.claude/projects/-Users-ogt-awoooi/memory/project_current_status.md"
|
||
)
|
||
PUBLIC_PROJECT_STATUS_REF = "awoooi_memory/project_current_status.md"
|
||
|
||
|
||
def utc_now_iso() -> str:
|
||
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
||
|
||
|
||
def load_json(path: Path) -> dict[str, Any]:
|
||
return json.loads(path.read_text(encoding="utf-8"))
|
||
|
||
|
||
def workplan_completion(scorecard: dict[str, Any], item_id: str) -> int:
|
||
for item in scorecard.get("workplan_items", []):
|
||
if item.get("id") == item_id:
|
||
return int(item.get("completion_percent", 0))
|
||
return 0
|
||
|
||
|
||
def unique_strings(*groups: list[str]) -> list[str]:
|
||
seen: set[str] = set()
|
||
output: list[str] = []
|
||
for group in groups:
|
||
for item in group:
|
||
text = str(item)
|
||
if text and text not in seen:
|
||
seen.add(text)
|
||
output.append(text)
|
||
return output
|
||
|
||
|
||
def status_from_boolean(ok: bool) -> str:
|
||
return "green" if ok else "blocked"
|
||
|
||
|
||
def bool_token(value: Any) -> str:
|
||
return "true" if value is True else "false"
|
||
|
||
|
||
def append_boundary_tokens(boundary: str, tokens: list[str]) -> str:
|
||
parts = [boundary.strip()] if boundary.strip() else []
|
||
parts.extend(token for token in tokens if token and token not in boundary)
|
||
return " ".join(parts)
|
||
|
||
|
||
def public_blocker_text(value: str) -> str:
|
||
return value.replace(PRIVATE_PROJECT_STATUS_PATH, PUBLIC_PROJECT_STATUS_REF)
|
||
|
||
|
||
def section_value(owner_package: dict[str, Any], section_id: str) -> str:
|
||
for item in owner_package.get("required_update_sections", []):
|
||
if item.get("section_id") == section_id:
|
||
return str(item.get("value", ""))
|
||
return ""
|
||
|
||
|
||
def gate_cards(
|
||
preflight: dict[str, Any],
|
||
owner_package: dict[str, Any],
|
||
owner_response_preflight: dict[str, Any],
|
||
execution_plan: dict[str, Any],
|
||
apply_gate: dict[str, Any],
|
||
) -> list[dict[str, Any]]:
|
||
preflight_status = preflight["status"]
|
||
owner_summary = owner_package["summary"]
|
||
response_summary = owner_response_preflight["summary"]
|
||
plan_summary = execution_plan["summary"]
|
||
apply_summary = apply_gate["summary"]
|
||
|
||
return [
|
||
{
|
||
"gate_id": "status_cleanup_preflight",
|
||
"title": "狀態清理預檢",
|
||
"status": preflight_status,
|
||
"completion_label": (
|
||
f"age_days={preflight['project_current_status']['age_days']} "
|
||
f"stale_after={preflight['project_current_status']['stale_after_days']}"
|
||
),
|
||
"evidence_ref": "docs/operations/codex-status-cleanup-preflight.snapshot.json",
|
||
"blocked": preflight_status != "ready_status_current",
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
},
|
||
{
|
||
"gate_id": "owner_review_package",
|
||
"title": "Owner 審核包",
|
||
"status": owner_summary["review_status"],
|
||
"completion_label": (
|
||
f"{owner_summary['required_update_section_count']} sections / "
|
||
f"{owner_summary['required_owner_flag_count']} owner flags"
|
||
),
|
||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-review-package.snapshot.json",
|
||
"blocked": owner_summary["ready_for_status_cleanup_apply_gate"] is not True,
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
},
|
||
{
|
||
"gate_id": "owner_response_preflight",
|
||
"title": "Owner 回覆預檢",
|
||
"status": response_summary["preflight_status"],
|
||
"completion_label": (
|
||
f"{response_summary['accepted_owner_flag_count']}/"
|
||
f"{response_summary['required_owner_flag_count']} flags, "
|
||
f"{response_summary['approved_update_section_count']}/"
|
||
f"{response_summary['required_update_section_count']} sections"
|
||
),
|
||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json",
|
||
"blocked": response_summary["ready_for_status_cleanup_apply_gate"] is not True,
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
},
|
||
{
|
||
"gate_id": "execution_plan",
|
||
"title": "Dry-run 執行計畫",
|
||
"status": plan_summary["execution_status"],
|
||
"completion_label": (
|
||
f"{plan_summary['approved_update_section_count']}/"
|
||
f"{plan_summary['required_update_section_count']} sections, "
|
||
f"{plan_summary['approved_target_path_count']}/"
|
||
f"{plan_summary['target_path_count']} targets"
|
||
),
|
||
"evidence_ref": "docs/operations/codex-status-cleanup-execution-plan.snapshot.json",
|
||
"blocked": plan_summary["execution_status"] != "blocked_final_confirmation_required",
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
},
|
||
{
|
||
"gate_id": "apply_gate",
|
||
"title": "最終套用閘門",
|
||
"status": apply_summary["apply_gate_status"],
|
||
"completion_label": (
|
||
f"{apply_summary['confirmed_command_count']}/"
|
||
f"{apply_summary['planned_command_count']} commands, "
|
||
f"{apply_summary['confirmed_target_path_count']}/"
|
||
f"{apply_summary['target_path_count']} targets"
|
||
),
|
||
"evidence_ref": "docs/operations/codex-status-cleanup-apply-gate.snapshot.json",
|
||
"blocked": apply_summary["apply_allowed"] is not True,
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
},
|
||
]
|
||
|
||
|
||
def metric_cards(
|
||
preflight: dict[str, Any],
|
||
owner_response_preflight: dict[str, Any],
|
||
execution_plan: dict[str, Any],
|
||
apply_gate: dict[str, Any],
|
||
artifact_sync: dict[str, Any],
|
||
scorecard: dict[str, Any],
|
||
) -> list[dict[str, Any]]:
|
||
project_status = preflight["project_current_status"]
|
||
response_summary = owner_response_preflight["summary"]
|
||
plan_summary = execution_plan["summary"]
|
||
apply_summary = apply_gate["summary"]
|
||
artifact_summary = artifact_sync["summary"]
|
||
return [
|
||
{
|
||
"metric_id": "overall_completion",
|
||
"title": "整體治理完成度",
|
||
"value": f"{scorecard['overall_completion_percent']}%",
|
||
"status": scorecard.get("status", "orange"),
|
||
"caption": "產品 Runtime 治理完成度計分卡",
|
||
},
|
||
{
|
||
"metric_id": "project_status_age",
|
||
"title": "project_current_status 年齡",
|
||
"value": f"{project_status['age_days']} days",
|
||
"status": status_from_boolean(project_status["cleanup_required"] is False),
|
||
"caption": f"stale_after_days={project_status['stale_after_days']}",
|
||
},
|
||
{
|
||
"metric_id": "owner_flags",
|
||
"title": "Owner 旗標",
|
||
"value": f"{response_summary['accepted_owner_flag_count']}/{response_summary['required_owner_flag_count']}",
|
||
"status": status_from_boolean(response_summary["accepted_owner_flag_count"] == response_summary["required_owner_flag_count"]),
|
||
"caption": response_summary["owner_response_status"],
|
||
},
|
||
{
|
||
"metric_id": "approved_sections",
|
||
"title": "已批准章節",
|
||
"value": f"{response_summary['approved_update_section_count']}/{response_summary['required_update_section_count']}",
|
||
"status": status_from_boolean(response_summary["approved_update_section_count"] == response_summary["required_update_section_count"]),
|
||
"caption": f"targets={response_summary['approved_target_path_count']}/{response_summary['target_path_count']}",
|
||
},
|
||
{
|
||
"metric_id": "execution_preview",
|
||
"title": "執行預覽",
|
||
"value": plan_summary["execution_status"],
|
||
"status": status_from_boolean(plan_summary["preflight_ready"]),
|
||
"caption": "dry_run_only=true",
|
||
},
|
||
{
|
||
"metric_id": "apply_confirmation",
|
||
"title": "套用確認",
|
||
"value": f"{apply_summary['confirmed_command_count']}/{apply_summary['planned_command_count']}",
|
||
"status": status_from_boolean(apply_summary["apply_allowed"]),
|
||
"caption": apply_summary["final_confirmation_status"],
|
||
},
|
||
{
|
||
"metric_id": "artifact_sync",
|
||
"title": "安全交接 artifacts",
|
||
"value": f"{artifact_summary['synced_target_count']}/{artifact_summary['target_count']}",
|
||
"status": status_from_boolean(artifact_summary["blocked_target_count"] == 0),
|
||
"caption": f"artifacts={artifact_summary['artifact_count']}",
|
||
},
|
||
]
|
||
|
||
|
||
def workflow_rows(cards: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||
next_map = {
|
||
"status_cleanup_preflight": "owner_review_package",
|
||
"owner_review_package": "owner_response_preflight",
|
||
"owner_response_preflight": "execution_plan",
|
||
"execution_plan": "apply_gate",
|
||
"apply_gate": "manual_project_current_status_update_after_explicit_final_confirmation",
|
||
}
|
||
return [
|
||
{
|
||
"step_id": card["gate_id"],
|
||
"title": card["title"],
|
||
"status": card["status"],
|
||
"evidence_ref": card["evidence_ref"],
|
||
"next_step": next_map[card["gate_id"]],
|
||
"blocked": card["blocked"],
|
||
"memory_write_authorized": False,
|
||
"execution_authorized": False,
|
||
}
|
||
for card in cards
|
||
]
|
||
|
||
|
||
def risk_controls() -> list[dict[str, Any]]:
|
||
rows = [
|
||
("memory_write", "更新 project_current_status / memory", "blocked_final_apply_gate_required"),
|
||
("raw_codex_history", "同步 raw Codex App history", "blocked_raw_history_never_synced"),
|
||
("wazuh_runtime", "部署或查詢 live Wazuh runtime", "blocked_iwooos_runtime_lane_only"),
|
||
("workflow", "修改或觸發 CI/CD workflow", "blocked_workflow_authorization_required"),
|
||
("repo_refs", "建立 repo / branch / mirror refs", "blocked_source_control_approval_required"),
|
||
("host", "SSH / restart / firewall / Nginx / Docker / K8s", "blocked_host_maintenance_required"),
|
||
("backup_restore", "執行 backup / restore / migration", "blocked_data_owner_approval_required"),
|
||
("ui", "實作可見 AWOOOI UI", "blocked_apps_web_readiness_required"),
|
||
]
|
||
return [
|
||
{
|
||
"control_id": control_id,
|
||
"title": title,
|
||
"status": status,
|
||
"authorized": False,
|
||
}
|
||
for control_id, title, status in rows
|
||
]
|
||
|
||
|
||
def next_actions(owner_response_preflight: dict[str, Any], apply_gate: dict[str, Any]) -> list[str]:
|
||
response_summary = owner_response_preflight["summary"]
|
||
apply_summary = apply_gate["summary"]
|
||
return [
|
||
f"收齊 owner 旗標:{response_summary['accepted_owner_flag_count']}/{response_summary['required_owner_flag_count']}。",
|
||
f"收齊已批准章節:{response_summary['approved_update_section_count']}/{response_summary['required_update_section_count']}。",
|
||
f"確認目標路徑:{response_summary['approved_target_path_count']}/{response_summary['target_path_count']}。",
|
||
f"確認邊界 acknowledgement:{response_summary['boundary_ack_count']}/{response_summary['required_boundary_ack_count']}。",
|
||
"owner response preflight ready 後,重新產生 dry-run execution plan。",
|
||
f"apply gate 目前 apply_allowed={apply_summary['apply_allowed']};未變 true 前不得寫 project_current_status。",
|
||
]
|
||
|
||
|
||
def hard_gates() -> list[str]:
|
||
return [
|
||
"本 dashboard 只整合 read-only snapshots,不代表 project_current_status / memory 已更新。",
|
||
"memory_write_authorized=false 時不得修改 project_current_status 或任何 Memory 檔案。",
|
||
"不得同步 raw Codex App DB / auth / conversations / sessions。",
|
||
"不得把一般批准繼續視為 owner response 或 final apply confirmation。",
|
||
"不得新增 Wazuh / Kali / SOC runtime UI/API、live query、active response 或 host containment。",
|
||
"不得修改 Nginx / Docker / K8s / firewall / Wazuh secret,也不得重啟 host。",
|
||
"不得修改 .gitea/workflows、workflow_dispatch、repo refs、GitHub mirror 或 Gitea environment。",
|
||
"不得執行 backup、restore、migration、kubectl、docker compose up 或資料庫寫入。",
|
||
"apps/web build/i18n 必要檔案未恢復前,本 dashboard 只可作 page model 資料,不代表 UI 實作授權。",
|
||
]
|
||
|
||
|
||
def build_payload(
|
||
preflight_path: Path,
|
||
owner_package_path: Path,
|
||
owner_response_preflight_path: Path,
|
||
execution_plan_path: Path,
|
||
apply_gate_path: Path,
|
||
artifact_sync_path: Path,
|
||
scorecard_path: Path,
|
||
wazuh_handoff_path: Path,
|
||
) -> dict[str, Any]:
|
||
preflight = load_json(preflight_path)
|
||
owner_package = load_json(owner_package_path)
|
||
owner_response_preflight = load_json(owner_response_preflight_path)
|
||
execution_plan = load_json(execution_plan_path)
|
||
apply_gate = load_json(apply_gate_path)
|
||
artifact_sync = load_json(artifact_sync_path)
|
||
scorecard = load_json(scorecard_path)
|
||
wazuh_handoff = load_json(wazuh_handoff_path)
|
||
|
||
cards = gate_cards(
|
||
preflight,
|
||
owner_package,
|
||
owner_response_preflight,
|
||
execution_plan,
|
||
apply_gate,
|
||
)
|
||
response_summary = owner_response_preflight["summary"]
|
||
plan_summary = execution_plan["summary"]
|
||
apply_summary = apply_gate["summary"]
|
||
project_status = preflight["project_current_status"]
|
||
artifact_summary = artifact_sync["summary"]
|
||
wazuh_release_lane = wazuh_handoff["release_lane"]
|
||
wazuh_incident = wazuh_handoff.get("incident_sync", {})
|
||
wazuh_visibility = wazuh_handoff.get("agent_visibility_runtime_gate", {})
|
||
wazuh_boundary = append_boundary_tokens(
|
||
section_value(owner_package, "iwooos_wazuh_boundary"),
|
||
[
|
||
"agent_visibility_status="
|
||
+ str(wazuh_visibility.get("status", "blocked_waiting_manager_agent_registry_readback")),
|
||
"manager_agent_registry_readback_passed="
|
||
+ bool_token(wazuh_visibility.get("manager_agent_registry_readback_passed", False)),
|
||
"iwooos_live_route_readback_passed="
|
||
+ bool_token(wazuh_visibility.get("iwooos_live_route_readback_passed", False)),
|
||
"dashboard_agent_list_recovered="
|
||
+ bool_token(wazuh_visibility.get("dashboard_agent_list_recovered", False)),
|
||
"agent_visibility_runtime_gate_count="
|
||
+ str(wazuh_visibility.get("runtime_gate_count", 0)),
|
||
],
|
||
)
|
||
blockers = [
|
||
public_blocker_text(item)
|
||
for item in unique_strings(
|
||
preflight.get("hard_gates", []),
|
||
[
|
||
f"owner_response_preflight:{item}"
|
||
for item in owner_response_preflight.get("blocking_reasons", [])
|
||
],
|
||
[
|
||
f"execution_plan_blocked_until:{item}"
|
||
for item in execution_plan.get("blocked_until", [])
|
||
],
|
||
[f"apply_gate:{item}" for item in apply_gate.get("blocking_reasons", [])],
|
||
[f"wazuh_boundary:{wazuh_boundary}"] if wazuh_boundary else [],
|
||
)
|
||
]
|
||
actions = next_actions(owner_response_preflight, apply_gate)
|
||
metrics = metric_cards(
|
||
preflight,
|
||
owner_response_preflight,
|
||
execution_plan,
|
||
apply_gate,
|
||
artifact_sync,
|
||
scorecard,
|
||
)
|
||
|
||
return {
|
||
"schema_version": "awoooi_status_cleanup_dashboard_v1",
|
||
"generated_at": utc_now_iso(),
|
||
"target_route": TARGET_ROUTE,
|
||
"source_reviews": {
|
||
"status_cleanup_preflight": str(preflight_path),
|
||
"owner_review_package": str(owner_package_path),
|
||
"owner_response_preflight": str(owner_response_preflight_path),
|
||
"execution_plan": str(execution_plan_path),
|
||
"apply_gate": str(apply_gate_path),
|
||
"artifact_sync_readback": str(artifact_sync_path),
|
||
"scorecard": str(scorecard_path),
|
||
"iwooos_wazuh_release_handoff": str(wazuh_handoff_path),
|
||
},
|
||
"summary": {
|
||
"dashboard_status": "blocked_status_cleanup_apply_not_authorized",
|
||
"overall_completion_percent": scorecard["overall_completion_percent"],
|
||
"p0_009_completion_percent": workplan_completion(scorecard, "P0-009"),
|
||
"p0_010_completion_percent": workplan_completion(scorecard, "P0-010"),
|
||
"p1_006_completion_percent": workplan_completion(scorecard, "P1-006"),
|
||
"p2_001_completion_percent": workplan_completion(scorecard, "P2-001"),
|
||
"p2_002_completion_percent": workplan_completion(scorecard, "P2-002"),
|
||
"gate_count": len(cards),
|
||
"blocked_gate_count": sum(1 for card in cards if card["blocked"]),
|
||
"project_current_status_age_days": project_status["age_days"],
|
||
"cleanup_required": project_status["cleanup_required"],
|
||
"accepted_owner_flag_count": response_summary["accepted_owner_flag_count"],
|
||
"required_owner_flag_count": response_summary["required_owner_flag_count"],
|
||
"approved_update_section_count": response_summary["approved_update_section_count"],
|
||
"required_update_section_count": response_summary["required_update_section_count"],
|
||
"approved_target_path_count": response_summary["approved_target_path_count"],
|
||
"target_path_count": response_summary["target_path_count"],
|
||
"boundary_ack_count": response_summary["boundary_ack_count"],
|
||
"required_boundary_ack_count": response_summary["required_boundary_ack_count"],
|
||
"execution_plan_status": plan_summary["execution_status"],
|
||
"apply_gate_status": apply_summary["apply_gate_status"],
|
||
"confirmed_command_count": apply_summary["confirmed_command_count"],
|
||
"planned_command_count": apply_summary["planned_command_count"],
|
||
"artifact_count": artifact_summary["artifact_count"],
|
||
"artifact_sync_blocked_count": artifact_summary["blocked_target_count"],
|
||
"wazuh_handoff_status": wazuh_handoff["status"],
|
||
"wazuh_handoff_base_commit": wazuh_handoff["base"]["commit"],
|
||
"wazuh_handoff_commit_count": len(wazuh_handoff["commits"]),
|
||
"wazuh_handoff_patch_count": len(wazuh_handoff["patch_set"]["patches"]),
|
||
"wazuh_release_owner_request_sent_count": wazuh_release_lane["release_owner_request_sent_count"],
|
||
"wazuh_release_owner_response_accepted_count": wazuh_release_lane["release_owner_response_accepted_count"],
|
||
"wazuh_acknowledged_release_owner_ack_count": wazuh_release_lane["acknowledged_release_owner_ack_count"],
|
||
"wazuh_required_release_owner_ack_count": wazuh_release_lane["required_release_owner_ack_count"],
|
||
"wazuh_accepted_evidence_count": wazuh_release_lane["accepted_evidence_count"],
|
||
"wazuh_required_evidence_count": wazuh_release_lane["required_evidence_count"],
|
||
"wazuh_live_metadata_owner_count": wazuh_release_lane["live_metadata_owner_count"],
|
||
"wazuh_secret_metadata_count": wazuh_release_lane["secret_metadata_count"],
|
||
"wazuh_live_agent_registry_readback": wazuh_incident.get("wazuh_live_agent_registry_readback", 0),
|
||
"iwooos_wazuh_runtime_gate": wazuh_incident.get("iwooos_wazuh_runtime_gate", 0),
|
||
"wazuh_active_response_count": wazuh_incident.get("active_response", 0),
|
||
"wazuh_agent_visibility_status": wazuh_visibility.get("status", "blocked_waiting_manager_agent_registry_readback"),
|
||
"wazuh_manager_agent_registry_readback_passed": wazuh_visibility.get("manager_agent_registry_readback_passed", False),
|
||
"wazuh_iwooos_live_route_readback_passed": wazuh_visibility.get("iwooos_live_route_readback_passed", False),
|
||
"wazuh_dashboard_agent_list_recovered": wazuh_visibility.get("dashboard_agent_list_recovered", False),
|
||
"wazuh_agent_visibility_runtime_gate_count": wazuh_visibility.get("runtime_gate_count", 0),
|
||
"wazuh_push_gate_count": wazuh_release_lane["push_gate_count"],
|
||
"wazuh_deploy_gate_count": wazuh_release_lane["deploy_gate_count"],
|
||
"wazuh_readback_gate_count": wazuh_release_lane["readback_gate_count"],
|
||
"wazuh_runtime_gate_count": wazuh_release_lane["runtime_gate_count"],
|
||
"blocking_reason_count": len(blockers),
|
||
"next_action_count": len(actions),
|
||
"apply_allowed": False,
|
||
"memory_write_authorized": False,
|
||
"wazuh_api_live_query_authorized": False,
|
||
"runtime_execution_authorized": False,
|
||
"ui_implementation_allowed": False,
|
||
},
|
||
"metric_cards": metrics,
|
||
"gate_cards": cards,
|
||
"workflow_rows": workflow_rows(cards),
|
||
"risk_controls": risk_controls(),
|
||
"blocking_reasons": blockers,
|
||
"next_actions": actions,
|
||
"wazuh_handoff": {
|
||
"status": wazuh_handoff["status"],
|
||
"source_ref": str(wazuh_handoff_path),
|
||
"branch": wazuh_handoff["branch"],
|
||
"base_commit": wazuh_handoff["base"]["commit"],
|
||
"commit_count": len(wazuh_handoff["commits"]),
|
||
"patch_count": len(wazuh_handoff["patch_set"]["patches"]),
|
||
"release_owner_request_sent_count": wazuh_release_lane["release_owner_request_sent_count"],
|
||
"release_owner_response_accepted_count": wazuh_release_lane["release_owner_response_accepted_count"],
|
||
"acknowledged_release_owner_ack_count": wazuh_release_lane["acknowledged_release_owner_ack_count"],
|
||
"required_release_owner_ack_count": wazuh_release_lane["required_release_owner_ack_count"],
|
||
"accepted_evidence_count": wazuh_release_lane["accepted_evidence_count"],
|
||
"required_evidence_count": wazuh_release_lane["required_evidence_count"],
|
||
"live_metadata_owner_count": wazuh_release_lane["live_metadata_owner_count"],
|
||
"secret_metadata_count": wazuh_release_lane["secret_metadata_count"],
|
||
"wazuh_live_agent_registry_readback": wazuh_incident.get("wazuh_live_agent_registry_readback", 0),
|
||
"iwooos_wazuh_runtime_gate": wazuh_incident.get("iwooos_wazuh_runtime_gate", 0),
|
||
"active_response": wazuh_incident.get("active_response", 0),
|
||
"stored_api_secret_metadata_changed": wazuh_incident.get("stored_api_secret_metadata_changed", False),
|
||
"agent_visibility_status": wazuh_visibility.get("status", "blocked_waiting_manager_agent_registry_readback"),
|
||
"manager_agent_registry_readback_passed": wazuh_visibility.get("manager_agent_registry_readback_passed", False),
|
||
"iwooos_live_route_readback_passed": wazuh_visibility.get("iwooos_live_route_readback_passed", False),
|
||
"dashboard_agent_list_recovered": wazuh_visibility.get("dashboard_agent_list_recovered", False),
|
||
"agent_visibility_runtime_gate_count": wazuh_visibility.get("runtime_gate_count", 0),
|
||
"push_gate_count": wazuh_release_lane["push_gate_count"],
|
||
"deploy_gate_count": wazuh_release_lane["deploy_gate_count"],
|
||
"readback_gate_count": wazuh_release_lane["readback_gate_count"],
|
||
"runtime_gate_count": wazuh_release_lane["runtime_gate_count"],
|
||
"production_readback_status": wazuh_handoff["production_readback"]["status"],
|
||
"boundary": wazuh_boundary,
|
||
"runtime_execution_authorized": False,
|
||
"wazuh_api_live_query_authorized": False,
|
||
},
|
||
"hard_gates": hard_gates(),
|
||
"operation_boundaries": preflight["operation_boundaries"],
|
||
"secret_values_collected": False,
|
||
"remote_write_performed": False,
|
||
"local_product_write_performed": False,
|
||
"execution_authorized": False,
|
||
"memory_write_authorized": False,
|
||
"wazuh_api_live_query_authorized": False,
|
||
"runtime_execution_authorized": False,
|
||
"ui_implementation_authorized": False,
|
||
}
|
||
|
||
|
||
def write_markdown(payload: dict[str, Any], path: Path) -> None:
|
||
summary = payload["summary"]
|
||
lines = [
|
||
"# AWOOOI 狀態清理儀表板",
|
||
"",
|
||
f"- generated_at: `{payload['generated_at']}`",
|
||
f"- dashboard_status: `{summary['dashboard_status']}`",
|
||
f"- overall_completion_percent: `{summary['overall_completion_percent']}%`",
|
||
f"- gates: `{summary['blocked_gate_count']}/{summary['gate_count']} blocked`",
|
||
f"- owner_flags: `{summary['accepted_owner_flag_count']}/{summary['required_owner_flag_count']}`",
|
||
f"- sections: `{summary['approved_update_section_count']}/{summary['required_update_section_count']}`",
|
||
f"- confirmed_commands: `{summary['confirmed_command_count']}/{summary['planned_command_count']}`",
|
||
f"- artifact_count: `{summary['artifact_count']}`",
|
||
f"- wazuh_handoff: `{summary['wazuh_handoff_status']} base={summary['wazuh_handoff_base_commit']} patches={summary['wazuh_handoff_patch_count']}`",
|
||
f"- wazuh_live_metadata: `owner={summary['wazuh_live_metadata_owner_count']} secret_metadata={summary['wazuh_secret_metadata_count']}`",
|
||
f"- wazuh_live_agent_registry_readback: `{summary['wazuh_live_agent_registry_readback']}`",
|
||
f"- iwooos_wazuh_runtime_gate: `{summary['iwooos_wazuh_runtime_gate']}`",
|
||
f"- wazuh_active_response_count: `{summary['wazuh_active_response_count']}`",
|
||
f"- wazuh_agent_visibility_status: `{summary['wazuh_agent_visibility_status']}`",
|
||
f"- memory_write_authorized: `{payload['memory_write_authorized']}`",
|
||
f"- runtime_execution_authorized: `{payload['runtime_execution_authorized']}`",
|
||
f"- wazuh_api_live_query_authorized: `{payload['wazuh_api_live_query_authorized']}`",
|
||
"",
|
||
"## 指標卡片",
|
||
"",
|
||
"| 指標 | 數值 | 狀態 | 說明 |",
|
||
"|--------|-------|--------|---------|",
|
||
]
|
||
for item in payload["metric_cards"]:
|
||
lines.append(
|
||
f"| `{item['metric_id']}` | `{item['value']}` | `{item['status']}` | {item['caption']} |"
|
||
)
|
||
|
||
lines.extend(["", "## 閘門卡片", "", "| 閘門 | 狀態 | 是否封鎖 | 證據 |", "|------|------|----------|------|"])
|
||
for item in payload["gate_cards"]:
|
||
lines.append(
|
||
f"| `{item['gate_id']}` | `{item['status']}` | `{item['blocked']}` | `{item['evidence_ref']}` |"
|
||
)
|
||
|
||
lines.extend(["", "## 風險控制", "", "| 控制項 | 狀態 | 已授權 |", "|--------|------|--------|"])
|
||
for item in payload["risk_controls"]:
|
||
lines.append(f"| `{item['control_id']}` | `{item['status']}` | `{item['authorized']}` |")
|
||
|
||
lines.extend(["", "## 下一步", ""])
|
||
lines.extend(f"- {item}" for item in payload["next_actions"])
|
||
lines.extend(["", "## 封鎖原因", ""])
|
||
lines.extend(f"- `{item}`" for item in payload["blocking_reasons"])
|
||
lines.extend(["", "## 強制閘門", ""])
|
||
lines.extend(f"- {item}" for item in payload["hard_gates"])
|
||
lines.append("")
|
||
path.write_text("\n".join(lines), encoding="utf-8")
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument(
|
||
"--preflight",
|
||
default="docs/operations/codex-status-cleanup-preflight.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--owner-package",
|
||
default="docs/operations/codex-status-cleanup-owner-review-package.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--owner-response-preflight",
|
||
default="docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--execution-plan",
|
||
default="docs/operations/codex-status-cleanup-execution-plan.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--apply-gate",
|
||
default="docs/operations/codex-status-cleanup-apply-gate.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--artifact-sync",
|
||
default="docs/operations/codex-workstation-artifact-sync-readback.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--scorecard",
|
||
default="docs/operations/product-runtime-governance-completion-scorecard.snapshot.json",
|
||
)
|
||
parser.add_argument(
|
||
"--wazuh-handoff",
|
||
default="docs/operations/iwooos-wazuh-release-handoff.snapshot.json",
|
||
)
|
||
parser.add_argument("--output-json", required=True)
|
||
parser.add_argument("--output-md", required=True)
|
||
args = parser.parse_args()
|
||
|
||
payload = build_payload(
|
||
Path(args.preflight),
|
||
Path(args.owner_package),
|
||
Path(args.owner_response_preflight),
|
||
Path(args.execution_plan),
|
||
Path(args.apply_gate),
|
||
Path(args.artifact_sync),
|
||
Path(args.scorecard),
|
||
Path(args.wazuh_handoff),
|
||
)
|
||
Path(args.output_json).write_text(
|
||
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
|
||
encoding="utf-8",
|
||
)
|
||
write_markdown(payload, Path(args.output_md))
|
||
summary = payload["summary"]
|
||
print(
|
||
"AWOOOI_STATUS_CLEANUP_DASHBOARD_OK"
|
||
+ f" status={summary['dashboard_status']}"
|
||
+ f" gates={summary['blocked_gate_count']}/{summary['gate_count']}"
|
||
+ f" owner_flags={summary['accepted_owner_flag_count']}/{summary['required_owner_flag_count']}"
|
||
+ f" apply_allowed={summary['apply_allowed']}"
|
||
+ f" memory_write={payload['memory_write_authorized']}"
|
||
)
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|