diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 32a8118b..693d177e 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -91,6 +91,9 @@ from src.services.ai_agent_professional_task_expansion import ( from src.services.ai_agent_receipt_readback_owner_review import ( load_latest_ai_agent_receipt_readback_owner_review, ) +from src.services.ai_agent_report_no_write_analysis_runtime import ( + load_latest_ai_agent_report_no_write_analysis_runtime, +) from src.services.ai_agent_matched_playbook_learning_gap import ( load_latest_ai_agent_matched_playbook_learning_gap, ) @@ -829,6 +832,37 @@ async def get_agent_receipt_readback_owner_review() -> dict[str, Any]: ) from exc +@router.get( + "/agent-report-no-write-analysis-runtime", + response_model=dict[str, Any], + summary="取得 P2-407 AI Agent 報表 no-write 分析 runtime", + description=( + "讀取最新已提交的 P2-407 AI Agent 報表 no-write 分析快照;此端點只呈現 " + "OpenClaw、Hermes、NemoTron 讀取日報 / 週報 / 月報、P2-406B receipt owner review、" + "P2-004 供應鏈漂移與 P2-403J 報表真相後產生的分析草稿與風險分級。" + "它不啟動 live AI worker、不排程實發、不寫 Gateway queue、不送 Telegram、" + "不呼叫 Bot API、不寫 receipt production target、不寫 production、不讀 secret、" + "不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。" + ), +) +async def get_agent_report_no_write_analysis_runtime() -> dict[str, Any]: + """回傳最新 P2-407 report no-write analysis runtime 只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_report_no_write_analysis_runtime) + 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("ai_agent_report_no_write_analysis_runtime_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P2-407 AI Agent 報表 no-write 分析 runtime 快照無效", + ) from exc + + @router.get( "/agent-communication-learning-contract", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py b/apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py new file mode 100644 index 00000000..ade818a8 --- /dev/null +++ b/apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py @@ -0,0 +1,374 @@ +""" +P2-407 AI Agent report no-write analysis runtime snapshot. + +Loads the latest committed analysis draft that lets OpenClaw, Hermes, and +NemoTron read report evidence and propose risk-ranked recommendations. This +module intentionally does not run a live AI worker, send Telegram, write a +Gateway queue, write delivery receipts, read secrets, call paid APIs, mutate +hosts, run kubectl, or write production state. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from src.services.snapshot_paths import default_evaluations_dir + +_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__)) +_SNAPSHOT_PATTERN = "ai_agent_report_no_write_analysis_runtime_*.json" +_SCHEMA_VERSION = "ai_agent_report_no_write_analysis_runtime_v1" +_RUNTIME_AUTHORITY = "report_analysis_no_write_runtime_only_committed_snapshot" +_EXPECTED_CURRENT_TASK = "P2-407" +_EXPECTED_NEXT_TASK = "P2-408" +_EXPECTED_CANONICAL_ROOM = "AwoooI SRE 戰情室" +_EXPECTED_CANONICAL_ROOM_ENV = "SRE_GROUP_CHAT_ID" +_EXPECTED_SOURCE_SCHEMAS = { + "ai_agent_report_status_board_v1", + "ai_agent_report_automation_review_v1", + "ai_agent_receipt_readback_owner_review_v1", + "dependency_supply_chain_drift_monitor_v1", + "ai_agent_report_truth_actionability_review_v1", +} +_TRUE_TRUTH_FLAGS = { + "daily_weekly_monthly_reports_loaded", + "agent_workload_loaded", + "charts_loaded", + "receipt_owner_review_loaded", + "dependency_drift_loaded", + "report_truth_loaded", + "analysis_draft_snapshot_ready", +} +_FALSE_TRUTH_FLAGS = { + "ai_analysis_runtime_enabled", + "report_delivery_enabled", + "telegram_send_enabled", + "gateway_queue_write_enabled", + "bot_api_call_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", +} +_ZERO_TRUTH_COUNTS = { + "live_ai_analysis_run_count_24h", + "live_report_delivery_count_24h", + "telegram_send_count_24h", + "gateway_queue_write_count_24h", + "bot_api_call_count_24h", + "receipt_production_write_count_24h", + "production_write_count_24h", +} +_TRUE_BOUNDARY_FLAGS = { + "read_only_analysis_allowed", + "draft_snapshot_write_allowed", +} +_FALSE_BOUNDARY_FLAGS = { + "ai_analysis_runtime_enabled", + "report_delivery_enabled", + "telegram_send_enabled", + "gateway_queue_write_enabled", + "bot_api_call_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", + "openclaw_replacement_allowed", +} +_ZERO_ROLLUP_FIELDS = { + "live_report_delivery_count", + "live_ai_analysis_count", + "telegram_send_count", + "gateway_queue_write_count", + "bot_api_call_count", + "receipt_production_write_count", + "production_write_count", + "secret_read_count", + "paid_api_call_count", + "host_write_count", + "kubectl_action_count", +} +_FORBIDDEN_PUBLIC_TERMS = { + "批准!繼續", + "In app browser", + "My request for Codex", + "chain_of_thought", + "chain-of-thought", + "private reasoning text", + "authorization_header", + "authorization header value", + "telegram token value", + "raw prompt", + "raw_payload", +} + + +def load_latest_ai_agent_report_no_write_analysis_runtime( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed P2-407 no-write report analysis snapshot.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent report no-write analysis runtime snapshots 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") + + label = str(latest) + _require_schema(payload, label) + _require_sources(payload, label) + _require_analysis_truth(payload, label) + _require_report_inputs(payload, label) + _require_agent_passes(payload, label) + _require_recommendations_and_artifacts(payload, label) + _require_owner_gates(payload, label) + _require_boundaries(payload, label) + _require_rollups(payload, label) + _require_no_forbidden_public_terms(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + expected = { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": _EXPECTED_CURRENT_TASK, + "next_task_id": _EXPECTED_NEXT_TASK, + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_sources(payload: dict[str, Any], label: str) -> None: + if not payload.get("source_refs"): + raise ValueError(f"{label}: source_refs must not be empty") + sources = payload.get("source_readbacks") or [] + schemas = {item.get("source_schema_version") for item in sources} + missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas) + if missing: + raise ValueError(f"{label}: missing source schemas: {missing}") + for item in sources: + readback_id = item.get("readback_id") or "" + for field in ("source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"): + if not item.get(field): + raise ValueError(f"{label}: source readback {readback_id} missing {field}") + + +def _require_analysis_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("analysis_truth") or {} + missing_true = sorted(flag for flag in _TRUE_TRUTH_FLAGS if truth.get(flag) is not True) + if missing_true: + raise ValueError(f"{label}: analysis truth flags must remain true: {missing_true}") + unsafe_false = sorted(flag for flag in _FALSE_TRUTH_FLAGS if truth.get(flag) is not False) + if unsafe_false: + raise ValueError(f"{label}: analysis truth flags must remain false: {unsafe_false}") + non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: live analysis truth counts must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: analysis_truth.truth_note is required") + + +def _require_report_inputs(payload: dict[str, Any], label: str) -> None: + inputs = payload.get("report_inputs") or [] + input_ids = {item.get("report_id") for item in inputs} + if input_ids != {"daily", "weekly", "monthly"}: + raise ValueError(f"{label}: report_inputs must include daily, weekly, monthly") + for item in inputs: + report_id = item.get("report_id") or "" + if item.get("completion_percent") != 100: + raise ValueError(f"{label}: report input {report_id} completion_percent must remain 100") + if not isinstance(item.get("actionability_score"), int) or item.get("actionability_score") <= 0: + raise ValueError(f"{label}: report input {report_id} actionability_score must be positive") + if not item.get("analysis_focus") or not item.get("blocked_runtime_action"): + raise ValueError(f"{label}: report input {report_id} missing focus or blocked action") + + +def _require_agent_passes(payload: dict[str, Any], label: str) -> None: + passes = payload.get("agent_analysis_passes") or [] + agent_ids = {item.get("agent_id") for item in passes} + if agent_ids != {"openclaw", "hermes", "nemotron"}: + raise ValueError(f"{label}: agent_analysis_passes must include OpenClaw, Hermes, NemoTron") + for item in passes: + agent_id = item.get("agent_id") or "" + if item.get("live_runtime_write_allowed") is not False: + raise ValueError(f"{label}: agent pass {agent_id} live_runtime_write_allowed must remain false") + if not item.get("summary") or not item.get("handoff_to"): + raise ValueError(f"{label}: agent pass {agent_id} missing summary or handoff") + + +def _require_recommendations_and_artifacts(payload: dict[str, Any], label: str) -> None: + recommendations = payload.get("draft_recommendations") or [] + if len(recommendations) < 1: + raise ValueError(f"{label}: draft_recommendations must not be empty") + for item in recommendations: + recommendation_id = item.get("recommendation_id") or "" + if not isinstance(item.get("actionability_score"), int) or item.get("actionability_score") <= 0: + raise ValueError(f"{label}: recommendation {recommendation_id} actionability_score must be positive") + if not item.get("blocked_runtime_action"): + raise ValueError(f"{label}: recommendation {recommendation_id} missing blocked_runtime_action") + if item.get("risk_tier") in {"high", "critical"} and item.get("approval_required") is not True: + raise ValueError(f"{label}: high/critical recommendation {recommendation_id} must require approval") + + artifacts = payload.get("draft_artifacts") or [] + if len(artifacts) < 1: + raise ValueError(f"{label}: draft_artifacts must not be empty") + for item in artifacts: + artifact_id = item.get("artifact_id") or "" + for flag in ("writes_production", "sends_telegram", "contains_secret"): + if item.get(flag) is not False: + raise ValueError(f"{label}: draft artifact {artifact_id}.{flag} must remain false") + if not item.get("evidence_ref"): + raise ValueError(f"{label}: draft artifact {artifact_id} evidence_ref is required") + + +def _require_owner_gates(payload: dict[str, Any], label: str) -> None: + gates = payload.get("owner_review_gates") or [] + if len(gates) < 1: + raise ValueError(f"{label}: owner_review_gates must not be empty") + for gate in gates: + gate_id = gate.get("gate_id") or "" + if gate.get("risk_tier") in {"high", "critical"} and gate.get("status") not in { + "owner_review_required", + "blocked_by_runtime_gate", + }: + raise ValueError(f"{label}: high/critical owner gate {gate_id} must remain blocked or owner-review") + for field in ("required_fields", "acceptance_checks", "blocked_runtime_actions"): + if not gate.get(field): + raise ValueError(f"{label}: owner gate {gate_id} missing {field}") + + +def _require_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = payload.get("activation_boundaries") or {} + missing_true = sorted(flag for flag in _TRUE_BOUNDARY_FLAGS if boundaries.get(flag) is not True) + if missing_true: + raise ValueError(f"{label}: activation boundaries must remain true: {missing_true}") + unsafe_false = sorted(flag for flag in _FALSE_BOUNDARY_FLAGS if boundaries.get(flag) is not False) + if unsafe_false: + raise ValueError(f"{label}: activation boundaries must remain false: {unsafe_false}") + + telegram = payload.get("telegram_policy") or {} + expected_telegram = { + "canonical_room": _EXPECTED_CANONICAL_ROOM, + "canonical_room_env": _EXPECTED_CANONICAL_ROOM_ENV, + "gateway_queue_write_allowed": False, + "direct_bot_api_allowed": False, + "telegram_send_allowed": False, + "receipt_write_allowed": False, + } + mismatches = _mismatches(telegram, expected_telegram) + if mismatches: + raise ValueError(f"{label}: telegram_policy mismatch: {mismatches}") + + redaction = payload.get("display_redaction_contract") or {} + for flag in ( + "redaction_required", + ): + if redaction.get(flag) is not True: + raise ValueError(f"{label}: display redaction flag {flag} must remain true") + for flag in ( + "raw_report_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "work_window_transcript_display_allowed", + ): + if redaction.get(flag) is not False: + raise ValueError(f"{label}: display redaction flag {flag} must remain false") + + +def _require_rollups(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + sources = payload.get("source_readbacks") or [] + inputs = payload.get("report_inputs") or [] + passes = payload.get("agent_analysis_passes") or [] + recommendations = payload.get("draft_recommendations") or [] + artifacts = payload.get("draft_artifacts") or [] + gates = payload.get("owner_review_gates") or [] + blocked_actions = { + *(item.get("blocked_runtime_action") for item in recommendations), + *( + action + for gate in gates + for action in (gate.get("blocked_runtime_actions") or []) + ), + } + blocked_actions.discard(None) + expected = { + "source_readback_count": len(sources), + "report_input_count": len(inputs), + "agent_analysis_pass_count": len(passes), + "draft_recommendation_count": len(recommendations), + "draft_artifact_count": len(artifacts), + "owner_review_gate_count": len(gates), + "approval_required_recommendation_count": sum( + 1 for item in recommendations if item.get("approval_required") is True + ), + "low_risk_recommendation_count": sum(1 for item in recommendations if item.get("risk_tier") == "low"), + "medium_risk_recommendation_count": sum(1 for item in recommendations if item.get("risk_tier") == "medium"), + "high_risk_recommendation_count": sum(1 for item in recommendations if item.get("risk_tier") == "high"), + "critical_risk_recommendation_count": sum(1 for item in recommendations if item.get("risk_tier") == "critical"), + "actionability_score_ready_count": sum( + 1 for item in recommendations if isinstance(item.get("actionability_score"), int) and item["actionability_score"] > 0 + ), + "blocked_runtime_action_count": len(blocked_actions), + } + mismatches = { + key: {"expected": value, "actual": rollups.get(key)} + for key, value in expected.items() + if rollups.get(key) != value + } + if mismatches: + raise ValueError(f"{label}: rollup counts must match payload sections: {mismatches}") + + approval_required_ids = sorted( + item.get("recommendation_id") + for item in recommendations + if item.get("approval_required") is True + ) + if sorted(rollups.get("approval_required_recommendation_ids") or []) != approval_required_ids: + raise ValueError(f"{label}: approval_required_recommendation_ids mismatch") + + non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: live rollup counts must remain zero: {non_zero}") + + +def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None: + public_text = json.dumps(payload, ensure_ascii=False) + lower_public_text = public_text.lower() + leaked_terms = sorted( + term + for term in _FORBIDDEN_PUBLIC_TERMS + if (term.lower() if term.isascii() else term) in lower_public_text + ) + if leaked_terms: + raise ValueError(f"{label}: forbidden public terms present: {leaked_terms}") + + +def _mismatches(actual: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + key: {"expected": expected_value, "actual": actual.get(key)} + for key, expected_value in expected.items() + if actual.get(key) != expected_value + } diff --git a/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime.py b/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime.py new file mode 100644 index 00000000..f1cba10c --- /dev/null +++ b/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_report_no_write_analysis_runtime import ( + load_latest_ai_agent_report_no_write_analysis_runtime, +) + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_COMMITTED_SNAPSHOT = ( + _REPO_ROOT + / "docs" + / "evaluations" + / "ai_agent_report_no_write_analysis_runtime_2026-06-18.json" +) + + +def test_load_latest_ai_agent_report_no_write_analysis_runtime_reads_newest_file(tmp_path): + older = _snapshot(generated_at="2026-06-17T00:00:00+08:00") + newer = _snapshot(generated_at="2026-06-18T00:00:00+08:00") + (tmp_path / "ai_agent_report_no_write_analysis_runtime_2026-06-17.json").write_text( + json.dumps(older), + encoding="utf-8", + ) + (tmp_path / "ai_agent_report_no_write_analysis_runtime_2026-06-18.json").write_text( + json.dumps(newer), + encoding="utf-8", + ) + + loaded = load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + assert loaded["generated_at"] == "2026-06-18T00:00:00+08:00" + assert loaded["program_status"]["current_task_id"] == "P2-407" + assert loaded["program_status"]["next_task_id"] == "P2-408" + assert loaded["program_status"]["read_only_mode"] is True + assert loaded["rollups"]["live_ai_analysis_count"] == 0 + assert loaded["rollups"]["telegram_send_count"] == 0 + + +def test_ai_agent_report_no_write_analysis_runtime_requires_read_only_mode(tmp_path): + snapshot = _snapshot() + snapshot["program_status"]["read_only_mode"] = False + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="program_status"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_blocks_live_ai_runtime(tmp_path): + snapshot = _snapshot() + snapshot["analysis_truth"]["ai_analysis_runtime_enabled"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="analysis truth flags"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_blocks_telegram_send(tmp_path): + snapshot = _snapshot() + snapshot["telegram_policy"]["telegram_send_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="telegram_policy"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_requires_source_schemas(tmp_path): + snapshot = _snapshot() + snapshot["source_readbacks"] = snapshot["source_readbacks"][:-1] + snapshot["rollups"]["source_readback_count"] = len(snapshot["source_readbacks"]) + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing source schemas"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_requires_rollup_consistency(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["draft_recommendation_count"] = 99 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_rejects_private_terms(tmp_path): + snapshot = _snapshot() + snapshot["draft_recommendations"][0]["draft_solution"] = "請把 In app browser 內容放進前端" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden public terms"): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def test_ai_agent_report_no_write_analysis_runtime_fails_when_missing(tmp_path): + with pytest.raises(FileNotFoundError): + load_latest_ai_agent_report_no_write_analysis_runtime(tmp_path) + + +def _snapshot(*, generated_at: str = "2026-06-18T15:20:00+08:00") -> dict: + payload = json.loads(_COMMITTED_SNAPSHOT.read_text(encoding="utf-8")) + cloned = copy.deepcopy(payload) + cloned["generated_at"] = generated_at + return cloned + + +def _write_snapshot(tmp_path, payload: dict) -> None: + (tmp_path / "ai_agent_report_no_write_analysis_runtime_2026-06-18.json").write_text( + json.dumps(payload), + encoding="utf-8", + ) diff --git a/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime_api.py b/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime_api.py new file mode 100644 index 00000000..7cecfaf3 --- /dev/null +++ b/apps/api/tests/test_ai_agent_report_no_write_analysis_runtime_api.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_report_no_write_analysis_runtime_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-report-no-write-analysis-runtime") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_report_no_write_analysis_runtime_v1" + assert data["program_status"]["current_task_id"] == "P2-407" + assert data["program_status"]["next_task_id"] == "P2-408" + assert data["program_status"]["read_only_mode"] is True + assert data["program_status"]["runtime_authority"] == "report_analysis_no_write_runtime_only_committed_snapshot" + assert data["rollups"]["source_readback_count"] == len(data["source_readbacks"]) == 5 + assert data["rollups"]["report_input_count"] == len(data["report_inputs"]) == 3 + assert data["rollups"]["agent_analysis_pass_count"] == len(data["agent_analysis_passes"]) == 3 + assert data["rollups"]["draft_recommendation_count"] == len(data["draft_recommendations"]) == 6 + assert data["rollups"]["draft_artifact_count"] == len(data["draft_artifacts"]) == 4 + assert data["rollups"]["owner_review_gate_count"] == len(data["owner_review_gates"]) == 3 + assert data["rollups"]["approval_required_recommendation_count"] == 2 + assert data["rollups"]["live_report_delivery_count"] == 0 + assert data["rollups"]["live_ai_analysis_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["receipt_production_write_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["secret_read_count"] == 0 + assert data["rollups"]["paid_api_call_count"] == 0 + assert data["rollups"]["host_write_count"] == 0 + assert data["rollups"]["kubectl_action_count"] == 0 + assert data["telegram_policy"]["canonical_room"] == "AwoooI SRE 戰情室" + assert data["telegram_policy"]["canonical_room_env"] == "SRE_GROUP_CHAT_ID" + assert data["telegram_policy"]["telegram_send_allowed"] is False + assert data["telegram_policy"]["gateway_queue_write_allowed"] is False + assert data["telegram_policy"]["direct_bot_api_allowed"] is False + assert data["activation_boundaries"]["ai_analysis_runtime_enabled"] is False + assert data["activation_boundaries"]["openclaw_replacement_allowed"] is False diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 0687e2a1..2d71200c 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -3707,6 +3707,46 @@ "critical": "critical" } }, + "reportNoWriteAnalysis": { + "title": "P2-407 AI 報表 no-write 分析", + "subtitle": "{current} → {next};分析建議 {recommendations};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "committed snapshot", + "room": "戰情室 {room}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "sources": "來源讀回", + "inputs": "報表輸入", + "passes": "Agent pass", + "recommendations": "分析建議", + "artifacts": "草稿產物", + "gates": "Owner gates", + "approvals": "需批准", + "live": "Live total" + }, + "sections": { + "recommendations": "No-write 分析建議", + "agentPasses": "Agent 接手分析", + "ownerGates": "Owner gate", + "truth": "No-write truth" + }, + "labels": { + "score": "score {score}", + "approvalRequired": "approval {value}", + "gateDetail": "owner {owner} · 欄位 {fields} · 驗收 {checks}", + "generated": "generated {generated}", + "env": "env {value}", + "redaction": "redaction {value}" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 0687e2a1..2d71200c 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -3707,6 +3707,46 @@ "critical": "critical" } }, + "reportNoWriteAnalysis": { + "title": "P2-407 AI 報表 no-write 分析", + "subtitle": "{current} → {next};分析建議 {recommendations};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "committed snapshot", + "room": "戰情室 {room}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "sources": "來源讀回", + "inputs": "報表輸入", + "passes": "Agent pass", + "recommendations": "分析建議", + "artifacts": "草稿產物", + "gates": "Owner gates", + "approvals": "需批准", + "live": "Live total" + }, + "sections": { + "recommendations": "No-write 分析建議", + "agentPasses": "Agent 接手分析", + "ownerGates": "Owner gate", + "truth": "No-write truth" + }, + "labels": { + "score": "score {score}", + "approvalRequired": "approval {value}", + "gateDetail": "owner {owner} · 欄位 {fields} · 驗收 {checks}", + "generated": "generated {generated}", + "env": "env {value}", + "redaction": "redaction {value}" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index 772295ca..d91737c4 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -47,6 +47,7 @@ import { type AiAgent12AgentWarRoomSnapshot, type AiAgentProfessionalTaskExpansionSnapshot, type AiAgentReceiptReadbackOwnerReviewSnapshot, + type AiAgentReportNoWriteAnalysisRuntimeSnapshot, type AiAgentCandidateOperationDryRunEvidenceSnapshot, type AiAgentCriticReviewerResultCaptureSnapshot, type AiAgentDeploymentLayoutSnapshot, @@ -718,6 +719,7 @@ export function AutomationInventoryTab() { const [warRoom, setWarRoom] = useState(null) const [professionalTaskExpansion, setProfessionalTaskExpansion] = useState(null) const [receiptReadbackOwnerReview, setReceiptReadbackOwnerReview] = useState(null) + const [reportNoWriteAnalysisRuntime, setReportNoWriteAnalysisRuntime] = useState(null) const [proactiveOperations, setProactiveOperations] = useState(null) const [interactionLearningProof, setInteractionLearningProof] = useState(null) const [liveReadModelGate, setLiveReadModelGate] = useState(null) @@ -809,6 +811,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgent12AgentWarRoom(), apiClient.getAiAgentProfessionalTaskExpansion(), apiClient.getAiAgentReceiptReadbackOwnerReview(), + apiClient.getAiAgentReportNoWriteAnalysisRuntime(), apiClient.getAiAgentProactiveOperationsContract(), apiClient.getAiAgentInteractionLearningProof(), apiClient.getAiAgentLiveReadModelGate(), @@ -893,6 +896,7 @@ export function AutomationInventoryTab() { warRoomResult, professionalTaskExpansionResult, receiptReadbackOwnerReviewResult, + reportNoWriteAnalysisRuntimeResult, proactiveOperationsResult, interactionLearningProofResult, liveReadModelGateResult, @@ -974,6 +978,7 @@ export function AutomationInventoryTab() { setWarRoom(warRoomResult.status === 'fulfilled' ? warRoomResult.value : null) setProfessionalTaskExpansion(professionalTaskExpansionResult.status === 'fulfilled' ? professionalTaskExpansionResult.value : null) setReceiptReadbackOwnerReview(receiptReadbackOwnerReviewResult.status === 'fulfilled' ? receiptReadbackOwnerReviewResult.value : null) + setReportNoWriteAnalysisRuntime(reportNoWriteAnalysisRuntimeResult.status === 'fulfilled' ? reportNoWriteAnalysisRuntimeResult.value : null) setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null) setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null) setLiveReadModelGate(liveReadModelGateResult.status === 'fulfilled' ? liveReadModelGateResult.value : null) @@ -1057,6 +1062,7 @@ export function AutomationInventoryTab() { warRoomResult, professionalTaskExpansionResult, receiptReadbackOwnerReviewResult, + reportNoWriteAnalysisRuntimeResult, proactiveOperationsResult, interactionLearningProofResult, liveReadModelGateResult, @@ -1541,6 +1547,39 @@ export function AutomationInventoryTab() { .slice(0, 5) }, [reportAutomationReview]) + const visibleReportNoWriteRecommendations = useMemo(() => { + if (!reportNoWriteAnalysisRuntime) return [] + const priority = { critical: 0, high: 0, medium: 1, low: 2 } as Record + return [...reportNoWriteAnalysisRuntime.draft_recommendations] + .sort((a, b) => { + const left = priority[a.risk_tier] ?? 3 + const right = priority[b.risk_tier] ?? 3 + if (left !== right) return left - right + if (a.approval_required !== b.approval_required) { + return a.approval_required ? -1 : 1 + } + return b.actionability_score - a.actionability_score + }) + .slice(0, 6) + }, [reportNoWriteAnalysisRuntime]) + + const visibleReportNoWriteGates = useMemo(() => { + if (!reportNoWriteAnalysisRuntime) return [] + const statusPriority = { owner_review_required: 0, blocked_by_runtime_gate: 1, draft_ready: 2 } as Record + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...reportNoWriteAnalysisRuntime.owner_review_gates] + .sort((a, b) => { + const leftStatus = statusPriority[a.status] ?? 3 + const rightStatus = statusPriority[b.status] ?? 3 + if (leftStatus !== rightStatus) return leftStatus - rightStatus + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.gate_id.localeCompare(b.gate_id) + }) + .slice(0, 4) + }, [reportNoWriteAnalysisRuntime]) + const visibleReportRuntimeLanes = useMemo(() => { if (!reportRuntimeReadiness) return [] const priority = { blocked_by_runtime_gate: 0, ready_for_owner_review: 1 } as Record @@ -2473,7 +2512,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -2717,6 +2756,28 @@ export function AutomationInventoryTab() { || receiptReadbackOwnerReview.activation_boundaries.kubectl_action_enabled, }, ] + const reportNoWriteAnalysisOverall = reportNoWriteAnalysisRuntime.program_status.overall_completion_percent + const reportNoWriteSources = reportNoWriteAnalysisRuntime.rollups.source_readback_count + const reportNoWriteInputs = reportNoWriteAnalysisRuntime.rollups.report_input_count + const reportNoWriteAgentPasses = reportNoWriteAnalysisRuntime.rollups.agent_analysis_pass_count + const reportNoWriteRecommendations = reportNoWriteAnalysisRuntime.rollups.draft_recommendation_count + const reportNoWriteArtifacts = reportNoWriteAnalysisRuntime.rollups.draft_artifact_count + const reportNoWriteGates = reportNoWriteAnalysisRuntime.rollups.owner_review_gate_count + const reportNoWriteApprovals = reportNoWriteAnalysisRuntime.rollups.approval_required_recommendation_count + const reportNoWriteBlocked = reportNoWriteAnalysisRuntime.rollups.blocked_runtime_action_count + const reportNoWriteLiveTotal = ( + reportNoWriteAnalysisRuntime.rollups.live_report_delivery_count + + reportNoWriteAnalysisRuntime.rollups.live_ai_analysis_count + + reportNoWriteAnalysisRuntime.rollups.telegram_send_count + + reportNoWriteAnalysisRuntime.rollups.gateway_queue_write_count + + reportNoWriteAnalysisRuntime.rollups.bot_api_call_count + + reportNoWriteAnalysisRuntime.rollups.receipt_production_write_count + + reportNoWriteAnalysisRuntime.rollups.production_write_count + + reportNoWriteAnalysisRuntime.rollups.secret_read_count + + reportNoWriteAnalysisRuntime.rollups.paid_api_call_count + + reportNoWriteAnalysisRuntime.rollups.host_write_count + + reportNoWriteAnalysisRuntime.rollups.kubectl_action_count + ) const reportRuntimeOverall = reportRuntimeReadiness.program_status.overall_completion_percent const reportRuntimeLanes = reportRuntimeReadiness.rollups.runtime_lane_count const reportRuntimeReady = reportRuntimeReadiness.rollups.ready_contract_count @@ -4701,6 +4762,137 @@ export function AutomationInventoryTab() {
+ +
+
+
+
+ +
+
+ + {t('reportNoWriteAnalysis.title')} + + + {t('reportNoWriteAnalysis.subtitle', { + current: reportNoWriteAnalysisRuntime.program_status.current_task_id, + next: reportNoWriteAnalysisRuntime.program_status.next_task_id, + recommendations: reportNoWriteRecommendations, + blocked: reportNoWriteBlocked, + })} + +
+
+
+ + + +
+
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + 0 ? 'danger' : 'ok'} icon={} /> + } /> +
+ +
+
+ {t('reportNoWriteAnalysis.sections.recommendations')} + {visibleReportNoWriteRecommendations.map(recommendation => { + const tone = recommendation.risk_tier === 'high' || recommendation.risk_tier === 'critical' ? 'danger' : recommendation.risk_tier === 'medium' ? 'warn' : 'ok' + return ( +
+
+ + {redactPublicText(recommendation.display_name)} + + +
+ + {redactPublicText(recommendation.draft_solution)} + +
+ + + + +
+
+ ) + })} +
+ +
+
+ {t('reportNoWriteAnalysis.sections.agentPasses')} +
+ {reportNoWriteAnalysisRuntime.agent_analysis_passes.map(pass => ( + + ))} +
+
+ +
+ {t('reportNoWriteAnalysis.sections.ownerGates')} +
+ {visibleReportNoWriteGates.map(gate => ( + + ))} +
+
+ +
+ {t('reportNoWriteAnalysis.sections.truth')} + + {redactPublicText(reportNoWriteAnalysisRuntime.analysis_truth.truth_note)} + +
+ + + +
+
+
+
+
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 6a8649b6..efb988b4 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -344,6 +344,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentReportNoWriteAnalysisRuntime() { + const res = await fetch(`${API_BASE_URL}/agents/agent-report-no-write-analysis-runtime`) + return handleResponse(res) + }, + async getAiAgentProactiveOperationsContract() { const res = await fetch(`${API_BASE_URL}/agents/agent-proactive-operations-contract`) return handleResponse(res) @@ -3054,6 +3059,180 @@ export interface AiAgentReportTruthActionabilityReviewSnapshot { } } +export interface AiAgentReportNoWriteAnalysisRuntimeSnapshot { + schema_version: 'ai_agent_report_no_write_analysis_runtime_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-407' + next_task_id: 'P2-408' + read_only_mode: true + runtime_authority: 'report_analysis_no_write_runtime_only_committed_snapshot' + status_note: string + } + source_refs: string[] + source_readbacks: Array<{ + readback_id: string + source_schema_version: string + source_ref: string + endpoint: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: string + key_readback: string + next_action: string + }> + analysis_truth: { + daily_weekly_monthly_reports_loaded: true + agent_workload_loaded: true + charts_loaded: true + receipt_owner_review_loaded: true + dependency_drift_loaded: true + report_truth_loaded: true + analysis_draft_snapshot_ready: true + ai_analysis_runtime_enabled: false + report_delivery_enabled: false + telegram_send_enabled: false + gateway_queue_write_enabled: false + bot_api_call_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + live_ai_analysis_run_count_24h: number + live_report_delivery_count_24h: number + telegram_send_count_24h: number + gateway_queue_write_count_24h: number + bot_api_call_count_24h: number + receipt_production_write_count_24h: number + production_write_count_24h: number + truth_note: string + } + report_inputs: Array<{ + report_id: 'daily' | 'weekly' | 'monthly' + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + completion_percent: number + source_confidence: string + actionability_score: number + analysis_focus: string[] + blocked_runtime_action: string + }> + agent_analysis_passes: Array<{ + agent_id: 'openclaw' | 'hermes' | 'nemotron' + display_name: string + role: string + reviewed_source_count: number + draft_finding_count: number + highest_risk_tier: 'low' | 'medium' | 'high' | 'critical' + live_runtime_write_allowed: false + summary: string + handoff_to: string + }> + draft_recommendations: Array<{ + recommendation_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + approval_required: boolean + actionability_score: number + problem: string + draft_solution: string + expected_signal: string + blocked_runtime_action: string + }> + draft_artifacts: Array<{ + artifact_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'committed_snapshot_ready' | 'draft_only' | 'owner_review_required' | 'blocked_by_runtime_gate' + writes_production: false + sends_telegram: false + contains_secret: false + evidence_ref: string + }> + owner_review_gates: Array<{ + gate_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + status: 'owner_review_required' | 'blocked_by_runtime_gate' | 'draft_ready' + required_fields: string[] + acceptance_checks: string[] + blocked_runtime_actions: string[] + }> + activation_boundaries: { + read_only_analysis_allowed: true + draft_snapshot_write_allowed: true + ai_analysis_runtime_enabled: false + report_delivery_enabled: false + telegram_send_enabled: false + gateway_queue_write_enabled: false + bot_api_call_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + destructive_operation_enabled: false + openclaw_replacement_allowed: false + } + telegram_policy: { + canonical_room: 'AwoooI SRE 戰情室' + canonical_room_env: 'SRE_GROUP_CHAT_ID' + gateway_queue_write_allowed: false + direct_bot_api_allowed: false + telegram_send_allowed: false + receipt_write_allowed: false + policy_note: string + } + display_redaction_contract: { + redaction_required: true + raw_report_payload_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + work_window_transcript_display_allowed: false + allowed_display_fields: string[] + blocked_display_fields: string[] + } + rollups: { + source_readback_count: number + report_input_count: number + agent_analysis_pass_count: number + draft_recommendation_count: number + draft_artifact_count: number + owner_review_gate_count: number + approval_required_recommendation_count: number + approval_required_recommendation_ids: string[] + low_risk_recommendation_count: number + medium_risk_recommendation_count: number + high_risk_recommendation_count: number + critical_risk_recommendation_count: number + actionability_score_ready_count: number + blocked_runtime_action_count: number + live_report_delivery_count: number + live_ai_analysis_count: number + telegram_send_count: number + gateway_queue_write_count: number + bot_api_call_count: number + receipt_production_write_count: number + production_write_count: number + secret_read_count: number + paid_api_call_count: number + host_write_count: number + kubectl_action_count: number + } + next_actions: Array<{ + task_id: string + priority: 'P0' | 'P1' | 'P2' | 'P3' + summary: string + gate: string + }> +} + export interface AiAgentReportAutomationReviewSnapshot { schema_version: 'ai_agent_report_automation_review_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index e780eca3..38dec3dc 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -62,6 +62,29 @@ - Production deploy / `/zh-TW/iwooos` 前台驗證:`cd.yaml #3140` 因後續主線推進已取消,前一段基準為 `f358a0f6` / `cd.yaml #3150` / deploy marker `2d278568`;本段 AI 自動化產品契約正式基準為 code commit `87f1dc8d`、deploy marker `9851be79`、Gitea `cd.yaml #3155` 成功、`code-review.yaml #3156` 成功,production probe 與 desktop / mobile browser 驗證完成。 **邊界**:本段只做規範與 repo-side 契約,不送 Telegram、不改主機、不 reload Nginx、不改 firewall、不 kill process、不觸發 Wazuh active response、不跑 Kali active scan、不收 secret、不把工作視窗內容放入前端。 +## 2026-06-18|P2-407 AI 報表 no-write 分析 runtime 本地完成 + +**背景**:P2-406B 已正式驗證 receipt readback owner review,下一個有效動作依工作清單與 MASTER §8 是 P2-407:讓 OpenClaw / Hermes / NemoTron 讀取日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 dependency drift monitor 與 P2-403J 報表真相後產生 no-write 分析建議;此階段不得啟動 live AI worker、Telegram 實發、Gateway queue、Bot API、receipt production write 或 production write。 + +**完成內容**: +- 新增 `docs/schemas/ai_agent_report_no_write_analysis_runtime_v1.schema.json` 與 `docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json`。 +- 新增 `apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py` 與 `GET /api/v1/agents/agent-report-no-write-analysis-runtime`。 +- 新增 `apps/api/tests/test_ai_agent_report_no_write_analysis_runtime.py` 與 `apps/api/tests/test_ai_agent_report_no_write_analysis_runtime_api.py`。 +- 治理頁 `automation-inventory` 新增 P2-407 卡片,顯示 `5` 個 source readback、`3` 份 report input、`3` 個 Agent analysis pass、`6` 筆 draft recommendation、`4` 個 draft artifact、`3` 個 owner gate、`9` 個 blocked runtime action 與 live total `0`。 +- `apps/web/messages/zh-TW.json` / `en.json` 已補 P2-407 i18n,且不顯示工作對話、原始 prompt、private reasoning、secret value 或瀏覽器上下文。 + +**驗證**: +- `python3 -m py_compile apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py`:通過。 +- `DATABASE_URL=postgresql://test:test@localhost:5432/test pytest apps/api/tests/test_ai_agent_report_no_write_analysis_runtime.py apps/api/tests/test_ai_agent_report_no_write_analysis_runtime_api.py`:`9 passed`。 +- JSON parse:新 schema、新 snapshot、`zh-TW.json`、`en.json` 均通過。 +- i18n parity:`zh 12648 / en 12648 / missingEn 0 / missingZh 0 / typeDiff 0`。 + +**完成度同步**: +- P2-407 本地完成度:`100%`。 +- P2-407 正式部署 / production readback:`0%`,待本輪推版與 production API / browser smoke。 +- P2-408 成為下一個有效動作:中 / 低風險自動處理白名單、dry-run verifier、rollback proof、高風險 Owner Review Queue。 + +**邊界**:本段沒有開 AI analysis live runtime、沒有發 Telegram、沒有寫 Gateway queue、沒有呼叫 Bot API、沒有寫 receipt production target、沒有讀 secret、沒有呼叫 paid API、沒有 host write、沒有 kubectl、沒有 production write、沒有替換 OpenClaw。P2-407 只產 committed snapshot / API / governance UI 草稿。 ## 2026-06-18|110 Runaway Process AIOps 監控 / 告警 / PlayBook 收斂 diff --git a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md index cc085852..c4331d88 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -13,10 +13,10 @@ | 項目 | 目前完成度 | 本次判讀 | 下一個有效動作 | |---|---:|---|---| | 本工作清單細化 | 100% | 已把所有工作流拆成 P0 / P1 / P2 / P3 | 同步 LOGBOOK 與 MASTER §8 | -| AgentOps 治理與可觀測基礎 | 70% | 已有 schema / snapshot / API / UI / gate;P2-406B 已把報表、Telegram receipt 與 P2-004 drift monitor 串成只讀 owner review,但 runtime 真正執行仍低 | P2-407 no-write 報表分析 | +| AgentOps 治理與可觀測基礎 | 74% | 已有 schema / snapshot / API / UI / gate;P2-407 已把日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成 no-write 分析草稿,但 runtime 真正執行仍低 | P2-408 中 / 低風險白名單 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | 目前是只讀 layout 與治理頁可視化,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | | Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 35% | 互動證據、War Room 與 readback gate 已齊;Event Bus、RAG writeback、PlayBook trust 寫入仍未開 | 先做 no-write event stream,再做 Owner-approved writeback | -| 日報 / 週報 / 月報 | 可視化 100%,實發 0% | 報表批准包、P2-406B owner review 與治理頁已可見;Telegram 實發、receipt production write、AI analysis runtime 仍為 0 | P2-407 先做 no-write analyst | +| 日報 / 週報 / 月報 | 可視化 100%,no-write 分析 100%,實發 0% | 報表批准包、P2-406B owner review、P2-407 no-write analyst 與治理頁已可見;Telegram 實發、receipt production write、AI analysis live runtime 仍為 0 | P2-408 白名單與 P2-406F no-send scheduler | | Telegram Bot / TG 群組 | 契約 44%,實發 0% | no-send preview、dry-run、owner review gate 與 P2-406B receipt readback owner review 已有;live send / Bot API / Gateway queue 未批准 | 收到合格 owner approval 後才評估 P2-406C one-message canary | | 中低風險自動化 | 30% | 政策方向已明確,但實際 auto worker 未開 | 建立 low / medium whitelist、dry-run verifier、rollback proof | | 高風險審核 | 68% | Owner Gate、拒收規則與 P2-406B receipt owner review 已多層化;仍缺真實 owner response accepted ledger | P2-145 / P2-406C 類 gate 繼續只讀讀回 | @@ -65,7 +65,7 @@ | 4 | P2-406E | P0 | Failure-only Telegram digest route | SRE + Telegram | 待辦 | success quiet、failure action-required、rate limit、mute / rollback 可讀 | | 5 | P2-406F | P0 | 日報 / 週報 / 月報 no-send scheduler | Hermes | 待辦 | 產生日 / 週 / 月三種報表 snapshot;不實發 | | 6 | P2-406G | P0 | 單一 Telegram canary live send | Telegram + OpenClaw | Owner Gate 阻擋 | 需明確 owner approval、maintenance window、rollback、receipt owner;否則不得執行 | -| 7 | P2-407 | P0 | AI 報表自動分析 no-write runtime | Hermes + NemoTron | 待辦 | AI 讀報表後產生建議,僅寫草稿 snapshot,不寫 production | +| 7 | P2-407 | P0 | AI 報表自動分析 no-write runtime | Hermes + NemoTron | 本地完成,待正式驗證 | 新增 `ai_agent_report_no_write_analysis_runtime_v1` schema / snapshot / API / tests / governance UI;5 個 source readback、3 份 report input、3 個 Agent analysis pass、6 筆 draft recommendation、4 個 draft artifact、3 個 owner gate;live AI runtime / Telegram / Gateway / production write 仍為 0 | | 8 | P2-408 | P0 | 中 / 低風險自動處理白名單 | OpenClaw + SRE | 待辦 | low / medium action policy、dry-run verifier、rollback proof、audit reason | | 9 | P2-409 | P0 | 高風險 Owner Review Queue | OpenClaw | 待辦 | 高風險 action 全部 pause,產生 approval packet 與拒收規則 | | 10 | P2-410 | P0 | Agent action audit ledger | Hermes + Security | 待辦 | 每次判斷、交接、建議、拒收、執行結果都有 immutable event | @@ -148,7 +148,7 @@ |---|---:|---|---| | Agent 市場治理 | 72% | 進行中 | `agent_market_governance_snapshot_v1`、API、UI 分頁、每週觀察流程 | | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | -| 工具 / 服務 / 套件 AI 自動化 | 96% | P2-004 依賴 / 供應鏈漂移監控讀回已完成,且已納入 P2-406B receipt readback owner review;下一主線是 P2-407 no-write 報表分析 | 新增 `dependency_supply_chain_drift_monitor_v1` 與 `ai_agent_receipt_readback_owner_review_v1` schema / snapshot / API / tests / governance UI 卡片;9 個 drift candidate、7 個 monitor check、6 個 source readback、6 個 owner gate、8 個 receipt check、17 個 runtime false boundary;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | +| 工具 / 服務 / 套件 AI 自動化 | 97% | P2-004 依賴 / 供應鏈漂移監控讀回已完成,且已納入 P2-406B receipt readback owner review 與 P2-407 no-write analysis;下一主線是 P2-408 中 / 低風險白名單 | 新增 `dependency_supply_chain_drift_monitor_v1`、`ai_agent_receipt_readback_owner_review_v1`、`ai_agent_report_no_write_analysis_runtime_v1` schema / snapshot / API / tests / governance UI 卡片;9 個 drift candidate、5 個 P2-407 source readback、6 筆 draft recommendation、3 個 owner gate;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | | OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 到 P2-144 已完成只讀證據面、runtime / report / result-capture gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold / readback gate、release verifier preflight / owner review packet、release decision hold / readback、release decision next handoff、release decision input prep、12-Agent War Room、owner response 預檢與 owner response 回讀;P2-141 基線與 S4.9 owner release packet 補強皆已正式驗證,P2-142 12-Agent War Room 已完成 production readback 與 desktop / mobile smoke,P2-143 owner response 預檢已完成 production readback 與 in-app browser smoke,P2-144 owner response 回讀已完成 production API readback 與 desktop / mobile smoke。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、live query、runtime score、result capture write、Telegram 實發、delivery receipt E2E、live report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、`docs/evaluations/ai_agent_result_capture_release_decision_owner_response_readback_2026-06-14.json`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個回覆讀回 lane、18 個 owner 必填欄位、6 個 readback validation check、6 個 rejection guard、5 個 operator action、等待外部回覆 `5`、未收件 lane `5`、正式寫入 / 發送 `0`;P2-142 feature commit `5de4b3f3`、deploy marker `1a2c9e36`、Gitea CD run `4232` success、production API readback、desktop / mobile in-app browser smoke;P2-143 feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、production API readback、desktop / mobile in-app browser smoke;MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | @@ -1660,8 +1660,8 @@ UI: 1. P2-004:依賴 / 供應鏈漂移監控讀回已完成;維持只讀觀察與批准包邊界,下一輪不得把完成誤讀成可外部掃描或自動升級。 2. P2-406B:AI Agent Telegram receipt readback owner review / production verification 已本地完成並等待本輪正式驗證;它只確認日報 / 週報 / 月報、P2-004 drift monitor 與 Telegram canary receipt readback gate 在 API / governance UI 可讀,且 Telegram send、Gateway queue、Bot API、receipt production write 全部仍為 `0`。只有收到合格 owner review、receipt readback owner、mute / rollback plan、停止條件與可驗證證據後,才可進一步評估 canary live delivery;未批准前仍不得實發。 -3. P2-407:AI 報表自動分析 no-write runtime 是下一個有效動作;讓 OpenClaw / Hermes / NemoTron 讀取日報 / 週報 / 月報與 P2-004 drift monitor 後產生建議與風險分級,但只寫 committed snapshot / 草稿,不寫 production。 -4. P2-408:中 / 低風險自動處理白名單與高風險審核分流;低風險先 dry-run,中風險需 verifier,高風險進 Owner Review Queue。 +3. P2-407:AI 報表自動分析 no-write runtime 已本地完成,待正式驗證;OpenClaw / Hermes / NemoTron 已讀取日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相後產生 `6` 筆建議與風險分級;只寫 committed snapshot / 草稿,不寫 production。 +4. P2-408:中 / 低風險自動處理白名單與高風險審核分流是下一個有效動作;低風險先 dry-run,中風險需 verifier,高風險進 Owner Review Queue。 5. P2-411:Agent Event Bus / handoff protocol / RAG memory 的 no-write 基線;先讓 Agent 的觀察、判斷、交接、拒收與學習提案可追蹤。 6. P2-412:市場主流 AI Agent 定期評估;刷新 NVIDIA Nemotron、NeMo Agent Toolkit、OpenAI Agents、LangGraph、A2A、MCP、OpenTelemetry GenAI 等 primary sources,形成 scorecard 與候選整合提案。 7. P2-413:AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期 no-write automation;只產版本情報、升級建議、PR 草案 lane 與 rollback plan。 diff --git a/docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json b/docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json new file mode 100644 index 00000000..506e0c6d --- /dev/null +++ b/docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json @@ -0,0 +1,391 @@ +{ + "schema_version": "ai_agent_report_no_write_analysis_runtime_v1", + "generated_at": "2026-06-18T15:20:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-407", + "next_task_id": "P2-408", + "read_only_mode": true, + "runtime_authority": "report_analysis_no_write_runtime_only_committed_snapshot", + "status_note": "P2-407 將日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 供應鏈漂移與 P2-403J 報表真相收斂成 AI 分析草稿;只寫 committed snapshot 與 governance UI,不啟動 live AI worker、不送 Telegram、不寫 Gateway queue、不寫 production。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_report_status_board_2026-06-13.json", + "docs/evaluations/ai_agent_report_automation_review_2026-06-12.json", + "docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/ai_agent_report_truth_actionability_review_2026-06-12.json" + ], + "source_readbacks": [ + { + "readback_id": "report_status_board", + "source_schema_version": "ai_agent_report_status_board_v1", + "source_ref": "docs/evaluations/ai_agent_report_status_board_2026-06-13.json", + "endpoint": "GET /api/v1/agents/agent-report-status-board", + "owner_agent": "hermes", + "status": "readback_ready", + "key_readback": "日報 / 週報 / 月報、Agent 工作量與圖表皆可見,live delivery 仍為 0。", + "next_action": "交給 P2-407 產生 no-write 分析草稿。" + }, + { + "readback_id": "report_automation_review", + "source_schema_version": "ai_agent_report_automation_review_v1", + "source_ref": "docs/evaluations/ai_agent_report_automation_review_2026-06-12.json", + "endpoint": "GET /api/v1/agents/agent-report-automation-review", + "owner_agent": "openclaw", + "status": "readback_ready", + "key_readback": "既有報表建議 5 筆,仍無中低風險 live auto execution。", + "next_action": "轉成 P2-408 白名單草稿,不直接執行。" + }, + { + "readback_id": "receipt_owner_review", + "source_schema_version": "ai_agent_receipt_readback_owner_review_v1", + "source_ref": "docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json", + "endpoint": "GET /api/v1/agents/agent-receipt-readback-owner-review", + "owner_agent": "hermes", + "status": "readback_ready", + "key_readback": "AwoooI SRE 戰情室與 SRE_GROUP_CHAT_ID 僅為 receipt owner review policy,Telegram 實發仍為 0。", + "next_action": "高風險 canary 仍需 owner review。" + }, + { + "readback_id": "dependency_drift_monitor", + "source_schema_version": "dependency_supply_chain_drift_monitor_v1", + "source_ref": "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "endpoint": "GET /api/v1/agents/dependency-supply-chain-drift-monitor", + "owner_agent": "nemotron", + "status": "readback_ready", + "key_readback": "9 個 drift candidate 需 action,但外部查詢、升級、lockfile write 仍全關。", + "next_action": "產生中風險升級建議草稿,不查外部 registry。" + }, + { + "readback_id": "report_truth_actionability", + "source_schema_version": "ai_agent_report_truth_actionability_review_v1", + "source_ref": "docs/evaluations/ai_agent_report_truth_actionability_review_2026-06-12.json", + "endpoint": "GET /api/v1/agents/agent-report-truth-actionability-review", + "owner_agent": "openclaw", + "status": "readback_ready", + "key_readback": "weekly all-zero signal 必須保留為可處置異常,不可被綠色狀態覆蓋。", + "next_action": "納入高風險 owner gate。" + } + ], + "analysis_truth": { + "daily_weekly_monthly_reports_loaded": true, + "agent_workload_loaded": true, + "charts_loaded": true, + "receipt_owner_review_loaded": true, + "dependency_drift_loaded": true, + "report_truth_loaded": true, + "analysis_draft_snapshot_ready": true, + "ai_analysis_runtime_enabled": false, + "report_delivery_enabled": false, + "telegram_send_enabled": false, + "gateway_queue_write_enabled": false, + "bot_api_call_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "live_ai_analysis_run_count_24h": 0, + "live_report_delivery_count_24h": 0, + "telegram_send_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "bot_api_call_count_24h": 0, + "receipt_production_write_count_24h": 0, + "production_write_count_24h": 0, + "truth_note": "這份快照代表 AI Agent 已可用 committed evidence 產生分析建議與風險分級;目前仍是 no-write runtime baseline,不代表已啟動常駐 AI worker。" + }, + "report_inputs": [ + { + "report_id": "daily", + "display_name": "AI Agent 日報", + "owner_agent": "hermes", + "completion_percent": 100, + "source_confidence": "committed_snapshot_ready", + "actionability_score": 72, + "analysis_focus": ["24h 工作量", "等待批准", "Telegram 告警摘要", "下一步"], + "blocked_runtime_action": "report_scheduler_enablement" + }, + { + "report_id": "weekly", + "display_name": "AI Agent 週報", + "owner_agent": "openclaw", + "completion_percent": 100, + "source_confidence": "low_trust_actionable_anomaly", + "actionability_score": 95, + "analysis_focus": ["zero-signal truth gate", "source freshness", "高風險審核清單", "下週 rollout 建議"], + "blocked_runtime_action": "cronjob_change_or_green_badge" + }, + { + "report_id": "monthly", + "display_name": "AI Agent 月報", + "owner_agent": "nemotron", + "completion_percent": 100, + "source_confidence": "committed_snapshot_ready", + "actionability_score": 77, + "analysis_focus": ["市場 Agent 技術引進", "版本生命週期", "成本 / 效益", "供應鏈漂移"], + "blocked_runtime_action": "paid_api_or_market_lookup" + } + ], + "agent_analysis_passes": [ + { + "agent_id": "openclaw", + "display_name": "OpenClaw", + "role": "仲裁者:判定週報 zero-signal、Telegram canary 與中低風險白名單是否可進下一關。", + "reviewed_source_count": 5, + "draft_finding_count": 3, + "highest_risk_tier": "high", + "live_runtime_write_allowed": false, + "summary": "weekly all-zero、Telegram live canary 與 production optimization 全部維持 owner gate;下一步只產 P2-408 白名單草稿。", + "handoff_to": "P2-408 medium_low_risk_whitelist" + }, + { + "agent_id": "hermes", + "display_name": "Hermes", + "role": "報告者:彙整日報 / 週報 / 月報、receipt owner review 與可視化摘要。", + "reviewed_source_count": 4, + "draft_finding_count": 4, + "highest_risk_tier": "medium", + "live_runtime_write_allowed": false, + "summary": "可產生 no-send 報表摘要、owner packet 與 proof chips;仍不得發 Telegram 或寫 receipt target。", + "handoff_to": "P2-406F no-send scheduler" + }, + { + "agent_id": "nemotron", + "display_name": "NemoTron", + "role": "回放驗證者:比對供應鏈漂移、版本情報與 replay 候選,不做外部查詢。", + "reviewed_source_count": 3, + "draft_finding_count": 4, + "highest_risk_tier": "medium", + "live_runtime_write_allowed": false, + "summary": "可把 9 個 drift candidate 轉成升級建議草稿與 replay queue;仍不得查 registry、升級套件、改 lockfile 或呼叫付費 API。", + "handoff_to": "P2-413 version_lifecycle_no_write" + } + ], + "draft_recommendations": [ + { + "recommendation_id": "weekly_zero_signal_truth_gate", + "display_name": "週報 zero-signal 真相 gate", + "owner_agent": "openclaw", + "risk_tier": "high", + "approval_required": true, + "actionability_score": 95, + "problem": "weekly report 可能把資料來源失敗誤顯示成 0,容易讓治理頁看起來過度健康。", + "draft_solution": "保留 all-zero anomaly 標籤、補 freshness / confidence chips,未有 live source readback 前不得給綠燈。", + "expected_signal": "週報會顯示 source gap,而不是顯示已健康。", + "blocked_runtime_action": "cronjob_change_or_green_badge" + }, + { + "recommendation_id": "telegram_receipt_canary_intake", + "display_name": "Telegram canary owner intake", + "owner_agent": "hermes", + "risk_tier": "high", + "approval_required": true, + "actionability_score": 90, + "problem": "已有 AwoooI SRE 戰情室與 receipt gate,但缺合格 owner response accepted ledger。", + "draft_solution": "P2-406C 僅接受遮罩後 owner approval 欄位,缺 rollback、mute、stop condition 或 receipt owner 即拒收。", + "expected_signal": "canary route lock 可審核,但 send / queue / Bot API count 維持 0。", + "blocked_runtime_action": "telegram_send_gateway_queue_write" + }, + { + "recommendation_id": "dependency_drift_owner_packet", + "display_name": "供應鏈漂移 owner packet", + "owner_agent": "nemotron", + "risk_tier": "medium", + "approval_required": false, + "actionability_score": 84, + "problem": "9 個 drift candidate 已可讀,但不能外查 registry 或直接升級。", + "draft_solution": "以 committed manifest / lockfile 狀態產生 no-write 升級建議與 rollback checklist,等 P2-413 接手。", + "expected_signal": "每個 drift candidate 會有 owner、blocked action 與下一步。", + "blocked_runtime_action": "package_upgrade_or_external_lookup" + }, + { + "recommendation_id": "agent_workload_daily_digest", + "display_name": "Agent 工作量日報摘要", + "owner_agent": "hermes", + "risk_tier": "low", + "approval_required": false, + "actionability_score": 72, + "problem": "使用者需要每天看到每個 Agent 做了多少工作,但目前不應 live send。", + "draft_solution": "先在 governance UI 顯示 action count、work units、risk mix 與 blocked rate,P2-406F 再做 no-send scheduler。", + "expected_signal": "日報卡片能讀到 OpenClaw / Hermes / NemoTron 工作量與等待批准數。", + "blocked_runtime_action": "report_scheduler_enablement" + }, + { + "recommendation_id": "medium_low_auto_whitelist_draft", + "display_name": "中低風險自動處理白名單草稿", + "owner_agent": "openclaw", + "risk_tier": "medium", + "approval_required": false, + "actionability_score": 80, + "problem": "中低風險可自動處理的範圍尚未轉成可驗證白名單。", + "draft_solution": "P2-408 只先接受 no-write / idempotent / rollback-proven / verifier-ready 四類條件。", + "expected_signal": "低風險可以自動草稿化,中風險需 verifier proof,高風險進 owner queue。", + "blocked_runtime_action": "medium_low_auto_worker_enablement" + }, + { + "recommendation_id": "market_agent_weekly_watch_queue", + "display_name": "市場 Agent 週期追蹤 queue", + "owner_agent": "nemotron", + "risk_tier": "medium", + "approval_required": false, + "actionability_score": 77, + "problem": "Nemotron / Agent SDK / MCP / A2A 等市場版本需週期更新,但外部 lookup 與付費 API 仍需 gate。", + "draft_solution": "先建立 no-write watch queue 與 scorecard 欄位,P2-412 才刷新 primary sources。", + "expected_signal": "每週可產候選清單、source freshness 與 scorecard 差距,但不自動替換 OpenClaw。", + "blocked_runtime_action": "paid_api_or_market_lookup" + } + ], + "draft_artifacts": [ + { + "artifact_id": "p2_407_report_analysis_snapshot", + "display_name": "P2-407 committed analysis snapshot", + "owner_agent": "hermes", + "status": "committed_snapshot_ready", + "writes_production": false, + "sends_telegram": false, + "contains_secret": false, + "evidence_ref": "docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json" + }, + { + "artifact_id": "p2_408_whitelist_draft", + "display_name": "P2-408 medium / low whitelist draft", + "owner_agent": "openclaw", + "status": "draft_only", + "writes_production": false, + "sends_telegram": false, + "contains_secret": false, + "evidence_ref": "docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md#p2-408" + }, + { + "artifact_id": "p2_406f_no_send_scheduler_draft", + "display_name": "P2-406F no-send scheduler draft", + "owner_agent": "hermes", + "status": "owner_review_required", + "writes_production": false, + "sends_telegram": false, + "contains_secret": false, + "evidence_ref": "docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md#p2-406f" + }, + { + "artifact_id": "p2_413_version_lifecycle_queue", + "display_name": "P2-413 version lifecycle queue", + "owner_agent": "nemotron", + "status": "draft_only", + "writes_production": false, + "sends_telegram": false, + "contains_secret": false, + "evidence_ref": "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json" + } + ], + "owner_review_gates": [ + { + "gate_id": "telegram_canary_owner_response_gate", + "display_name": "Telegram canary owner response gate", + "owner_agent": "hermes", + "risk_tier": "high", + "status": "owner_review_required", + "required_fields": ["owner_identity", "canonical_room", "rollback_plan", "mute_plan", "stop_conditions", "receipt_owner"], + "acceptance_checks": ["all_fields_present", "redacted_owner_packet", "send_count_zero_before_approval"], + "blocked_runtime_actions": ["telegram_live_delivery"] + }, + { + "gate_id": "report_truth_owner_gate", + "display_name": "Report truth owner gate", + "owner_agent": "openclaw", + "risk_tier": "high", + "status": "owner_review_required", + "required_fields": ["source_freshness", "confidence_score", "operator_ack"], + "acceptance_checks": ["zero_signal_not_green", "source_gap_visible", "owner_packet_ready"], + "blocked_runtime_actions": ["ai_analysis_live_runtime"] + }, + { + "gate_id": "production_optimization_gate", + "display_name": "Production optimization gate", + "owner_agent": "openclaw", + "risk_tier": "high", + "status": "blocked_by_runtime_gate", + "required_fields": ["dry_run_result", "verifier_result", "rollback_evidence", "blast_radius"], + "acceptance_checks": ["p2_408_whitelist_passed", "verifier_passed", "human_approval_when_high_risk"], + "blocked_runtime_actions": ["production_optimization_write"] + } + ], + "activation_boundaries": { + "read_only_analysis_allowed": true, + "draft_snapshot_write_allowed": true, + "ai_analysis_runtime_enabled": false, + "report_delivery_enabled": false, + "telegram_send_enabled": false, + "gateway_queue_write_enabled": false, + "bot_api_call_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false, + "openclaw_replacement_allowed": false + }, + "telegram_policy": { + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_queue_write_allowed": false, + "direct_bot_api_allowed": false, + "telegram_send_allowed": false, + "receipt_write_allowed": false, + "policy_note": "P2-407 只產分析草稿;Telegram 只作為目標政策讀回,不發送。" + }, + "display_redaction_contract": { + "redaction_required": true, + "raw_report_payload_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "work_window_transcript_display_allowed": false, + "allowed_display_fields": ["committed evidence refs", "rollups", "risk tier", "agent owner", "blocked action", "next task"], + "blocked_display_fields": ["prompt bodies", "private inference text", "credential material", "authorization headers", "browser context", "session transcript"] + }, + "rollups": { + "source_readback_count": 5, + "report_input_count": 3, + "agent_analysis_pass_count": 3, + "draft_recommendation_count": 6, + "draft_artifact_count": 4, + "owner_review_gate_count": 3, + "approval_required_recommendation_count": 2, + "approval_required_recommendation_ids": ["weekly_zero_signal_truth_gate", "telegram_receipt_canary_intake"], + "low_risk_recommendation_count": 1, + "medium_risk_recommendation_count": 3, + "high_risk_recommendation_count": 2, + "critical_risk_recommendation_count": 0, + "actionability_score_ready_count": 6, + "blocked_runtime_action_count": 9, + "live_report_delivery_count": 0, + "live_ai_analysis_count": 0, + "telegram_send_count": 0, + "gateway_queue_write_count": 0, + "bot_api_call_count": 0, + "receipt_production_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "paid_api_call_count": 0, + "host_write_count": 0, + "kubectl_action_count": 0 + }, + "next_actions": [ + { + "task_id": "P2-408", + "priority": "P0", + "summary": "把 P2-407 no-write 分析建議轉成中 / 低風險白名單、dry-run verifier 與 rollback proof。", + "gate": "仍不得 live auto execution;高風險進 owner queue。" + }, + { + "task_id": "P2-406C", + "priority": "P0", + "summary": "Telegram canary route lock 只能處理遮罩後 owner approval intake。", + "gate": "未有完整 owner fields 前 send / queue / Bot API 仍為 0。" + } + ] +} diff --git a/docs/schemas/ai_agent_report_no_write_analysis_runtime_v1.schema.json b/docs/schemas/ai_agent_report_no_write_analysis_runtime_v1.schema.json new file mode 100644 index 00000000..133836f3 --- /dev/null +++ b/docs/schemas/ai_agent_report_no_write_analysis_runtime_v1.schema.json @@ -0,0 +1,220 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-agent-report-no-write-analysis-runtime-v1", + "title": "AWOOOI AI Agent report no-write analysis runtime v1", + "description": "P2-407 讓 OpenClaw、Hermes 與 NemoTron 讀取日報 / 週報 / 月報、P2-406B receipt owner review 與 P2-004 供應鏈漂移監控後產生分析草稿。此 schema 只允許 committed snapshot / governance UI 呈現,不授權 AI live runtime、Telegram 實發、Gateway queue 寫入、Bot API、receipt production write、production write、secret 讀取、付費 API、host write、kubectl 或不可逆操作。", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "source_readbacks", + "analysis_truth", + "report_inputs", + "agent_analysis_passes", + "draft_recommendations", + "draft_artifacts", + "owner_review_gates", + "activation_boundaries", + "telegram_policy", + "display_redaction_contract", + "rollups", + "next_actions" + ], + "properties": { + "schema_version": { "type": "string", "const": "ai_agent_report_no_write_analysis_runtime_v1" }, + "generated_at": { "type": "string", "minLength": 1 }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_priority", + "current_task_id", + "next_task_id", + "read_only_mode", + "runtime_authority", + "status_note" + ], + "properties": { + "overall_completion_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "current_priority": { "type": "string", "enum": ["P0", "P1", "P2", "P3"] }, + "current_task_id": { "type": "string", "const": "P2-407" }, + "next_task_id": { "type": "string", "const": "P2-408" }, + "read_only_mode": { "type": "boolean", "const": true }, + "runtime_authority": { "type": "string", "const": "report_analysis_no_write_runtime_only_committed_snapshot" }, + "status_note": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, + "source_readbacks": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/source_readback" } }, + "analysis_truth": { + "type": "object", + "additionalProperties": { "type": ["boolean", "integer", "string"] } + }, + "report_inputs": { "type": "array", "minItems": 3, "items": { "$ref": "#/$defs/report_input" } }, + "agent_analysis_passes": { "type": "array", "minItems": 3, "items": { "$ref": "#/$defs/agent_analysis_pass" } }, + "draft_recommendations": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/draft_recommendation" } }, + "draft_artifacts": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/draft_artifact" } }, + "owner_review_gates": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/owner_review_gate" } }, + "activation_boundaries": { + "type": "object", + "additionalProperties": { "type": "boolean" } + }, + "telegram_policy": { + "type": "object", + "required": [ + "canonical_room", + "canonical_room_env", + "gateway_queue_write_allowed", + "direct_bot_api_allowed", + "telegram_send_allowed", + "receipt_write_allowed" + ], + "properties": { + "canonical_room": { "type": "string", "const": "AwoooI SRE 戰情室" }, + "canonical_room_env": { "type": "string", "const": "SRE_GROUP_CHAT_ID" }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "direct_bot_api_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "receipt_write_allowed": { "type": "boolean", "const": false } + }, + "additionalProperties": true + }, + "display_redaction_contract": { + "type": "object", + "required": [ + "redaction_required", + "raw_report_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "work_window_transcript_display_allowed", + "allowed_display_fields", + "blocked_display_fields" + ], + "properties": { + "redaction_required": { "type": "boolean", "const": true }, + "raw_report_payload_display_allowed": { "type": "boolean", "const": false }, + "private_reasoning_display_allowed": { "type": "boolean", "const": false }, + "secret_value_display_allowed": { "type": "boolean", "const": false }, + "work_window_transcript_display_allowed": { "type": "boolean", "const": false }, + "allowed_display_fields": { "type": "array", "items": { "type": "string" } }, + "blocked_display_fields": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "rollups": { + "type": "object", + "additionalProperties": { "type": ["integer", "array"] } + }, + "next_actions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/next_action" } } + }, + "additionalProperties": false, + "$defs": { + "source_readback": { + "type": "object", + "required": ["readback_id", "source_schema_version", "source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"], + "properties": { + "readback_id": { "type": "string", "minLength": 1 }, + "source_schema_version": { "type": "string", "minLength": 1 }, + "source_ref": { "type": "string", "minLength": 1 }, + "endpoint": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "type": "string", "minLength": 1 }, + "key_readback": { "type": "string", "minLength": 1 }, + "next_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "report_input": { + "type": "object", + "required": ["report_id", "display_name", "owner_agent", "completion_percent", "source_confidence", "actionability_score", "analysis_focus", "blocked_runtime_action"], + "properties": { + "report_id": { "enum": ["daily", "weekly", "monthly"] }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "completion_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "source_confidence": { "type": "string", "minLength": 1 }, + "actionability_score": { "type": "integer", "minimum": 0, "maximum": 100 }, + "analysis_focus": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "blocked_runtime_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "agent_analysis_pass": { + "type": "object", + "required": ["agent_id", "display_name", "role", "reviewed_source_count", "draft_finding_count", "highest_risk_tier", "live_runtime_write_allowed", "summary", "handoff_to"], + "properties": { + "agent_id": { "enum": ["openclaw", "hermes", "nemotron"] }, + "display_name": { "type": "string", "minLength": 1 }, + "role": { "type": "string", "minLength": 1 }, + "reviewed_source_count": { "type": "integer", "minimum": 0 }, + "draft_finding_count": { "type": "integer", "minimum": 0 }, + "highest_risk_tier": { "enum": ["low", "medium", "high", "critical"] }, + "live_runtime_write_allowed": { "type": "boolean", "const": false }, + "summary": { "type": "string", "minLength": 1 }, + "handoff_to": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "draft_recommendation": { + "type": "object", + "required": ["recommendation_id", "display_name", "owner_agent", "risk_tier", "approval_required", "actionability_score", "problem", "draft_solution", "expected_signal", "blocked_runtime_action"], + "properties": { + "recommendation_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "risk_tier": { "enum": ["low", "medium", "high", "critical"] }, + "approval_required": { "type": "boolean" }, + "actionability_score": { "type": "integer", "minimum": 0, "maximum": 100 }, + "problem": { "type": "string", "minLength": 1 }, + "draft_solution": { "type": "string", "minLength": 1 }, + "expected_signal": { "type": "string", "minLength": 1 }, + "blocked_runtime_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "draft_artifact": { + "type": "object", + "required": ["artifact_id", "display_name", "owner_agent", "status", "writes_production", "sends_telegram", "contains_secret", "evidence_ref"], + "properties": { + "artifact_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "enum": ["committed_snapshot_ready", "draft_only", "owner_review_required", "blocked_by_runtime_gate"] }, + "writes_production": { "type": "boolean", "const": false }, + "sends_telegram": { "type": "boolean", "const": false }, + "contains_secret": { "type": "boolean", "const": false }, + "evidence_ref": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "owner_review_gate": { + "type": "object", + "required": ["gate_id", "display_name", "owner_agent", "risk_tier", "status", "required_fields", "acceptance_checks", "blocked_runtime_actions"], + "properties": { + "gate_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "risk_tier": { "enum": ["low", "medium", "high", "critical"] }, + "status": { "enum": ["owner_review_required", "blocked_by_runtime_gate", "draft_ready"] }, + "required_fields": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "acceptance_checks": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "blocked_runtime_actions": { "type": "array", "minItems": 1, "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "required": ["task_id", "priority", "summary", "gate"], + "properties": { + "task_id": { "type": "string", "minLength": 1 }, + "priority": { "enum": ["P0", "P1", "P2", "P3"] }, + "summary": { "type": "string", "minLength": 1 }, + "gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + } +} diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index 994fa84b..5835686c 100644 --- a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md +++ b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md @@ -5105,6 +5105,20 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 **裁決:** P2-406B 正式驗證完成;P2-406C / P2-407 可以接手,但仍不得 Telegram 實發、Gateway queue write、Bot API call、receipt production write、AI analysis live runtime、production write、secret read、paid API、host write 或 kubectl action。 +### 2026-06-18 15:20 (台北) — §8 / P2-407 — 新增 AI 報表 no-write 分析 runtime — 把日週月報、receipt owner review 與漂移監控轉成可審核建議 + +**觸發**:P2-406B 已正式驗證 receipt readback owner review;下一步必須讓 AI Agent 真正「讀報表後提出建議」,但仍不能把分析草稿誤開成 live worker、Telegram 實發或 production write。 + +**已推進:** +- 新增 `docs/schemas/ai_agent_report_no_write_analysis_runtime_v1.schema.json` 與 `docs/evaluations/ai_agent_report_no_write_analysis_runtime_2026-06-18.json`。 +- 新增 `apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py` 與 `GET /api/v1/agents/agent-report-no-write-analysis-runtime`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-407 卡片,顯示 P2-407 no-write 分析、source readback、Agent analysis pass、draft recommendation、owner gate、redaction 與 live total。 +- P2-407 固定 `5` 個 source readback、`3` 份 report input、`3` 個 Agent analysis pass、`6` 筆 draft recommendation、`4` 個 draft artifact、`3` 個 owner review gate、`9` 個 blocked runtime action。 +- OpenClaw 負責高風險 truth / whitelist 仲裁;Hermes 負責報告摘要與 receipt owner packet;NemoTron 負責供應鏈漂移與版本生命週期 no-write replay / queue 草稿。 +- 本地驗證:新 schema / snapshot / i18n JSON parse 通過;`python3 -m py_compile apps/api/src/services/ai_agent_report_no_write_analysis_runtime.py` 通過;目標 service / API tests `9 passed`;i18n parity `zh 12648 / en 12648 / missingEn 0 / missingZh 0 / typeDiff 0`。 + +**裁決:** P2-407 是 committed snapshot / API / governance UI 的 no-write 分析基線,不是 live AI runtime、report scheduler、Telegram send、Gateway queue write、Bot API call、receipt production write、production optimization、secret read、paid API、host write、kubectl action、destructive operation 或 OpenClaw 替換。下一步是 `P2-408`:中 / 低風險自動處理白名單、dry-run verifier、rollback proof 與高風險 Owner Review Queue。 + ### 2026-06-18 14:20 (台北) — §8 / Host CPU AIOps — 新增 110 runaway process 監控 / 告警 / PlayBook / gated remediation **觸發**:110 CPU 滿載已確認是跨專案 stockPlatform headless Chrome smoke 遺留 5 組 orphan process group,精準 SIGTERM 後 `REMAINING_AFTER_TERM=0`;後續 load 仍高則是 AWOOOI / VibeWork / 2026 World Cup Gitea Actions build/test。這證明泛用 `HostHighCpuLoad` 不足以支撐 AI 自動化產品,必須能把 orphan process、合法 CI load、Docker/Sentry/Harbor 事故分開。