Compare commits
2 Commits
main
...
codex/stat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f3a5cdcdd | ||
|
|
e4c13530fd |
@@ -73,6 +73,9 @@ from src.services.ai_agent_critic_reviewer_result_capture import (
|
||||
from src.services.ai_agent_deployment_layout import (
|
||||
load_latest_ai_agent_deployment_layout,
|
||||
)
|
||||
from src.services.awoooi_status_cleanup_dashboard import (
|
||||
load_latest_awoooi_status_cleanup_dashboard,
|
||||
)
|
||||
from src.services.ai_agent_failure_receipt_no_send_replay import (
|
||||
load_latest_ai_agent_failure_receipt_no_send_replay,
|
||||
)
|
||||
@@ -763,6 +766,36 @@ async def get_agent_deployment_layout() -> dict[str, Any]:
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/awoooi-status-cleanup-dashboard",
|
||||
response_model=dict[str, Any],
|
||||
summary="取得 AWOOOI 狀態清理儀表板",
|
||||
description=(
|
||||
"讀取最新已提交的 AWOOOI Status Cleanup Dashboard 只讀快照;"
|
||||
"此端點只呈現狀態清理完成度、owner gate、apply gate、artifact sync 與 Wazuh handoff 邊界。"
|
||||
"它不更新 project_current_status / memory、不同步 raw Codex App DB / auth / conversations / sessions、"
|
||||
"不呼叫 Wazuh live API、不執行 active response、不改主機、不修改 workflow / repo refs、"
|
||||
"不執行 backup / restore / migration。"
|
||||
),
|
||||
)
|
||||
async def get_awoooi_status_cleanup_dashboard() -> dict[str, Any]:
|
||||
"""回傳最新 AWOOOI 狀態清理儀表板只讀快照。"""
|
||||
try:
|
||||
payload = await asyncio.to_thread(load_latest_awoooi_status_cleanup_dashboard)
|
||||
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_status_cleanup_dashboard_invalid", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="AWOOOI 狀態清理儀表板快照無效",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/agent-12-agent-war-room",
|
||||
response_model=dict[str, Any],
|
||||
|
||||
276
apps/api/src/services/awoooi_status_cleanup_dashboard.py
Normal file
276
apps/api/src/services/awoooi_status_cleanup_dashboard.py
Normal file
@@ -0,0 +1,276 @@
|
||||
"""
|
||||
AWOOOI Status Cleanup dashboard snapshot.
|
||||
|
||||
Loads the read-only dashboard that combines status cleanup preflight, owner
|
||||
review, owner response preflight, dry-run execution plan, apply gate, and
|
||||
artifact sync readback. This module never writes memory, syncs raw Codex app
|
||||
data, fetches repos, modifies workflows, queries Wazuh live APIs, or authorizes
|
||||
runtime writes.
|
||||
"""
|
||||
|
||||
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_PATTERN = "awoooi-status-cleanup-dashboard.snapshot.json"
|
||||
_SCHEMA_VERSION = "awoooi_status_cleanup_dashboard_v1"
|
||||
|
||||
|
||||
def load_latest_awoooi_status_cleanup_dashboard(
|
||||
operations_dir: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Load the committed AWOOOI Status Cleanup dashboard snapshot."""
|
||||
directory = operations_dir or _DEFAULT_OPERATIONS_DIR
|
||||
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
|
||||
if not candidates:
|
||||
raise FileNotFoundError(f"no AWOOOI status cleanup dashboard found in {directory}")
|
||||
|
||||
latest = candidates[-1]
|
||||
with latest.open(encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError(f"{latest}: expected JSON object")
|
||||
_require_schema(payload, _SCHEMA_VERSION, str(latest))
|
||||
_require_read_only_boundaries(payload, str(latest))
|
||||
_require_summary(payload, str(latest))
|
||||
_require_cards(payload, str(latest))
|
||||
_require_risk_controls(payload, str(latest))
|
||||
_require_wazuh_boundary(payload, str(latest))
|
||||
_require_hard_gates(payload, str(latest))
|
||||
return payload
|
||||
|
||||
|
||||
def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None:
|
||||
actual = payload.get("schema_version")
|
||||
if actual != expected:
|
||||
raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}")
|
||||
|
||||
|
||||
def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None:
|
||||
blocked_flags = {
|
||||
"secret_values_collected",
|
||||
"remote_write_performed",
|
||||
"local_product_write_performed",
|
||||
"execution_authorized",
|
||||
"memory_write_authorized",
|
||||
"wazuh_api_live_query_authorized",
|
||||
"runtime_execution_authorized",
|
||||
"ui_implementation_authorized",
|
||||
}
|
||||
invalid = sorted(flag for flag in blocked_flags if payload.get(flag) is not False)
|
||||
if invalid:
|
||||
raise ValueError(f"{label}: dashboard boundaries must remain false: {invalid}")
|
||||
|
||||
operation_boundaries = payload.get("operation_boundaries") or {}
|
||||
invalid_ops = sorted(key for key, value in operation_boundaries.items() if value is not False)
|
||||
if invalid_ops:
|
||||
raise ValueError(f"{label}: operation boundaries must remain false: {invalid_ops}")
|
||||
|
||||
|
||||
def _require_summary(payload: dict[str, Any], label: str) -> None:
|
||||
summary = payload.get("summary") or {}
|
||||
gates = payload.get("gate_cards") or []
|
||||
blockers = payload.get("blocking_reasons") or []
|
||||
actions = payload.get("next_actions") or []
|
||||
if payload.get("target_route") != "/workspace/status-cleanup":
|
||||
raise ValueError(f"{label}: target_route must remain /workspace/status-cleanup")
|
||||
if summary.get("dashboard_status") != "blocked_status_cleanup_apply_not_authorized":
|
||||
raise ValueError(f"{label}: dashboard_status must remain blocked")
|
||||
if summary.get("gate_count") != len(gates):
|
||||
raise ValueError(f"{label}: gate_count mismatch")
|
||||
if summary.get("blocked_gate_count") != sum(1 for item in gates if item.get("blocked") is True):
|
||||
raise ValueError(f"{label}: blocked_gate_count mismatch")
|
||||
if summary.get("blocking_reason_count") != len(blockers):
|
||||
raise ValueError(f"{label}: blocking_reason_count mismatch")
|
||||
if summary.get("next_action_count") != len(actions):
|
||||
raise ValueError(f"{label}: next_action_count mismatch")
|
||||
for flag in (
|
||||
"apply_allowed",
|
||||
"memory_write_authorized",
|
||||
"wazuh_api_live_query_authorized",
|
||||
"runtime_execution_authorized",
|
||||
"ui_implementation_allowed",
|
||||
):
|
||||
if summary.get(flag) is not False:
|
||||
raise ValueError(f"{label}: summary.{flag} must remain false")
|
||||
if summary.get("gate_count") != 5:
|
||||
raise ValueError(f"{label}: dashboard must contain five status cleanup gates")
|
||||
if summary.get("wazuh_handoff_status") != "blocked_not_released":
|
||||
raise ValueError(f"{label}: Wazuh handoff status must remain blocked")
|
||||
if summary.get("wazuh_handoff_base_commit") != "b540fc0c":
|
||||
raise ValueError(f"{label}: Wazuh handoff base must remain b540fc0c")
|
||||
if summary.get("wazuh_handoff_commit_count") != 7:
|
||||
raise ValueError(f"{label}: Wazuh handoff commit count must be 7")
|
||||
if summary.get("wazuh_handoff_patch_count") != 7:
|
||||
raise ValueError(f"{label}: Wazuh handoff patch count must be 7")
|
||||
zero_wazuh_summary = (
|
||||
"wazuh_release_owner_request_sent_count",
|
||||
"wazuh_release_owner_response_accepted_count",
|
||||
"wazuh_acknowledged_release_owner_ack_count",
|
||||
"wazuh_accepted_evidence_count",
|
||||
"wazuh_live_metadata_owner_count",
|
||||
"wazuh_secret_metadata_count",
|
||||
"wazuh_live_agent_registry_readback",
|
||||
"iwooos_wazuh_runtime_gate",
|
||||
"wazuh_active_response_count",
|
||||
"wazuh_agent_visibility_runtime_gate_count",
|
||||
"wazuh_push_gate_count",
|
||||
"wazuh_deploy_gate_count",
|
||||
"wazuh_readback_gate_count",
|
||||
"wazuh_runtime_gate_count",
|
||||
)
|
||||
non_zero_wazuh = sorted(key for key in zero_wazuh_summary if summary.get(key) != 0)
|
||||
if non_zero_wazuh:
|
||||
raise ValueError(f"{label}: Wazuh release summary gates must remain zero: {non_zero_wazuh}")
|
||||
|
||||
|
||||
def _require_cards(payload: dict[str, Any], label: str) -> None:
|
||||
gate_ids = {item.get("gate_id") for item in payload.get("gate_cards") or []}
|
||||
required_gates = {
|
||||
"status_cleanup_preflight",
|
||||
"owner_review_package",
|
||||
"owner_response_preflight",
|
||||
"execution_plan",
|
||||
"apply_gate",
|
||||
}
|
||||
missing_gates = sorted(required_gates - gate_ids)
|
||||
if missing_gates:
|
||||
raise ValueError(f"{label}: gate_cards missing required gates: {missing_gates}")
|
||||
|
||||
metric_ids = {item.get("metric_id") for item in payload.get("metric_cards") or []}
|
||||
required_metrics = {
|
||||
"overall_completion",
|
||||
"project_status_age",
|
||||
"owner_flags",
|
||||
"approved_sections",
|
||||
"execution_preview",
|
||||
"apply_confirmation",
|
||||
"artifact_sync",
|
||||
}
|
||||
missing_metrics = sorted(required_metrics - metric_ids)
|
||||
if missing_metrics:
|
||||
raise ValueError(f"{label}: metric_cards missing required metrics: {missing_metrics}")
|
||||
|
||||
unsafe = [
|
||||
item.get("gate_id")
|
||||
for item in payload.get("gate_cards") or []
|
||||
if item.get("memory_write_authorized") is not False
|
||||
or item.get("execution_authorized") is not False
|
||||
]
|
||||
if unsafe:
|
||||
raise ValueError(f"{label}: gate cards must remain read-only: {sorted(unsafe)}")
|
||||
|
||||
|
||||
def _require_risk_controls(payload: dict[str, Any], label: str) -> None:
|
||||
control_ids = {item.get("control_id") for item in payload.get("risk_controls") or []}
|
||||
required = {
|
||||
"memory_write",
|
||||
"raw_codex_history",
|
||||
"wazuh_runtime",
|
||||
"workflow",
|
||||
"repo_refs",
|
||||
"host",
|
||||
"backup_restore",
|
||||
"ui",
|
||||
}
|
||||
missing = sorted(required - control_ids)
|
||||
if missing:
|
||||
raise ValueError(f"{label}: risk_controls missing controls: {missing}")
|
||||
unsafe = sorted(
|
||||
item.get("control_id")
|
||||
for item in payload.get("risk_controls") or []
|
||||
if item.get("authorized") is not False
|
||||
)
|
||||
if unsafe:
|
||||
raise ValueError(f"{label}: risk controls must not authorize actions: {unsafe}")
|
||||
|
||||
|
||||
def _require_wazuh_boundary(payload: dict[str, Any], label: str) -> None:
|
||||
handoff = payload.get("wazuh_handoff") or {}
|
||||
if handoff.get("status") != "blocked_not_released":
|
||||
raise ValueError(f"{label}: Wazuh handoff status must remain blocked_not_released")
|
||||
if handoff.get("base_commit") != "b540fc0c":
|
||||
raise ValueError(f"{label}: Wazuh handoff base must remain b540fc0c")
|
||||
if handoff.get("commit_count") != 7:
|
||||
raise ValueError(f"{label}: Wazuh handoff commit_count must be 7")
|
||||
if handoff.get("patch_count") != 7:
|
||||
raise ValueError(f"{label}: Wazuh handoff patch_count must be 7")
|
||||
zero_handoff_keys = (
|
||||
"release_owner_request_sent_count",
|
||||
"release_owner_response_accepted_count",
|
||||
"acknowledged_release_owner_ack_count",
|
||||
"accepted_evidence_count",
|
||||
"live_metadata_owner_count",
|
||||
"secret_metadata_count",
|
||||
"wazuh_live_agent_registry_readback",
|
||||
"iwooos_wazuh_runtime_gate",
|
||||
"active_response",
|
||||
"push_gate_count",
|
||||
"deploy_gate_count",
|
||||
"readback_gate_count",
|
||||
"runtime_gate_count",
|
||||
)
|
||||
non_zero_handoff = sorted(key for key in zero_handoff_keys if handoff.get(key) != 0)
|
||||
if non_zero_handoff:
|
||||
raise ValueError(f"{label}: Wazuh handoff gates must remain zero: {non_zero_handoff}")
|
||||
if handoff.get("runtime_execution_authorized") is not False:
|
||||
raise ValueError(f"{label}: Wazuh runtime execution must remain false")
|
||||
if handoff.get("wazuh_api_live_query_authorized") is not False:
|
||||
raise ValueError(f"{label}: Wazuh live query must remain false")
|
||||
if handoff.get("stored_api_secret_metadata_changed") is not False:
|
||||
raise ValueError(f"{label}: Wazuh stored API secret metadata must remain unchanged")
|
||||
if handoff.get("agent_visibility_status") != "blocked_waiting_manager_agent_registry_readback":
|
||||
raise ValueError(f"{label}: Wazuh agent visibility must remain blocked")
|
||||
visibility_false = (
|
||||
"manager_agent_registry_readback_passed",
|
||||
"iwooos_live_route_readback_passed",
|
||||
"dashboard_agent_list_recovered",
|
||||
)
|
||||
invalid_visibility = sorted(key for key in visibility_false if handoff.get(key) is not False)
|
||||
if invalid_visibility:
|
||||
raise ValueError(f"{label}: Wazuh visibility flags must remain false: {invalid_visibility}")
|
||||
if handoff.get("agent_visibility_runtime_gate_count") != 0:
|
||||
raise ValueError(f"{label}: Wazuh visibility runtime gate count must remain zero")
|
||||
boundary = str(handoff.get("boundary", ""))
|
||||
for token in (
|
||||
"base=b540fc0c",
|
||||
"38dc3c2f",
|
||||
"9a53d3e1",
|
||||
"e9972d47",
|
||||
"758d419e",
|
||||
"04db4b8a",
|
||||
"8eec298e",
|
||||
"325f262a",
|
||||
"release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0",
|
||||
"owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0",
|
||||
"live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0",
|
||||
"wazuh_live_agent_registry_readback=0",
|
||||
"iwooos_wazuh_runtime_gate=0",
|
||||
"active_response=0",
|
||||
"manager_agent_registry_readback_passed=false",
|
||||
"iwooos_live_route_readback_passed=false",
|
||||
"dashboard_agent_list_recovered=false",
|
||||
"push_blocked",
|
||||
):
|
||||
if token not in boundary:
|
||||
raise ValueError(f"{label}: Wazuh handoff boundary missing {token}")
|
||||
|
||||
|
||||
def _require_hard_gates(payload: dict[str, Any], label: str) -> None:
|
||||
gate_text = "\n".join(payload.get("hard_gates") or [])
|
||||
for token in (
|
||||
"project_current_status",
|
||||
"memory_write_authorized=false",
|
||||
"raw Codex App",
|
||||
"Wazuh",
|
||||
".gitea/workflows",
|
||||
"apps/web",
|
||||
):
|
||||
if token not in gate_text:
|
||||
raise ValueError(f"{label}: hard_gates missing {token}")
|
||||
@@ -30,3 +30,8 @@ def resolve_repo_root(anchor: Path) -> Path:
|
||||
def default_evaluations_dir(anchor: Path) -> Path:
|
||||
"""Resolve the default committed evaluations snapshot directory."""
|
||||
return resolve_repo_root(anchor) / "docs" / "evaluations"
|
||||
|
||||
|
||||
def default_operations_dir(anchor: Path) -> Path:
|
||||
"""Resolve the default committed operations snapshot directory."""
|
||||
return resolve_repo_root(anchor) / "docs" / "operations"
|
||||
|
||||
115
apps/api/tests/test_awoooi_status_cleanup_dashboard.py
Normal file
115
apps/api/tests/test_awoooi_status_cleanup_dashboard.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from src.services.awoooi_status_cleanup_dashboard import (
|
||||
load_latest_awoooi_status_cleanup_dashboard,
|
||||
)
|
||||
|
||||
|
||||
def test_load_latest_awoooi_status_cleanup_dashboard_reads_committed_snapshot():
|
||||
data = load_latest_awoooi_status_cleanup_dashboard()
|
||||
|
||||
assert data["schema_version"] == "awoooi_status_cleanup_dashboard_v1"
|
||||
assert data["summary"]["dashboard_status"] == "blocked_status_cleanup_apply_not_authorized"
|
||||
assert data["summary"]["gate_count"] == 5
|
||||
assert data["summary"]["blocked_gate_count"] == 5
|
||||
assert data["summary"]["accepted_owner_flag_count"] == 0
|
||||
assert data["summary"]["required_owner_flag_count"] == 6
|
||||
assert data["summary"]["apply_allowed"] is False
|
||||
assert data["summary"]["memory_write_authorized"] is False
|
||||
assert data["summary"]["wazuh_api_live_query_authorized"] is False
|
||||
assert data["summary"]["runtime_execution_authorized"] is False
|
||||
assert data["summary"]["wazuh_handoff_status"] == "blocked_not_released"
|
||||
assert data["summary"]["wazuh_handoff_base_commit"] == "b540fc0c"
|
||||
assert data["summary"]["wazuh_handoff_commit_count"] == 7
|
||||
assert data["summary"]["wazuh_handoff_patch_count"] == 7
|
||||
assert data["summary"]["wazuh_live_metadata_owner_count"] == 0
|
||||
assert data["summary"]["wazuh_secret_metadata_count"] == 0
|
||||
assert data["summary"]["wazuh_live_agent_registry_readback"] == 0
|
||||
assert data["summary"]["iwooos_wazuh_runtime_gate"] == 0
|
||||
assert data["summary"]["wazuh_active_response_count"] == 0
|
||||
assert data["summary"]["wazuh_agent_visibility_status"] == "blocked_waiting_manager_agent_registry_readback"
|
||||
assert data["summary"]["wazuh_manager_agent_registry_readback_passed"] is False
|
||||
assert data["summary"]["wazuh_iwooos_live_route_readback_passed"] is False
|
||||
assert data["summary"]["wazuh_dashboard_agent_list_recovered"] is False
|
||||
assert data["summary"]["wazuh_agent_visibility_runtime_gate_count"] == 0
|
||||
assert data["summary"]["wazuh_runtime_gate_count"] == 0
|
||||
assert data["memory_write_authorized"] is False
|
||||
assert data["runtime_execution_authorized"] is False
|
||||
assert data["wazuh_handoff"]["wazuh_api_live_query_authorized"] is False
|
||||
assert data["wazuh_handoff"]["base_commit"] == "b540fc0c"
|
||||
assert data["wazuh_handoff"]["patch_count"] == 7
|
||||
assert data["wazuh_handoff"]["live_metadata_owner_count"] == 0
|
||||
assert data["wazuh_handoff"]["wazuh_live_agent_registry_readback"] == 0
|
||||
assert data["wazuh_handoff"]["active_response"] == 0
|
||||
assert data["wazuh_handoff"]["agent_visibility_status"] == "blocked_waiting_manager_agent_registry_readback"
|
||||
assert data["wazuh_handoff"]["manager_agent_registry_readback_passed"] is False
|
||||
assert "base=b540fc0c" in data["wazuh_handoff"]["boundary"]
|
||||
assert "release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0" in data["wazuh_handoff"]["boundary"]
|
||||
assert "owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0" in data["wazuh_handoff"]["boundary"]
|
||||
assert "live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0" in data["wazuh_handoff"]["boundary"]
|
||||
assert "wazuh_live_agent_registry_readback=0" in data["wazuh_handoff"]["boundary"]
|
||||
assert "manager_agent_registry_readback_passed=false" in data["wazuh_handoff"]["boundary"]
|
||||
serialized = json.dumps(data, ensure_ascii=False)
|
||||
assert "/Users/ogt" not in serialized
|
||||
assert ".claude/projects" not in serialized
|
||||
assert {item["gate_id"] for item in data["gate_cards"]} >= {
|
||||
"status_cleanup_preflight",
|
||||
"owner_review_package",
|
||||
"owner_response_preflight",
|
||||
"execution_plan",
|
||||
"apply_gate",
|
||||
}
|
||||
|
||||
|
||||
def test_awoooi_status_cleanup_dashboard_rejects_memory_write_authorization(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["memory_write_authorized"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="dashboard boundaries"):
|
||||
load_latest_awoooi_status_cleanup_dashboard(tmp_path)
|
||||
|
||||
|
||||
def test_awoooi_status_cleanup_dashboard_rejects_wazuh_live_query(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["wazuh_handoff"]["wazuh_api_live_query_authorized"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="Wazuh live query"):
|
||||
load_latest_awoooi_status_cleanup_dashboard(tmp_path)
|
||||
|
||||
|
||||
def test_awoooi_status_cleanup_dashboard_rejects_live_metadata_gate(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["summary"]["wazuh_live_metadata_owner_count"] = 1
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="Wazuh release summary gates"):
|
||||
load_latest_awoooi_status_cleanup_dashboard(tmp_path)
|
||||
|
||||
|
||||
def test_awoooi_status_cleanup_dashboard_rejects_missing_risk_control(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["risk_controls"] = [
|
||||
item for item in snapshot["risk_controls"] if item["control_id"] != "host"
|
||||
]
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="risk_controls"):
|
||||
load_latest_awoooi_status_cleanup_dashboard(tmp_path)
|
||||
|
||||
|
||||
def _snapshot() -> dict:
|
||||
return copy.deepcopy(load_latest_awoooi_status_cleanup_dashboard())
|
||||
|
||||
|
||||
def _write_snapshot(tmp_path, snapshot: dict) -> None:
|
||||
(tmp_path / "awoooi-status-cleanup-dashboard.snapshot.json").write_text(
|
||||
json.dumps(snapshot),
|
||||
encoding="utf-8",
|
||||
)
|
||||
25
apps/api/tests/test_awoooi_status_cleanup_dashboard_api.py
Normal file
25
apps/api/tests/test_awoooi_status_cleanup_dashboard_api.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.api.v1.agents import router
|
||||
|
||||
|
||||
def test_awoooi_status_cleanup_dashboard_endpoint_returns_snapshot():
|
||||
app = FastAPI()
|
||||
app.include_router(router, prefix="/api/v1")
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/api/v1/agents/awoooi-status-cleanup-dashboard")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["schema_version"] == "awoooi_status_cleanup_dashboard_v1"
|
||||
assert data["summary"]["dashboard_status"] == "blocked_status_cleanup_apply_not_authorized"
|
||||
assert data["summary"]["gate_count"] == 5
|
||||
assert data["summary"]["apply_allowed"] is False
|
||||
assert data["summary"]["memory_write_authorized"] is False
|
||||
assert data["summary"]["wazuh_api_live_query_authorized"] is False
|
||||
assert data["memory_write_authorized"] is False
|
||||
assert data["runtime_execution_authorized"] is False
|
||||
@@ -329,6 +329,11 @@ export const apiClient = {
|
||||
return handleResponse<AiAgentDeploymentLayoutSnapshot>(res)
|
||||
},
|
||||
|
||||
async getAwoooIStatusCleanupDashboard() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/awoooi-status-cleanup-dashboard`)
|
||||
return handleResponse<AwoooIStatusCleanupDashboardSnapshot>(res)
|
||||
},
|
||||
|
||||
async getAiAgent12AgentWarRoom() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/agent-12-agent-war-room`)
|
||||
return handleResponse<AiAgent12AgentWarRoomSnapshot>(res)
|
||||
@@ -1349,6 +1354,43 @@ export interface AiAgentDeploymentLayoutSnapshot {
|
||||
>
|
||||
}
|
||||
|
||||
export interface AwoooIStatusCleanupDashboardSnapshot {
|
||||
schema_version: 'awoooi_status_cleanup_dashboard_v1'
|
||||
generated_at: string
|
||||
target_route: '/workspace/status-cleanup'
|
||||
summary: {
|
||||
dashboard_status: string
|
||||
overall_completion_percent: number
|
||||
gate_count: number
|
||||
blocked_gate_count: number
|
||||
accepted_owner_flag_count: number
|
||||
required_owner_flag_count: number
|
||||
apply_allowed: false
|
||||
memory_write_authorized: false
|
||||
wazuh_api_live_query_authorized: false
|
||||
runtime_execution_authorized: false
|
||||
ui_implementation_allowed: false
|
||||
[key: string]: unknown
|
||||
}
|
||||
metric_cards: Array<Record<string, unknown>>
|
||||
gate_cards: Array<Record<string, unknown>>
|
||||
workflow_rows: Array<Record<string, unknown>>
|
||||
risk_controls: Array<Record<string, unknown>>
|
||||
blocking_reasons: string[]
|
||||
next_actions: string[]
|
||||
wazuh_handoff: Record<string, unknown>
|
||||
hard_gates: string[]
|
||||
operation_boundaries: Record<string, false>
|
||||
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
|
||||
}
|
||||
|
||||
export interface AiAgent12AgentWarRoomSnapshot {
|
||||
schema_version: 'ai_agent_12_agent_war_room_v1'
|
||||
generated_at: string
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
## 2026-06-24|Status Cleanup Dashboard 繁中化與 read-only API 切片
|
||||
|
||||
**本輪收斂範圍:**
|
||||
- 新增 AWOOOI Status Cleanup Dashboard read-only service / schema / snapshot / Markdown / generator,將狀態清理完成度、owner gate、apply gate、artifact sync 與 Wazuh handoff 邊界整理成單一資料模型。
|
||||
- 人類可讀標題、卡片 caption、Markdown 區塊標題與 risk control 顯示文字使用繁體中文;保留 `metric_id`、`gate_id`、API status、schema key 等技術識別碼。
|
||||
- 修正 Wazuh visibility boundary:將 `manager_agent_registry_readback_passed=false`、`iwooos_live_route_readback_passed=false`、`dashboard_agent_list_recovered=false` 與 `agent_visibility_runtime_gate_count=0` 寫入 boundary,讓 read-model guard、報告文字與 API payload 對齊。
|
||||
- 新增 `GET /api/v1/agents/awoooi-status-cleanup-dashboard` 與 `getAwoooIStatusCleanupDashboard()`;端點回傳前會走既有 public LAN topology redaction。
|
||||
|
||||
**readback:**
|
||||
- Status Cleanup Dashboard:`AWOOOI_STATUS_CLEANUP_DASHBOARD_OK status=blocked_status_cleanup_apply_not_authorized gates=5/5 owner_flags=0/6 apply_allowed=False memory_write=False`。
|
||||
- API / read-model tests:`PYTHONPATH=apps/api PYTHONDONTWRITEBYTECODE=1 apps/api/.venv/bin/python -m pytest apps/api/tests/test_awoooi_status_cleanup_dashboard.py apps/api/tests/test_awoooi_status_cleanup_dashboard_api.py -q` → `6 passed`。
|
||||
|
||||
**仍禁止 / 未完成:**
|
||||
- 整體治理完成度維持 `41.9%`;Status Cleanup 仍 `gates=5/5 blocked`、`owner_flags=0/6`、`apply_allowed=false`、`memory_write_authorized=false`。
|
||||
- 本輪沒有更新 `project_current_status` / memory,沒有 Wazuh live query / active response / runtime deploy,沒有 host、Nginx、Docker、K8s、firewall、workflow、repo refs、backup、restore 或 migration 操作。
|
||||
- 本輪只交付 read-only API / client / report model,未宣稱可視 UI 已恢復或正式展示完成。
|
||||
|
||||
## 2026-06-24|23:15 110 cold-start monitor live-sync gate readback
|
||||
|
||||
**背景**:23:04 已把 MOMO source absence classifier 納入 repo-side cold-start v1.42,但這不等於 110 上 `/home/wooo/scripts/full-stack-cold-start-check.sh` 已更新。為避免下次重啟時 operator 以 live 110 舊腳本輸出做錯判,本輪只做部署 parity 的 read-only 驗證與 SOP gate 補強。
|
||||
|
||||
121
docs/operations/AWOOOI-STATUS-CLEANUP-DASHBOARD-2026-06-24.md
Normal file
121
docs/operations/AWOOOI-STATUS-CLEANUP-DASHBOARD-2026-06-24.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# AWOOOI 狀態清理儀表板
|
||||
|
||||
- generated_at: `2026-06-24T15:39:52+00:00`
|
||||
- dashboard_status: `blocked_status_cleanup_apply_not_authorized`
|
||||
- overall_completion_percent: `41.9%`
|
||||
- gates: `5/5 blocked`
|
||||
- owner_flags: `0/6`
|
||||
- sections: `0/7`
|
||||
- confirmed_commands: `0/1`
|
||||
- artifact_count: `47`
|
||||
- wazuh_handoff: `blocked_not_released base=b540fc0c patches=7`
|
||||
- wazuh_live_metadata: `owner=0 secret_metadata=0`
|
||||
- wazuh_live_agent_registry_readback: `0`
|
||||
- iwooos_wazuh_runtime_gate: `0`
|
||||
- wazuh_active_response_count: `0`
|
||||
- wazuh_agent_visibility_status: `blocked_waiting_manager_agent_registry_readback`
|
||||
- memory_write_authorized: `False`
|
||||
- runtime_execution_authorized: `False`
|
||||
- wazuh_api_live_query_authorized: `False`
|
||||
|
||||
## 指標卡片
|
||||
|
||||
| 指標 | 數值 | 狀態 | 說明 |
|
||||
|--------|-------|--------|---------|
|
||||
| `overall_completion` | `41.9%` | `orange` | 產品 Runtime 治理完成度計分卡 |
|
||||
| `project_status_age` | `11 days` | `blocked` | stale_after_days=2 |
|
||||
| `owner_flags` | `0/6` | `blocked` | pending_owner_response |
|
||||
| `approved_sections` | `0/7` | `blocked` | targets=0/1 |
|
||||
| `execution_preview` | `blocked_owner_response_preflight_not_ready` | `blocked` | dry_run_only=true |
|
||||
| `apply_confirmation` | `0/1` | `blocked` | pending_final_confirmation |
|
||||
| `artifact_sync` | `1/2` | `blocked` | artifacts=47 |
|
||||
|
||||
## 閘門卡片
|
||||
|
||||
| 閘門 | 狀態 | 是否封鎖 | 證據 |
|
||||
|------|------|----------|------|
|
||||
| `status_cleanup_preflight` | `blocked_status_cleanup_required` | `True` | `docs/operations/codex-status-cleanup-preflight.snapshot.json` |
|
||||
| `owner_review_package` | `owner_decision_required` | `True` | `docs/operations/codex-status-cleanup-owner-review-package.snapshot.json` |
|
||||
| `owner_response_preflight` | `blocked_owner_response_required` | `True` | `docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json` |
|
||||
| `execution_plan` | `blocked_owner_response_preflight_not_ready` | `True` | `docs/operations/codex-status-cleanup-execution-plan.snapshot.json` |
|
||||
| `apply_gate` | `blocked_status_cleanup_apply_not_authorized` | `True` | `docs/operations/codex-status-cleanup-apply-gate.snapshot.json` |
|
||||
|
||||
## 風險控制
|
||||
|
||||
| 控制項 | 狀態 | 已授權 |
|
||||
|--------|------|--------|
|
||||
| `memory_write` | `blocked_final_apply_gate_required` | `False` |
|
||||
| `raw_codex_history` | `blocked_raw_history_never_synced` | `False` |
|
||||
| `wazuh_runtime` | `blocked_iwooos_runtime_lane_only` | `False` |
|
||||
| `workflow` | `blocked_workflow_authorization_required` | `False` |
|
||||
| `repo_refs` | `blocked_source_control_approval_required` | `False` |
|
||||
| `host` | `blocked_host_maintenance_required` | `False` |
|
||||
| `backup_restore` | `blocked_data_owner_approval_required` | `False` |
|
||||
| `ui` | `blocked_apps_web_readiness_required` | `False` |
|
||||
|
||||
## 下一步
|
||||
|
||||
- 收齊 owner 旗標:0/6。
|
||||
- 收齊已批准章節:0/7。
|
||||
- 確認目標路徑:0/1。
|
||||
- 確認邊界 acknowledgement:0/5。
|
||||
- owner response preflight ready 後,重新產生 dry-run execution plan。
|
||||
- apply gate 目前 apply_allowed=False;未變 true 前不得寫 project_current_status。
|
||||
|
||||
## 封鎖原因
|
||||
|
||||
- `project_current_status 過期只代表需要狀態清理,不代表可修改 memory / runtime。`
|
||||
- `不得把一般『批准繼續』解讀成 force push、workflow copy、workflow trigger、repo creation 或 refs sync 授權。`
|
||||
- `不得同步 raw Codex App DB / auth / conversations / sessions。`
|
||||
- `不得碰 MacBook Pro MOMO PRO protected legacy paths。`
|
||||
- `不得新增 Wazuh / Kali / SOC runtime UI/API 或 live query。`
|
||||
- `不得 SSH 修改主機、改 firewall / Nginx / Docker / K8s 或執行 active scan。`
|
||||
- `Data / Backup coverage 是治理 evidence,不是 backup / restore / migration executor。`
|
||||
- `owner_response_preflight:owner_response_status_not_approved_status_cleanup_preview`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:accept_status_cleanup_scope`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:approve_project_current_status_update_preview`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_latest_logbook_and_scorecard_as_sources`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_no_runtime_or_host_authorization`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_no_raw_codex_history_sync`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_iwooos_wazuh_boundary_preserved`
|
||||
- `owner_response_preflight:update_section_not_approved:artifact_sync_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:completion_scorecard`
|
||||
- `owner_response_preflight:update_section_not_approved:data_backup_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:gitea_warning_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:iwooos_wazuh_boundary`
|
||||
- `owner_response_preflight:update_section_not_approved:latest_logbook_heading`
|
||||
- `owner_response_preflight:update_section_not_approved:operation_boundaries`
|
||||
- `owner_response_preflight:target_path_not_approved:awoooi_memory/project_current_status.md`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:memory_write_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:refs_sync_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:repo_creation_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:runtime_execution_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:wazuh_api_live_query_authorized=false`
|
||||
- `execution_plan_blocked_until:owner response preflight ready_for_status_cleanup_apply_gate=true`
|
||||
- `execution_plan_blocked_until:all owner flags accepted`
|
||||
- `execution_plan_blocked_until:all update sections approved`
|
||||
- `execution_plan_blocked_until:target path approved`
|
||||
- `execution_plan_blocked_until:all critical false boundaries acknowledged`
|
||||
- `execution_plan_blocked_until:final status cleanup apply confirmation accepted`
|
||||
- `apply_gate:execution_plan_not_ready_for_final_confirmation`
|
||||
- `apply_gate:owner_response_preflight_not_ready`
|
||||
- `apply_gate:final_confirmation_status_not_approved_status_cleanup_apply`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_status_cleanup_command_preview`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_project_current_status_target_path`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_post_apply_validation`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_no_runtime_or_wazuh_deploy`
|
||||
- `apply_gate:command_preview_not_confirmed`
|
||||
- `apply_gate:target_path_not_confirmed:awoooi_memory/project_current_status.md`
|
||||
- `wazuh_boundary:Wazuh route / production 404 由另一受控 branch 處理;branch=codex/iwooos-wazuh-boundary-guard-20260624 base=b540fc0c commits=38dc3c2f,9a53d3e1,e9972d47,758d419e,04db4b8a,8eec298e,325f262a patch_sha_1=08f8b36d7261b0dde6bfb0c47597bd0727d578dec3335c5ff7ded2bcaa2b7eb4 patch_sha_2=e6ec8f8d10e8a2bd711c399fa14ba0ab2dfb22f8ab6a733402944302eec7da7c patch_sha_3=7e99bd5284a25519313aea05bb314d3386454b91ce86241424385752d358900d patch_sha_4=f4ffbaecd94d3696660766cc6f4a6bd195762bc533d9502f8edfed2bb8379fab patch_sha_5=9035d6c411bf86d0857970b69dd33631f052aa90de27e52d82d448d4b8e4cec5 patch_sha_6=d3bb98711a3ebf91b9936b41bc232b689befc68a4a7cec38bf9cab4c8d015827 patch_sha_7=5aa3e69fee9624d0ff3f2bfad90595a81eb9306ad6387d640690a85a2f8038d7 apply_proof=release_apply_check_20260624_2248 release_gate=source1_push0_deploy0_readback0_runtime0 release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0;wazuh_live_agent_registry_readback=0 manager_agent_registry_readback_passed=false iwooos_live_route_readback_passed=false dashboard_agent_list_recovered=false iwooos_wazuh_runtime_gate=0 active_response=0;push_blocked=missing_noninteractive_gitea_https_credential;本視窗不改 runtime / Nginx / Docker / K8s / firewall / Wazuh secret。 agent_visibility_status=blocked_waiting_manager_agent_registry_readback agent_visibility_runtime_gate_count=0`
|
||||
|
||||
## 強制閘門
|
||||
|
||||
- 本 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 實作授權。
|
||||
121
docs/operations/AWOOOI-STATUS-CLEANUP-DASHBOARD.md
Normal file
121
docs/operations/AWOOOI-STATUS-CLEANUP-DASHBOARD.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# AWOOOI 狀態清理儀表板
|
||||
|
||||
- generated_at: `2026-06-24T15:39:52+00:00`
|
||||
- dashboard_status: `blocked_status_cleanup_apply_not_authorized`
|
||||
- overall_completion_percent: `41.9%`
|
||||
- gates: `5/5 blocked`
|
||||
- owner_flags: `0/6`
|
||||
- sections: `0/7`
|
||||
- confirmed_commands: `0/1`
|
||||
- artifact_count: `47`
|
||||
- wazuh_handoff: `blocked_not_released base=b540fc0c patches=7`
|
||||
- wazuh_live_metadata: `owner=0 secret_metadata=0`
|
||||
- wazuh_live_agent_registry_readback: `0`
|
||||
- iwooos_wazuh_runtime_gate: `0`
|
||||
- wazuh_active_response_count: `0`
|
||||
- wazuh_agent_visibility_status: `blocked_waiting_manager_agent_registry_readback`
|
||||
- memory_write_authorized: `False`
|
||||
- runtime_execution_authorized: `False`
|
||||
- wazuh_api_live_query_authorized: `False`
|
||||
|
||||
## 指標卡片
|
||||
|
||||
| 指標 | 數值 | 狀態 | 說明 |
|
||||
|--------|-------|--------|---------|
|
||||
| `overall_completion` | `41.9%` | `orange` | 產品 Runtime 治理完成度計分卡 |
|
||||
| `project_status_age` | `11 days` | `blocked` | stale_after_days=2 |
|
||||
| `owner_flags` | `0/6` | `blocked` | pending_owner_response |
|
||||
| `approved_sections` | `0/7` | `blocked` | targets=0/1 |
|
||||
| `execution_preview` | `blocked_owner_response_preflight_not_ready` | `blocked` | dry_run_only=true |
|
||||
| `apply_confirmation` | `0/1` | `blocked` | pending_final_confirmation |
|
||||
| `artifact_sync` | `1/2` | `blocked` | artifacts=47 |
|
||||
|
||||
## 閘門卡片
|
||||
|
||||
| 閘門 | 狀態 | 是否封鎖 | 證據 |
|
||||
|------|------|----------|------|
|
||||
| `status_cleanup_preflight` | `blocked_status_cleanup_required` | `True` | `docs/operations/codex-status-cleanup-preflight.snapshot.json` |
|
||||
| `owner_review_package` | `owner_decision_required` | `True` | `docs/operations/codex-status-cleanup-owner-review-package.snapshot.json` |
|
||||
| `owner_response_preflight` | `blocked_owner_response_required` | `True` | `docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json` |
|
||||
| `execution_plan` | `blocked_owner_response_preflight_not_ready` | `True` | `docs/operations/codex-status-cleanup-execution-plan.snapshot.json` |
|
||||
| `apply_gate` | `blocked_status_cleanup_apply_not_authorized` | `True` | `docs/operations/codex-status-cleanup-apply-gate.snapshot.json` |
|
||||
|
||||
## 風險控制
|
||||
|
||||
| 控制項 | 狀態 | 已授權 |
|
||||
|--------|------|--------|
|
||||
| `memory_write` | `blocked_final_apply_gate_required` | `False` |
|
||||
| `raw_codex_history` | `blocked_raw_history_never_synced` | `False` |
|
||||
| `wazuh_runtime` | `blocked_iwooos_runtime_lane_only` | `False` |
|
||||
| `workflow` | `blocked_workflow_authorization_required` | `False` |
|
||||
| `repo_refs` | `blocked_source_control_approval_required` | `False` |
|
||||
| `host` | `blocked_host_maintenance_required` | `False` |
|
||||
| `backup_restore` | `blocked_data_owner_approval_required` | `False` |
|
||||
| `ui` | `blocked_apps_web_readiness_required` | `False` |
|
||||
|
||||
## 下一步
|
||||
|
||||
- 收齊 owner 旗標:0/6。
|
||||
- 收齊已批准章節:0/7。
|
||||
- 確認目標路徑:0/1。
|
||||
- 確認邊界 acknowledgement:0/5。
|
||||
- owner response preflight ready 後,重新產生 dry-run execution plan。
|
||||
- apply gate 目前 apply_allowed=False;未變 true 前不得寫 project_current_status。
|
||||
|
||||
## 封鎖原因
|
||||
|
||||
- `project_current_status 過期只代表需要狀態清理,不代表可修改 memory / runtime。`
|
||||
- `不得把一般『批准繼續』解讀成 force push、workflow copy、workflow trigger、repo creation 或 refs sync 授權。`
|
||||
- `不得同步 raw Codex App DB / auth / conversations / sessions。`
|
||||
- `不得碰 MacBook Pro MOMO PRO protected legacy paths。`
|
||||
- `不得新增 Wazuh / Kali / SOC runtime UI/API 或 live query。`
|
||||
- `不得 SSH 修改主機、改 firewall / Nginx / Docker / K8s 或執行 active scan。`
|
||||
- `Data / Backup coverage 是治理 evidence,不是 backup / restore / migration executor。`
|
||||
- `owner_response_preflight:owner_response_status_not_approved_status_cleanup_preview`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:accept_status_cleanup_scope`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:approve_project_current_status_update_preview`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_latest_logbook_and_scorecard_as_sources`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_no_runtime_or_host_authorization`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_no_raw_codex_history_sync`
|
||||
- `owner_response_preflight:owner_flag_not_accepted:confirm_iwooos_wazuh_boundary_preserved`
|
||||
- `owner_response_preflight:update_section_not_approved:artifact_sync_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:completion_scorecard`
|
||||
- `owner_response_preflight:update_section_not_approved:data_backup_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:gitea_warning_gate`
|
||||
- `owner_response_preflight:update_section_not_approved:iwooos_wazuh_boundary`
|
||||
- `owner_response_preflight:update_section_not_approved:latest_logbook_heading`
|
||||
- `owner_response_preflight:update_section_not_approved:operation_boundaries`
|
||||
- `owner_response_preflight:target_path_not_approved:awoooi_memory/project_current_status.md`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:memory_write_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:refs_sync_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:repo_creation_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:runtime_execution_authorized=false`
|
||||
- `owner_response_preflight:boundary_not_acknowledged:wazuh_api_live_query_authorized=false`
|
||||
- `execution_plan_blocked_until:owner response preflight ready_for_status_cleanup_apply_gate=true`
|
||||
- `execution_plan_blocked_until:all owner flags accepted`
|
||||
- `execution_plan_blocked_until:all update sections approved`
|
||||
- `execution_plan_blocked_until:target path approved`
|
||||
- `execution_plan_blocked_until:all critical false boundaries acknowledged`
|
||||
- `execution_plan_blocked_until:final status cleanup apply confirmation accepted`
|
||||
- `apply_gate:execution_plan_not_ready_for_final_confirmation`
|
||||
- `apply_gate:owner_response_preflight_not_ready`
|
||||
- `apply_gate:final_confirmation_status_not_approved_status_cleanup_apply`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_status_cleanup_command_preview`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_project_current_status_target_path`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_post_apply_validation`
|
||||
- `apply_gate:final_flag_not_accepted:confirm_no_runtime_or_wazuh_deploy`
|
||||
- `apply_gate:command_preview_not_confirmed`
|
||||
- `apply_gate:target_path_not_confirmed:awoooi_memory/project_current_status.md`
|
||||
- `wazuh_boundary:Wazuh route / production 404 由另一受控 branch 處理;branch=codex/iwooos-wazuh-boundary-guard-20260624 base=b540fc0c commits=38dc3c2f,9a53d3e1,e9972d47,758d419e,04db4b8a,8eec298e,325f262a patch_sha_1=08f8b36d7261b0dde6bfb0c47597bd0727d578dec3335c5ff7ded2bcaa2b7eb4 patch_sha_2=e6ec8f8d10e8a2bd711c399fa14ba0ab2dfb22f8ab6a733402944302eec7da7c patch_sha_3=7e99bd5284a25519313aea05bb314d3386454b91ce86241424385752d358900d patch_sha_4=f4ffbaecd94d3696660766cc6f4a6bd195762bc533d9502f8edfed2bb8379fab patch_sha_5=9035d6c411bf86d0857970b69dd33631f052aa90de27e52d82d448d4b8e4cec5 patch_sha_6=d3bb98711a3ebf91b9936b41bc232b689befc68a4a7cec38bf9cab4c8d015827 patch_sha_7=5aa3e69fee9624d0ff3f2bfad90595a81eb9306ad6387d640690a85a2f8038d7 apply_proof=release_apply_check_20260624_2248 release_gate=source1_push0_deploy0_readback0_runtime0 release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0;wazuh_live_agent_registry_readback=0 manager_agent_registry_readback_passed=false iwooos_live_route_readback_passed=false dashboard_agent_list_recovered=false iwooos_wazuh_runtime_gate=0 active_response=0;push_blocked=missing_noninteractive_gitea_https_credential;本視窗不改 runtime / Nginx / Docker / K8s / firewall / Wazuh secret。 agent_visibility_status=blocked_waiting_manager_agent_registry_readback agent_visibility_runtime_gate_count=0`
|
||||
|
||||
## 強制閘門
|
||||
|
||||
- 本 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 實作授權。
|
||||
399
docs/operations/awoooi-status-cleanup-dashboard.snapshot.json
Normal file
399
docs/operations/awoooi-status-cleanup-dashboard.snapshot.json
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"schema_version": "awoooi_status_cleanup_dashboard_v1",
|
||||
"generated_at": "2026-06-24T15:39:52+00:00",
|
||||
"target_route": "/workspace/status-cleanup",
|
||||
"source_reviews": {
|
||||
"status_cleanup_preflight": "docs/operations/codex-status-cleanup-preflight.snapshot.json",
|
||||
"owner_review_package": "docs/operations/codex-status-cleanup-owner-review-package.snapshot.json",
|
||||
"owner_response_preflight": "docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json",
|
||||
"execution_plan": "docs/operations/codex-status-cleanup-execution-plan.snapshot.json",
|
||||
"apply_gate": "docs/operations/codex-status-cleanup-apply-gate.snapshot.json",
|
||||
"artifact_sync_readback": "docs/operations/codex-workstation-artifact-sync-readback.snapshot.json",
|
||||
"scorecard": "docs/operations/product-runtime-governance-completion-scorecard.snapshot.json",
|
||||
"iwooos_wazuh_release_handoff": "docs/operations/iwooos-wazuh-release-handoff.snapshot.json"
|
||||
},
|
||||
"summary": {
|
||||
"dashboard_status": "blocked_status_cleanup_apply_not_authorized",
|
||||
"overall_completion_percent": 41.9,
|
||||
"p0_009_completion_percent": 87,
|
||||
"p0_010_completion_percent": 50,
|
||||
"p1_006_completion_percent": 83,
|
||||
"p2_001_completion_percent": 41,
|
||||
"p2_002_completion_percent": 72,
|
||||
"gate_count": 5,
|
||||
"blocked_gate_count": 5,
|
||||
"project_current_status_age_days": 11,
|
||||
"cleanup_required": true,
|
||||
"accepted_owner_flag_count": 0,
|
||||
"required_owner_flag_count": 6,
|
||||
"approved_update_section_count": 0,
|
||||
"required_update_section_count": 7,
|
||||
"approved_target_path_count": 0,
|
||||
"target_path_count": 1,
|
||||
"boundary_ack_count": 0,
|
||||
"required_boundary_ack_count": 5,
|
||||
"execution_plan_status": "blocked_owner_response_preflight_not_ready",
|
||||
"apply_gate_status": "blocked_status_cleanup_apply_not_authorized",
|
||||
"confirmed_command_count": 0,
|
||||
"planned_command_count": 1,
|
||||
"artifact_count": 47,
|
||||
"artifact_sync_blocked_count": 1,
|
||||
"wazuh_handoff_status": "blocked_not_released",
|
||||
"wazuh_handoff_base_commit": "b540fc0c",
|
||||
"wazuh_handoff_commit_count": 7,
|
||||
"wazuh_handoff_patch_count": 7,
|
||||
"wazuh_release_owner_request_sent_count": 0,
|
||||
"wazuh_release_owner_response_accepted_count": 0,
|
||||
"wazuh_acknowledged_release_owner_ack_count": 0,
|
||||
"wazuh_required_release_owner_ack_count": 6,
|
||||
"wazuh_accepted_evidence_count": 0,
|
||||
"wazuh_required_evidence_count": 6,
|
||||
"wazuh_live_metadata_owner_count": 0,
|
||||
"wazuh_secret_metadata_count": 0,
|
||||
"wazuh_live_agent_registry_readback": 0,
|
||||
"iwooos_wazuh_runtime_gate": 0,
|
||||
"wazuh_active_response_count": 0,
|
||||
"wazuh_agent_visibility_status": "blocked_waiting_manager_agent_registry_readback",
|
||||
"wazuh_manager_agent_registry_readback_passed": false,
|
||||
"wazuh_iwooos_live_route_readback_passed": false,
|
||||
"wazuh_dashboard_agent_list_recovered": false,
|
||||
"wazuh_agent_visibility_runtime_gate_count": 0,
|
||||
"wazuh_push_gate_count": 0,
|
||||
"wazuh_deploy_gate_count": 0,
|
||||
"wazuh_readback_gate_count": 0,
|
||||
"wazuh_runtime_gate_count": 0,
|
||||
"blocking_reason_count": 43,
|
||||
"next_action_count": 6,
|
||||
"apply_allowed": false,
|
||||
"memory_write_authorized": false,
|
||||
"wazuh_api_live_query_authorized": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"ui_implementation_allowed": false
|
||||
},
|
||||
"metric_cards": [
|
||||
{
|
||||
"metric_id": "overall_completion",
|
||||
"title": "整體治理完成度",
|
||||
"value": "41.9%",
|
||||
"status": "orange",
|
||||
"caption": "產品 Runtime 治理完成度計分卡"
|
||||
},
|
||||
{
|
||||
"metric_id": "project_status_age",
|
||||
"title": "project_current_status 年齡",
|
||||
"value": "11 days",
|
||||
"status": "blocked",
|
||||
"caption": "stale_after_days=2"
|
||||
},
|
||||
{
|
||||
"metric_id": "owner_flags",
|
||||
"title": "Owner 旗標",
|
||||
"value": "0/6",
|
||||
"status": "blocked",
|
||||
"caption": "pending_owner_response"
|
||||
},
|
||||
{
|
||||
"metric_id": "approved_sections",
|
||||
"title": "已批准章節",
|
||||
"value": "0/7",
|
||||
"status": "blocked",
|
||||
"caption": "targets=0/1"
|
||||
},
|
||||
{
|
||||
"metric_id": "execution_preview",
|
||||
"title": "執行預覽",
|
||||
"value": "blocked_owner_response_preflight_not_ready",
|
||||
"status": "blocked",
|
||||
"caption": "dry_run_only=true"
|
||||
},
|
||||
{
|
||||
"metric_id": "apply_confirmation",
|
||||
"title": "套用確認",
|
||||
"value": "0/1",
|
||||
"status": "blocked",
|
||||
"caption": "pending_final_confirmation"
|
||||
},
|
||||
{
|
||||
"metric_id": "artifact_sync",
|
||||
"title": "安全交接 artifacts",
|
||||
"value": "1/2",
|
||||
"status": "blocked",
|
||||
"caption": "artifacts=47"
|
||||
}
|
||||
],
|
||||
"gate_cards": [
|
||||
{
|
||||
"gate_id": "status_cleanup_preflight",
|
||||
"title": "狀態清理預檢",
|
||||
"status": "blocked_status_cleanup_required",
|
||||
"completion_label": "age_days=11 stale_after=2",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-preflight.snapshot.json",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"gate_id": "owner_review_package",
|
||||
"title": "Owner 審核包",
|
||||
"status": "owner_decision_required",
|
||||
"completion_label": "7 sections / 6 owner flags",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-review-package.snapshot.json",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"gate_id": "owner_response_preflight",
|
||||
"title": "Owner 回覆預檢",
|
||||
"status": "blocked_owner_response_required",
|
||||
"completion_label": "0/6 flags, 0/7 sections",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"gate_id": "execution_plan",
|
||||
"title": "Dry-run 執行計畫",
|
||||
"status": "blocked_owner_response_preflight_not_ready",
|
||||
"completion_label": "0/7 sections, 0/1 targets",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-execution-plan.snapshot.json",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"gate_id": "apply_gate",
|
||||
"title": "最終套用閘門",
|
||||
"status": "blocked_status_cleanup_apply_not_authorized",
|
||||
"completion_label": "0/1 commands, 0/1 targets",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-apply-gate.snapshot.json",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
}
|
||||
],
|
||||
"workflow_rows": [
|
||||
{
|
||||
"step_id": "status_cleanup_preflight",
|
||||
"title": "狀態清理預檢",
|
||||
"status": "blocked_status_cleanup_required",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-preflight.snapshot.json",
|
||||
"next_step": "owner_review_package",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"step_id": "owner_review_package",
|
||||
"title": "Owner 審核包",
|
||||
"status": "owner_decision_required",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-review-package.snapshot.json",
|
||||
"next_step": "owner_response_preflight",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"step_id": "owner_response_preflight",
|
||||
"title": "Owner 回覆預檢",
|
||||
"status": "blocked_owner_response_required",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-owner-response-preflight.snapshot.json",
|
||||
"next_step": "execution_plan",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"step_id": "execution_plan",
|
||||
"title": "Dry-run 執行計畫",
|
||||
"status": "blocked_owner_response_preflight_not_ready",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-execution-plan.snapshot.json",
|
||||
"next_step": "apply_gate",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
},
|
||||
{
|
||||
"step_id": "apply_gate",
|
||||
"title": "最終套用閘門",
|
||||
"status": "blocked_status_cleanup_apply_not_authorized",
|
||||
"evidence_ref": "docs/operations/codex-status-cleanup-apply-gate.snapshot.json",
|
||||
"next_step": "manual_project_current_status_update_after_explicit_final_confirmation",
|
||||
"blocked": true,
|
||||
"memory_write_authorized": false,
|
||||
"execution_authorized": false
|
||||
}
|
||||
],
|
||||
"risk_controls": [
|
||||
{
|
||||
"control_id": "memory_write",
|
||||
"title": "更新 project_current_status / memory",
|
||||
"status": "blocked_final_apply_gate_required",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "raw_codex_history",
|
||||
"title": "同步 raw Codex App history",
|
||||
"status": "blocked_raw_history_never_synced",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "wazuh_runtime",
|
||||
"title": "部署或查詢 live Wazuh runtime",
|
||||
"status": "blocked_iwooos_runtime_lane_only",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "workflow",
|
||||
"title": "修改或觸發 CI/CD workflow",
|
||||
"status": "blocked_workflow_authorization_required",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "repo_refs",
|
||||
"title": "建立 repo / branch / mirror refs",
|
||||
"status": "blocked_source_control_approval_required",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "host",
|
||||
"title": "SSH / restart / firewall / Nginx / Docker / K8s",
|
||||
"status": "blocked_host_maintenance_required",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "backup_restore",
|
||||
"title": "執行 backup / restore / migration",
|
||||
"status": "blocked_data_owner_approval_required",
|
||||
"authorized": false
|
||||
},
|
||||
{
|
||||
"control_id": "ui",
|
||||
"title": "實作可見 AWOOOI UI",
|
||||
"status": "blocked_apps_web_readiness_required",
|
||||
"authorized": false
|
||||
}
|
||||
],
|
||||
"blocking_reasons": [
|
||||
"project_current_status 過期只代表需要狀態清理,不代表可修改 memory / runtime。",
|
||||
"不得把一般『批准繼續』解讀成 force push、workflow copy、workflow trigger、repo creation 或 refs sync 授權。",
|
||||
"不得同步 raw Codex App DB / auth / conversations / sessions。",
|
||||
"不得碰 MacBook Pro MOMO PRO protected legacy paths。",
|
||||
"不得新增 Wazuh / Kali / SOC runtime UI/API 或 live query。",
|
||||
"不得 SSH 修改主機、改 firewall / Nginx / Docker / K8s 或執行 active scan。",
|
||||
"Data / Backup coverage 是治理 evidence,不是 backup / restore / migration executor。",
|
||||
"owner_response_preflight:owner_response_status_not_approved_status_cleanup_preview",
|
||||
"owner_response_preflight:owner_flag_not_accepted:accept_status_cleanup_scope",
|
||||
"owner_response_preflight:owner_flag_not_accepted:approve_project_current_status_update_preview",
|
||||
"owner_response_preflight:owner_flag_not_accepted:confirm_latest_logbook_and_scorecard_as_sources",
|
||||
"owner_response_preflight:owner_flag_not_accepted:confirm_no_runtime_or_host_authorization",
|
||||
"owner_response_preflight:owner_flag_not_accepted:confirm_no_raw_codex_history_sync",
|
||||
"owner_response_preflight:owner_flag_not_accepted:confirm_iwooos_wazuh_boundary_preserved",
|
||||
"owner_response_preflight:update_section_not_approved:artifact_sync_gate",
|
||||
"owner_response_preflight:update_section_not_approved:completion_scorecard",
|
||||
"owner_response_preflight:update_section_not_approved:data_backup_gate",
|
||||
"owner_response_preflight:update_section_not_approved:gitea_warning_gate",
|
||||
"owner_response_preflight:update_section_not_approved:iwooos_wazuh_boundary",
|
||||
"owner_response_preflight:update_section_not_approved:latest_logbook_heading",
|
||||
"owner_response_preflight:update_section_not_approved:operation_boundaries",
|
||||
"owner_response_preflight:target_path_not_approved:awoooi_memory/project_current_status.md",
|
||||
"owner_response_preflight:boundary_not_acknowledged:memory_write_authorized=false",
|
||||
"owner_response_preflight:boundary_not_acknowledged:refs_sync_authorized=false",
|
||||
"owner_response_preflight:boundary_not_acknowledged:repo_creation_authorized=false",
|
||||
"owner_response_preflight:boundary_not_acknowledged:runtime_execution_authorized=false",
|
||||
"owner_response_preflight:boundary_not_acknowledged:wazuh_api_live_query_authorized=false",
|
||||
"execution_plan_blocked_until:owner response preflight ready_for_status_cleanup_apply_gate=true",
|
||||
"execution_plan_blocked_until:all owner flags accepted",
|
||||
"execution_plan_blocked_until:all update sections approved",
|
||||
"execution_plan_blocked_until:target path approved",
|
||||
"execution_plan_blocked_until:all critical false boundaries acknowledged",
|
||||
"execution_plan_blocked_until:final status cleanup apply confirmation accepted",
|
||||
"apply_gate:execution_plan_not_ready_for_final_confirmation",
|
||||
"apply_gate:owner_response_preflight_not_ready",
|
||||
"apply_gate:final_confirmation_status_not_approved_status_cleanup_apply",
|
||||
"apply_gate:final_flag_not_accepted:confirm_status_cleanup_command_preview",
|
||||
"apply_gate:final_flag_not_accepted:confirm_project_current_status_target_path",
|
||||
"apply_gate:final_flag_not_accepted:confirm_post_apply_validation",
|
||||
"apply_gate:final_flag_not_accepted:confirm_no_runtime_or_wazuh_deploy",
|
||||
"apply_gate:command_preview_not_confirmed",
|
||||
"apply_gate:target_path_not_confirmed:awoooi_memory/project_current_status.md",
|
||||
"wazuh_boundary:Wazuh route / production 404 由另一受控 branch 處理;branch=codex/iwooos-wazuh-boundary-guard-20260624 base=b540fc0c commits=38dc3c2f,9a53d3e1,e9972d47,758d419e,04db4b8a,8eec298e,325f262a patch_sha_1=08f8b36d7261b0dde6bfb0c47597bd0727d578dec3335c5ff7ded2bcaa2b7eb4 patch_sha_2=e6ec8f8d10e8a2bd711c399fa14ba0ab2dfb22f8ab6a733402944302eec7da7c patch_sha_3=7e99bd5284a25519313aea05bb314d3386454b91ce86241424385752d358900d patch_sha_4=f4ffbaecd94d3696660766cc6f4a6bd195762bc533d9502f8edfed2bb8379fab patch_sha_5=9035d6c411bf86d0857970b69dd33631f052aa90de27e52d82d448d4b8e4cec5 patch_sha_6=d3bb98711a3ebf91b9936b41bc232b689befc68a4a7cec38bf9cab4c8d015827 patch_sha_7=5aa3e69fee9624d0ff3f2bfad90595a81eb9306ad6387d640690a85a2f8038d7 apply_proof=release_apply_check_20260624_2248 release_gate=source1_push0_deploy0_readback0_runtime0 release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0;wazuh_live_agent_registry_readback=0 manager_agent_registry_readback_passed=false iwooos_live_route_readback_passed=false dashboard_agent_list_recovered=false iwooos_wazuh_runtime_gate=0 active_response=0;push_blocked=missing_noninteractive_gitea_https_credential;本視窗不改 runtime / Nginx / Docker / K8s / firewall / Wazuh secret。 agent_visibility_status=blocked_waiting_manager_agent_registry_readback agent_visibility_runtime_gate_count=0"
|
||||
],
|
||||
"next_actions": [
|
||||
"收齊 owner 旗標:0/6。",
|
||||
"收齊已批准章節:0/7。",
|
||||
"確認目標路徑:0/1。",
|
||||
"確認邊界 acknowledgement:0/5。",
|
||||
"owner response preflight ready 後,重新產生 dry-run execution plan。",
|
||||
"apply gate 目前 apply_allowed=False;未變 true 前不得寫 project_current_status。"
|
||||
],
|
||||
"wazuh_handoff": {
|
||||
"status": "blocked_not_released",
|
||||
"source_ref": "docs/operations/iwooos-wazuh-release-handoff.snapshot.json",
|
||||
"branch": "codex/iwooos-wazuh-boundary-guard-20260624",
|
||||
"base_commit": "b540fc0c",
|
||||
"commit_count": 7,
|
||||
"patch_count": 7,
|
||||
"release_owner_request_sent_count": 0,
|
||||
"release_owner_response_accepted_count": 0,
|
||||
"acknowledged_release_owner_ack_count": 0,
|
||||
"required_release_owner_ack_count": 6,
|
||||
"accepted_evidence_count": 0,
|
||||
"required_evidence_count": 6,
|
||||
"live_metadata_owner_count": 0,
|
||||
"secret_metadata_count": 0,
|
||||
"wazuh_live_agent_registry_readback": 0,
|
||||
"iwooos_wazuh_runtime_gate": 0,
|
||||
"active_response": 0,
|
||||
"stored_api_secret_metadata_changed": false,
|
||||
"agent_visibility_status": "blocked_waiting_manager_agent_registry_readback",
|
||||
"manager_agent_registry_readback_passed": false,
|
||||
"iwooos_live_route_readback_passed": false,
|
||||
"dashboard_agent_list_recovered": false,
|
||||
"agent_visibility_runtime_gate_count": 0,
|
||||
"push_gate_count": 0,
|
||||
"deploy_gate_count": 0,
|
||||
"readback_gate_count": 0,
|
||||
"runtime_gate_count": 0,
|
||||
"production_readback_status": "predeploy_404",
|
||||
"boundary": "Wazuh route / production 404 由另一受控 branch 處理;branch=codex/iwooos-wazuh-boundary-guard-20260624 base=b540fc0c commits=38dc3c2f,9a53d3e1,e9972d47,758d419e,04db4b8a,8eec298e,325f262a patch_sha_1=08f8b36d7261b0dde6bfb0c47597bd0727d578dec3335c5ff7ded2bcaa2b7eb4 patch_sha_2=e6ec8f8d10e8a2bd711c399fa14ba0ab2dfb22f8ab6a733402944302eec7da7c patch_sha_3=7e99bd5284a25519313aea05bb314d3386454b91ce86241424385752d358900d patch_sha_4=f4ffbaecd94d3696660766cc6f4a6bd195762bc533d9502f8edfed2bb8379fab patch_sha_5=9035d6c411bf86d0857970b69dd33631f052aa90de27e52d82d448d4b8e4cec5 patch_sha_6=d3bb98711a3ebf91b9936b41bc232b689befc68a4a7cec38bf9cab4c8d015827 patch_sha_7=5aa3e69fee9624d0ff3f2bfad90595a81eb9306ad6387d640690a85a2f8038d7 apply_proof=release_apply_check_20260624_2248 release_gate=source1_push0_deploy0_readback0_runtime0 release_lane_preflight=ready0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 owner_gate=request_sent0_response_accepted0_acks0of6_evidence0of6_push0_deploy0_readback0_runtime0 live_metadata_env_gate=owner0_secret_metadata0_push0_deploy0_readback0_runtime0;wazuh_live_agent_registry_readback=0 manager_agent_registry_readback_passed=false iwooos_live_route_readback_passed=false dashboard_agent_list_recovered=false iwooos_wazuh_runtime_gate=0 active_response=0;push_blocked=missing_noninteractive_gitea_https_credential;本視窗不改 runtime / Nginx / Docker / K8s / firewall / Wazuh secret。 agent_visibility_status=blocked_waiting_manager_agent_registry_readback agent_visibility_runtime_gate_count=0",
|
||||
"runtime_execution_authorized": false,
|
||||
"wazuh_api_live_query_authorized": false
|
||||
},
|
||||
"hard_gates": [
|
||||
"本 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 實作授權。"
|
||||
],
|
||||
"operation_boundaries": {
|
||||
"memory_write_authorized": false,
|
||||
"raw_codex_history_sync_authorized": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"active_scan_authorized": false,
|
||||
"wazuh_api_live_query_authorized": false,
|
||||
"wazuh_active_response_authorized": false,
|
||||
"repo_creation_authorized": false,
|
||||
"refs_sync_authorized": false,
|
||||
"workflow_modification_authorized": false,
|
||||
"workflow_trigger_authorized": false,
|
||||
"host_update_authorized": false,
|
||||
"backup_execution_authorized": false,
|
||||
"restore_execution_authorized": false,
|
||||
"migration_authorized": false
|
||||
},
|
||||
"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
|
||||
}
|
||||
88
docs/schemas/awoooi_status_cleanup_dashboard_v1.schema.json
Normal file
88
docs/schemas/awoooi_status_cleanup_dashboard_v1.schema.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "awoooi_status_cleanup_dashboard_v1",
|
||||
"title": "AWOOOI 狀態清理儀表板",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema_version",
|
||||
"generated_at",
|
||||
"target_route",
|
||||
"source_reviews",
|
||||
"summary",
|
||||
"metric_cards",
|
||||
"gate_cards",
|
||||
"workflow_rows",
|
||||
"risk_controls",
|
||||
"blocking_reasons",
|
||||
"next_actions",
|
||||
"wazuh_handoff",
|
||||
"hard_gates",
|
||||
"operation_boundaries",
|
||||
"secret_values_collected",
|
||||
"remote_write_performed",
|
||||
"local_product_write_performed",
|
||||
"execution_authorized",
|
||||
"memory_write_authorized",
|
||||
"wazuh_api_live_query_authorized",
|
||||
"runtime_execution_authorized",
|
||||
"ui_implementation_authorized"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {"const": "awoooi_status_cleanup_dashboard_v1"},
|
||||
"generated_at": {"type": "string"},
|
||||
"target_route": {"const": "/workspace/status-cleanup"},
|
||||
"source_reviews": {"type": "object", "additionalProperties": {"type": "string"}},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dashboard_status",
|
||||
"overall_completion_percent",
|
||||
"gate_count",
|
||||
"blocked_gate_count",
|
||||
"accepted_owner_flag_count",
|
||||
"required_owner_flag_count",
|
||||
"approved_update_section_count",
|
||||
"required_update_section_count",
|
||||
"wazuh_handoff_status",
|
||||
"wazuh_handoff_base_commit",
|
||||
"wazuh_handoff_commit_count",
|
||||
"wazuh_handoff_patch_count",
|
||||
"wazuh_live_metadata_owner_count",
|
||||
"wazuh_secret_metadata_count",
|
||||
"wazuh_live_agent_registry_readback",
|
||||
"iwooos_wazuh_runtime_gate",
|
||||
"wazuh_active_response_count",
|
||||
"wazuh_agent_visibility_status",
|
||||
"wazuh_manager_agent_registry_readback_passed",
|
||||
"wazuh_iwooos_live_route_readback_passed",
|
||||
"wazuh_dashboard_agent_list_recovered",
|
||||
"wazuh_agent_visibility_runtime_gate_count",
|
||||
"wazuh_runtime_gate_count",
|
||||
"apply_allowed",
|
||||
"memory_write_authorized",
|
||||
"wazuh_api_live_query_authorized",
|
||||
"runtime_execution_authorized",
|
||||
"ui_implementation_allowed"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"metric_cards": {"type": "array", "items": {"type": "object"}},
|
||||
"gate_cards": {"type": "array", "items": {"type": "object"}},
|
||||
"workflow_rows": {"type": "array", "items": {"type": "object"}},
|
||||
"risk_controls": {"type": "array", "items": {"type": "object"}},
|
||||
"blocking_reasons": {"type": "array", "items": {"type": "string"}},
|
||||
"next_actions": {"type": "array", "items": {"type": "string"}},
|
||||
"wazuh_handoff": {"type": "object", "additionalProperties": true},
|
||||
"hard_gates": {"type": "array", "items": {"type": "string"}},
|
||||
"operation_boundaries": {"type": "object", "additionalProperties": {"const": false}},
|
||||
"secret_values_collected": {"const": false},
|
||||
"remote_write_performed": {"const": false},
|
||||
"local_product_write_performed": {"const": false},
|
||||
"execution_authorized": {"const": false},
|
||||
"memory_write_authorized": {"const": false},
|
||||
"wazuh_api_live_query_authorized": {"const": false},
|
||||
"runtime_execution_authorized": {"const": false},
|
||||
"ui_implementation_authorized": {"const": false}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
625
scripts/dev/awoooi-status-cleanup-dashboard.py
Normal file
625
scripts/dev/awoooi-status-cleanup-dashboard.py
Normal file
@@ -0,0 +1,625 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user