diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index ff1875769..0617681d3 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -103,6 +103,9 @@ from src.services.ai_agent_proactive_operations_contract import ( from src.services.ai_agent_redis_dry_run_gate import ( load_latest_ai_agent_redis_dry_run_gate, ) +from src.services.ai_agent_runtime_readback_approval_package import ( + load_latest_ai_agent_runtime_readback_approval_package, +) from src.services.ai_agent_report_automation_review import ( load_latest_ai_agent_report_automation_review, ) @@ -1292,6 +1295,37 @@ async def get_agent_owner_approved_result_capture_readback() -> dict[str, Any]: ) from exc +@router.get( + "/agent-runtime-readback-approval-package", + response_model=dict[str, Any], + summary="取得 AI Agent runtime readback 批准包", + description=( + "讀取最新已提交的 P2-108 runtime readback approval package;" + "此端點只回傳 approval packet、canonical readback plan、rollback drill、" + "Telegram 失敗收據 gate 與 operator action," + "不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、" + "不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 rollback work item、" + "不寫 production target、不讀 secret。" + ), +) +async def get_agent_runtime_readback_approval_package() -> dict[str, Any]: + """Return the latest read-only runtime readback approval package.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_runtime_readback_approval_package) + 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_runtime_readback_approval_package_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent runtime readback approval package 無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_runtime_readback_approval_package.py b/apps/api/src/services/ai_agent_runtime_readback_approval_package.py new file mode 100644 index 000000000..d809a80bf --- /dev/null +++ b/apps/api/src/services/ai_agent_runtime_readback_approval_package.py @@ -0,0 +1,390 @@ +""" +AI Agent runtime readback approval package snapshot. + +Loads the latest committed P2-109 runtime readback approval package. This +module validates repo-committed evidence only; it never reads canonical runtime +targets, writes result capture rows, writes scores, writes learning state, +updates PlayBook trust, writes reviewer/Gateway queues, sends Telegram +messages, calls Bot API, writes rollback work items, writes production targets, +reads secrets, or runs destructive operations. +""" + +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_runtime_readback_approval_package_*.json" +_SCHEMA_VERSION = "ai_agent_runtime_readback_approval_package_v1" +_RUNTIME_AUTHORITY = "runtime_readback_approval_package_only_no_live_write" + + +def load_latest_ai_agent_runtime_readback_approval_package( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed runtime readback approval package.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent runtime readback approval package 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") + _require_schema(payload, str(latest)) + _require_prior_readback(payload, str(latest)) + _require_approval_truth(payload, str(latest)) + _require_approval_packets(payload, str(latest)) + _require_canonical_readback_plans(payload, str(latest)) + _require_rollback_drill_lanes(payload, str(latest)) + _require_telegram_failure_receipt_gates(payload, str(latest)) + _require_operator_actions(payload, str(latest)) + _require_display_redaction(payload, str(latest)) + _require_no_forbidden_display_terms(payload, str(latest)) + _require_rollup_consistency(payload, str(latest)) + 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 {} + if status.get("read_only_mode") is not True: + raise ValueError(f"{label}: program_status.read_only_mode must be true") + if status.get("runtime_authority") != _RUNTIME_AUTHORITY: + raise ValueError(f"{label}: runtime_authority must remain {_RUNTIME_AUTHORITY}") + if status.get("current_task_id") != "P2-109": + raise ValueError(f"{label}: current_task_id must be P2-109") + if status.get("next_task_id") != "P2-110": + raise ValueError(f"{label}: next_task_id must be P2-110") + + +def _require_prior_readback(payload: dict[str, Any], label: str) -> None: + prior = payload.get("prior_readback_readiness") or {} + if prior.get("source_schema_version") != "ai_agent_owner_approved_result_capture_readback_v1": + raise ValueError(f"{label}: prior_readback_readiness must chain from P2-107") + required_counts = { + "readback_digest_count": 5, + "promotion_review_count": 5, + "failure_lane_count": 4, + "reviewer_queue_preview_count": 4, + "operator_action_count": 5, + "approval_required_digest_count": 2, + "blocked_total_count": 7, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "readback_digest_generated_count": 0, + "promotion_approved_count": 0, + "reviewer_queue_write_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + } + mismatches = { + key: {"expected": expected, "actual": prior.get(key)} + for key, expected in required_counts.items() + if prior.get(key) != expected + } + if mismatches: + raise ValueError(f"{label}: P2-107 prior readback counts mismatch: {mismatches}") + + +def _require_approval_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("approval_package_truth") or {} + required_true = { + "p2_107_readback_loaded", + "approval_package_ready", + "canonical_readback_plan_ready", + "rollback_drill_ready", + "telegram_failure_receipt_gate_ready", + "owner_review_required_before_runtime", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: approval readiness flags must remain true: {missing}") + + required_false = { + "canonical_runtime_readback_enabled", + "runtime_result_capture_write_enabled", + "runtime_score_write_enabled", + "runtime_learning_write_enabled", + "playbook_trust_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_failure_receipt_send_enabled", + "bot_api_call_enabled", + "rollback_work_item_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled", + } + unsafe = sorted(field for field in required_false if truth.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: runtime read/write/send flags must remain false: {unsafe}") + + zero_counts = { + "owner_approval_received_count", + "runtime_readback_execution_count", + "result_capture_write_count_24h", + "score_write_count_24h", + "learning_write_count_24h", + "playbook_trust_write_count_24h", + "reviewer_queue_write_count_24h", + "gateway_queue_write_count_24h", + "telegram_failure_receipt_send_count_24h", + "bot_api_call_count_24h", + "rollback_work_item_write_count_24h", + "production_write_count_24h", + "secret_read_count_24h", + "destructive_operation_count_24h", + } + non_zero = sorted(field for field in zero_counts if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: approval package live counters must remain zero: {non_zero}") + + +def _require_approval_packets(payload: dict[str, Any], label: str) -> None: + packets = payload.get("approval_packet_templates") or [] + packet_ids = {packet.get("packet_id") for packet in packets} + required = { + "approved_result_capture_runtime_readback", + "execution_failed_negative_learning_review", + "pending_human_gate_expiry_review", + "manual_or_noop_result_acceptance", + "post_write_verifier_receipt_package", + } + if packet_ids != required: + raise ValueError(f"{label}: approval packet templates must match {sorted(required)}") + + valid_statuses = {"ready_for_owner_review", "approval_required", "blocked_by_policy"} + valid_tiers = {"medium", "high", "critical"} + for packet in packets: + packet_id = packet.get("packet_id") + if packet.get("status") not in valid_statuses: + raise ValueError(f"{label}: packet {packet_id} status is invalid") + if packet.get("risk_tier") not in valid_tiers: + raise ValueError(f"{label}: packet {packet_id} risk_tier is invalid") + if packet.get("approval_required") is not True: + raise ValueError(f"{label}: packet {packet_id} approval_required must remain true") + if packet.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: packet {packet_id} runtime_write_allowed must remain false") + if not packet.get("required_approval_fields") or not packet.get("blocked_runtime_actions"): + raise ValueError(f"{label}: packet {packet_id} must list approval fields and blocked actions") + if not _is_redacted_sha256(packet.get("evidence_hash")): + raise ValueError(f"{label}: packet {packet_id} must expose evidence_hash") + + +def _require_canonical_readback_plans(payload: dict[str, Any], label: str) -> None: + plans = payload.get("canonical_readback_plans") or [] + plan_ids = {plan.get("plan_id") for plan in plans} + required = { + "result_capture_row_readback", + "score_fixture_parity_readback", + "playbook_trust_noop_readback", + "verifier_receipt_readback", + } + if plan_ids != required: + raise ValueError(f"{label}: canonical readback plans must match {sorted(required)}") + for plan in plans: + plan_id = plan.get("plan_id") + if plan.get("status") not in {"planned_no_runtime_read", "blocked_by_policy"}: + raise ValueError(f"{label}: canonical readback plan {plan_id} status is invalid") + if plan.get("live_query_enabled") is not False: + raise ValueError(f"{label}: canonical readback plan {plan_id} live_query_enabled must remain false") + if plan.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: canonical readback plan {plan_id} runtime_write_allowed must remain false") + if not plan.get("required_fields") or not plan.get("redaction_checks"): + raise ValueError(f"{label}: canonical readback plan {plan_id} must list required fields and redaction checks") + if not _is_redacted_sha256(plan.get("evidence_hash")): + raise ValueError(f"{label}: canonical readback plan {plan_id} must expose evidence_hash") + + +def _require_rollback_drill_lanes(payload: dict[str, Any], label: str) -> None: + lanes = payload.get("rollback_drill_lanes") or [] + lane_ids = {lane.get("lane_id") for lane in lanes} + required = { + "result_capture_insert_rollback_drill", + "score_write_rollback_drill", + "learning_write_rollback_drill", + "telegram_receipt_rollback_drill", + } + if lane_ids != required: + raise ValueError(f"{label}: rollback drill lanes must match {sorted(required)}") + for lane in lanes: + lane_id = lane.get("lane_id") + if lane.get("status") not in {"planned_no_write", "blocked_by_policy"}: + raise ValueError(f"{label}: rollback drill lane {lane_id} status is invalid") + if lane.get("rollback_write_enabled") is not False: + raise ValueError(f"{label}: rollback drill lane {lane_id} rollback_write_enabled must remain false") + if lane.get("creates_runtime_write") is not False: + raise ValueError(f"{label}: rollback drill lane {lane_id} creates_runtime_write must remain false") + if not lane.get("rollback_owner") or not lane.get("required_steps"): + raise ValueError(f"{label}: rollback drill lane {lane_id} must list owner and required steps") + + +def _require_telegram_failure_receipt_gates(payload: dict[str, Any], label: str) -> None: + gates = payload.get("telegram_failure_receipt_gates") or [] + gate_ids = {gate.get("gate_id") for gate in gates} + required = { + "receipt_payload_redaction", + "sre_war_room_route_lock", + "delivery_correlation", + "duplicate_suppression", + } + if gate_ids != required: + raise ValueError(f"{label}: Telegram failure receipt gates must match {sorted(required)}") + for gate in gates: + gate_id = gate.get("gate_id") + if gate.get("status") not in {"ready_for_owner_review", "blocked_by_policy"}: + raise ValueError(f"{label}: Telegram failure gate {gate_id} status is invalid") + if gate.get("queue_write_enabled") is not False: + raise ValueError(f"{label}: Telegram failure gate {gate_id} queue_write_enabled must remain false") + if gate.get("telegram_send_enabled") is not False: + raise ValueError(f"{label}: Telegram failure gate {gate_id} telegram_send_enabled must remain false") + if gate.get("bot_api_call_enabled") is not False: + raise ValueError(f"{label}: Telegram failure gate {gate_id} bot_api_call_enabled must remain false") + if not gate.get("required_evidence"): + raise ValueError(f"{label}: Telegram failure gate {gate_id} must list required evidence") + if not _is_redacted_sha256(gate.get("evidence_hash")): + raise ValueError(f"{label}: Telegram failure gate {gate_id} must expose evidence_hash") + + +def _require_operator_actions(payload: dict[str, Any], label: str) -> None: + actions = payload.get("operator_actions") or [] + action_types = {action.get("action_type") for action in actions} + required = { + "review_approval_package", + "validate_canonical_plan", + "review_rollback_drill", + "review_failure_receipt_gate", + "reject_or_promote", + } + if action_types != required: + raise ValueError(f"{label}: operator actions must match {sorted(required)}") + for action in actions: + if action.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: operator action {action.get('action_id')} must not allow runtime write") + if not action.get("operator_instruction"): + raise ValueError(f"{label}: operator action {action.get('action_id')} must include instruction") + + +def _require_display_redaction(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + if contract.get("redaction_required") is not True: + raise ValueError(f"{label}: display redaction must remain required") + required_false = { + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed", + } + unsafe = sorted(field for field in required_false if contract.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: display redaction fields must remain false: {unsafe}") + + +def _require_no_forbidden_display_terms(payload: dict[str, Any], label: str) -> None: + forbidden_terms = { + "工作視窗", + "對話內容", + "批准!繼續", + "In app browser", + "My request for Codex", + "browser_context", + "codex_user_message", + "prompt_text", + "raw prompt", + "private reasoning", + "chain of thought", + "private_reasoning", + "chain_of_thought", + "authorization_header", + "work window transcript", + "internal collaboration transcript", + } + hits: list[str] = [] + + def walk(value: Any, path: str) -> None: + if isinstance(value, dict): + for key, nested in value.items(): + walk(nested, f"{path}.{key}" if path else str(key)) + return + if isinstance(value, list): + for index, nested in enumerate(value): + walk(nested, f"{path}[{index}]") + return + if isinstance(value, str): + matched = sorted(term for term in forbidden_terms if term in value) + if matched: + hits.append(f"{path}: {', '.join(matched)}") + + walk(payload, "") + if hits: + raise ValueError(f"{label}: forbidden display terms found: {hits}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + truth = payload.get("approval_package_truth") or {} + prior = payload.get("prior_readback_readiness") or {} + packets = payload.get("approval_packet_templates") or [] + plans = payload.get("canonical_readback_plans") or [] + rollback_lanes = payload.get("rollback_drill_lanes") or [] + telegram_gates = payload.get("telegram_failure_receipt_gates") or [] + actions = payload.get("operator_actions") or [] + expected = { + "approval_packet_count": len(packets), + "canonical_readback_plan_count": len(plans), + "rollback_drill_lane_count": len(rollback_lanes), + "telegram_failure_receipt_gate_count": len(telegram_gates), + "operator_action_count": len(actions), + "approval_required_packet_count": sum(1 for item in packets if item.get("status") == "approval_required"), + "blocked_packet_count": sum(1 for item in packets if item.get("status") == "blocked_by_policy"), + "blocked_readback_plan_count": sum(1 for item in plans if item.get("status") == "blocked_by_policy"), + "blocked_rollback_lane_count": sum(1 for item in rollback_lanes if item.get("status") == "blocked_by_policy"), + "blocked_telegram_gate_count": sum(1 for item in telegram_gates if item.get("status") == "blocked_by_policy"), + "approved_without_execution_meta_24h": prior.get("approved_without_execution_meta_24h"), + "execution_failed_with_matched_24h": prior.get("execution_failed_with_matched_24h"), + "owner_approval_received_count": truth.get("owner_approval_received_count"), + "runtime_readback_execution_count": truth.get("runtime_readback_execution_count"), + "result_capture_write_count": truth.get("result_capture_write_count_24h"), + "score_write_count": truth.get("score_write_count_24h"), + "learning_write_count": truth.get("learning_write_count_24h"), + "playbook_trust_write_count": truth.get("playbook_trust_write_count_24h"), + "reviewer_queue_write_count": truth.get("reviewer_queue_write_count_24h"), + "gateway_queue_write_count": truth.get("gateway_queue_write_count_24h"), + "telegram_failure_receipt_send_count": truth.get("telegram_failure_receipt_send_count_24h"), + "bot_api_call_count": truth.get("bot_api_call_count_24h"), + "rollback_work_item_write_count": truth.get("rollback_work_item_write_count_24h"), + "production_write_count": truth.get("production_write_count_24h"), + "secret_read_count": truth.get("secret_read_count_24h"), + "destructive_operation_count": truth.get("destructive_operation_count_24h"), + } + mismatches = { + key: {"expected": expected_value, "actual": rollups.get(key)} + for key, expected_value in expected.items() + if rollups.get(key) != expected_value + } + if mismatches: + raise ValueError(f"{label}: rollup counts mismatch: {mismatches}") + + +def _is_redacted_sha256(value: Any) -> bool: + if not isinstance(value, str): + return False + if not value.startswith("sha256:") or len(value) != 71: + return False + return all(char in "0123456789abcdef" for char in value.removeprefix("sha256:")) diff --git a/apps/api/tests/test_ai_agent_runtime_readback_approval_package.py b/apps/api/tests/test_ai_agent_runtime_readback_approval_package.py new file mode 100644 index 000000000..f8c6c9039 --- /dev/null +++ b/apps/api/tests/test_ai_agent_runtime_readback_approval_package.py @@ -0,0 +1,132 @@ +import copy +import json + +import pytest + +from src.services.ai_agent_runtime_readback_approval_package import ( + load_latest_ai_agent_runtime_readback_approval_package, +) + + +def _write_snapshot(tmp_path, payload): + path = tmp_path / "ai_agent_runtime_readback_approval_package_2026-06-13.json" + path.write_text(json.dumps(payload), encoding="utf-8") + return path + + +def test_load_latest_ai_agent_runtime_readback_approval_package(): + data = load_latest_ai_agent_runtime_readback_approval_package() + + assert data["schema_version"] == "ai_agent_runtime_readback_approval_package_v1" + assert data["program_status"]["current_task_id"] == "P2-109" + assert data["program_status"]["next_task_id"] == "P2-110" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_readback_readiness"]["source_schema_version"] == ( + "ai_agent_owner_approved_result_capture_readback_v1" + ) + assert data["prior_readback_readiness"]["readback_digest_count"] == 5 + assert data["prior_readback_readiness"]["blocked_total_count"] == 7 + assert data["approval_package_truth"]["p2_107_readback_loaded"] is True + assert data["approval_package_truth"]["approval_package_ready"] is True + assert data["approval_package_truth"]["canonical_readback_plan_ready"] is True + assert data["approval_package_truth"]["rollback_drill_ready"] is True + assert data["approval_package_truth"]["telegram_failure_receipt_gate_ready"] is True + assert data["approval_package_truth"]["canonical_runtime_readback_enabled"] is False + assert data["approval_package_truth"]["telegram_failure_receipt_send_enabled"] is False + assert data["approval_package_truth"]["bot_api_call_enabled"] is False + assert data["approval_package_truth"]["rollback_work_item_write_enabled"] is False + assert data["approval_package_truth"]["runtime_readback_execution_count"] == 0 + assert data["approval_package_truth"]["telegram_failure_receipt_send_count_24h"] == 0 + assert data["rollups"]["approval_packet_count"] == 5 + assert data["rollups"]["canonical_readback_plan_count"] == 4 + assert data["rollups"]["rollback_drill_lane_count"] == 4 + assert data["rollups"]["telegram_failure_receipt_gate_count"] == 4 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["approval_required_packet_count"] == 3 + assert data["rollups"]["blocked_packet_count"] == 1 + assert data["rollups"]["blocked_readback_plan_count"] == 1 + assert data["rollups"]["blocked_rollback_lane_count"] == 2 + assert data["rollups"]["blocked_telegram_gate_count"] == 1 + assert data["rollups"]["owner_approval_received_count"] == 0 + assert data["rollups"]["runtime_readback_execution_count"] == 0 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["reviewer_queue_write_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["telegram_failure_receipt_send_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["rollback_work_item_write_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + + +def test_rejects_runtime_readback_enabled(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["approval_package_truth"]["canonical_runtime_readback_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="runtime read/write/send flags"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_runtime_execution_count(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["approval_package_truth"]["runtime_readback_execution_count"] = 1 + bad["rollups"]["runtime_readback_execution_count"] = 1 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live counters"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_packet_runtime_write(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["approval_packet_templates"][0]["runtime_write_allowed"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="runtime_write_allowed"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_canonical_live_query(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["canonical_readback_plans"][0]["live_query_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live_query_enabled"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_telegram_send_enabled(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["telegram_failure_receipt_gates"][0]["telegram_send_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="telegram_send_enabled"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["operator_actions"][0]["operator_instruction"] = "不得顯示工作視窗內容" + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="forbidden display terms"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) + + +def test_rejects_rollup_mismatch(tmp_path): + data = load_latest_ai_agent_runtime_readback_approval_package() + bad = copy.deepcopy(data) + bad["rollups"]["approval_packet_count"] = 999 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_runtime_readback_approval_package(tmp_path) diff --git a/apps/api/tests/test_ai_agent_runtime_readback_approval_package_api.py b/apps/api/tests/test_ai_agent_runtime_readback_approval_package_api.py new file mode 100644 index 000000000..9335766fd --- /dev/null +++ b/apps/api/tests/test_ai_agent_runtime_readback_approval_package_api.py @@ -0,0 +1,48 @@ +from fastapi.testclient import TestClient + +from src.main import app + + +def test_get_ai_agent_runtime_readback_approval_package_api(): + client = TestClient(app) + response = client.get("/api/v1/agents/agent-runtime-readback-approval-package") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_runtime_readback_approval_package_v1" + assert data["program_status"]["current_task_id"] == "P2-109" + assert data["program_status"]["next_task_id"] == "P2-110" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_readback_readiness"]["source_schema_version"] == ( + "ai_agent_owner_approved_result_capture_readback_v1" + ) + assert data["prior_readback_readiness"]["readback_digest_count"] == 5 + assert data["approval_package_truth"]["p2_107_readback_loaded"] is True + assert data["approval_package_truth"]["approval_package_ready"] is True + assert data["approval_package_truth"]["canonical_readback_plan_ready"] is True + assert data["approval_package_truth"]["rollback_drill_ready"] is True + assert data["approval_package_truth"]["telegram_failure_receipt_gate_ready"] is True + assert data["approval_package_truth"]["canonical_runtime_readback_enabled"] is False + assert data["approval_package_truth"]["runtime_result_capture_write_enabled"] is False + assert data["approval_package_truth"]["reviewer_queue_write_enabled"] is False + assert data["approval_package_truth"]["telegram_failure_receipt_send_enabled"] is False + assert data["approval_package_truth"]["bot_api_call_enabled"] is False + assert data["approval_package_truth"]["rollback_work_item_write_enabled"] is False + assert data["approval_package_truth"]["runtime_readback_execution_count"] == 0 + assert data["rollups"]["approval_packet_count"] == 5 + assert data["rollups"]["canonical_readback_plan_count"] == 4 + assert data["rollups"]["rollback_drill_lane_count"] == 4 + assert data["rollups"]["telegram_failure_receipt_gate_count"] == 4 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["owner_approval_received_count"] == 0 + assert data["rollups"]["runtime_readback_execution_count"] == 0 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["reviewer_queue_write_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["telegram_failure_receipt_send_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["rollback_work_item_write_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 968cd460b..8ad22f661 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4921,6 +4921,84 @@ "reject_or_rework": "退回重整", "promote_to_next_gate": "推進下一閘門" } + }, + "runtimeReadbackApprovalPackage": { + "title": "P2-109 runtime readback 批准包", + "source": "{generated} · {current} → {next}", + "truthTitle": "批准包真相", + "priorTitle": "P2-107 承接", + "metrics": { + "overall": "P2-109 進度", + "packets": "批准包", + "plans": "readback plan", + "rollback": "rollback drill", + "telegramGates": "失敗收據 gate", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋總數", + "ownerApprovals": "已收批准", + "executions": "runtime readback", + "telegramSends": "Telegram 發送", + "rollbackWrites": "rollback 寫入", + "liveWrites": "正式寫入" + }, + "flags": { + "readbackLoaded": "P2-107 readback: {value}", + "packageReady": "批准包 ready: {value}", + "canonicalPlan": "canonical plan: {value}", + "rollbackDrill": "rollback drill: {value}", + "failureReceipt": "失敗收據 gate: {value}", + "canonicalRead": "canonical read: {value}", + "resultWrite": "結果寫入: {value}", + "queueWrite": "queue 寫入: {value}", + "telegramSend": "Telegram 發送: {value}", + "botApi": "Bot API: {value}", + "rollbackWrite": "rollback 寫入: {value}" + }, + "labels": { + "approvalFields": "批准欄位 {count}", + "blockedActions": "阻擋動作 {count}", + "runtimeWrite": "runtime 寫入: {value}", + "requiredFields": "readback 欄位 {count}", + "redactionChecks": "redaction 檢查 {count}", + "liveQuery": "live query: {value}", + "rollbackOwner": "rollback owner: {value}", + "requiredSteps": "演練步驟 {count}", + "rollbackWrite": "rollback write: {value}", + "requiredEvidence": "必備證據 {count}", + "queueWrite": "queue write: {value}", + "telegramSend": "Telegram send: {value}", + "botApi": "Bot API: {value}" + }, + "packetStatuses": { + "ready_for_owner_review": "待 owner 審查", + "approval_required": "需批准", + "blocked_by_policy": "政策阻擋" + }, + "planStatuses": { + "planned_no_runtime_read": "規劃中不讀 live", + "blocked_by_policy": "政策阻擋" + }, + "rollbackStatuses": { + "planned_no_write": "規劃中不寫入", + "blocked_by_policy": "政策阻擋" + }, + "telegramStatuses": { + "ready_for_owner_review": "待 owner 審查", + "blocked_by_policy": "政策阻擋" + }, + "riskTiers": { + "medium": "中風險", + "high": "高風險", + "critical": "關鍵阻擋" + }, + "actionTypes": { + "review_approval_package": "審查批准包", + "validate_canonical_plan": "驗證 canonical plan", + "review_rollback_drill": "審查 rollback drill", + "review_failure_receipt_gate": "審查失敗收據 gate", + "reject_or_promote": "退回或推進" + } } } }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 968cd460b..8ad22f661 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4921,6 +4921,84 @@ "reject_or_rework": "退回重整", "promote_to_next_gate": "推進下一閘門" } + }, + "runtimeReadbackApprovalPackage": { + "title": "P2-109 runtime readback 批准包", + "source": "{generated} · {current} → {next}", + "truthTitle": "批准包真相", + "priorTitle": "P2-107 承接", + "metrics": { + "overall": "P2-109 進度", + "packets": "批准包", + "plans": "readback plan", + "rollback": "rollback drill", + "telegramGates": "失敗收據 gate", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋總數", + "ownerApprovals": "已收批准", + "executions": "runtime readback", + "telegramSends": "Telegram 發送", + "rollbackWrites": "rollback 寫入", + "liveWrites": "正式寫入" + }, + "flags": { + "readbackLoaded": "P2-107 readback: {value}", + "packageReady": "批准包 ready: {value}", + "canonicalPlan": "canonical plan: {value}", + "rollbackDrill": "rollback drill: {value}", + "failureReceipt": "失敗收據 gate: {value}", + "canonicalRead": "canonical read: {value}", + "resultWrite": "結果寫入: {value}", + "queueWrite": "queue 寫入: {value}", + "telegramSend": "Telegram 發送: {value}", + "botApi": "Bot API: {value}", + "rollbackWrite": "rollback 寫入: {value}" + }, + "labels": { + "approvalFields": "批准欄位 {count}", + "blockedActions": "阻擋動作 {count}", + "runtimeWrite": "runtime 寫入: {value}", + "requiredFields": "readback 欄位 {count}", + "redactionChecks": "redaction 檢查 {count}", + "liveQuery": "live query: {value}", + "rollbackOwner": "rollback owner: {value}", + "requiredSteps": "演練步驟 {count}", + "rollbackWrite": "rollback write: {value}", + "requiredEvidence": "必備證據 {count}", + "queueWrite": "queue write: {value}", + "telegramSend": "Telegram send: {value}", + "botApi": "Bot API: {value}" + }, + "packetStatuses": { + "ready_for_owner_review": "待 owner 審查", + "approval_required": "需批准", + "blocked_by_policy": "政策阻擋" + }, + "planStatuses": { + "planned_no_runtime_read": "規劃中不讀 live", + "blocked_by_policy": "政策阻擋" + }, + "rollbackStatuses": { + "planned_no_write": "規劃中不寫入", + "blocked_by_policy": "政策阻擋" + }, + "telegramStatuses": { + "ready_for_owner_review": "待 owner 審查", + "blocked_by_policy": "政策阻擋" + }, + "riskTiers": { + "medium": "中風險", + "high": "高風險", + "critical": "關鍵阻擋" + }, + "actionTypes": { + "review_approval_package": "審查批准包", + "validate_canonical_plan": "驗證 canonical plan", + "review_rollback_drill": "審查 rollback drill", + "review_failure_receipt_gate": "審查失敗收據 gate", + "reject_or_promote": "退回或推進" + } } } }, 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 66f72eee4..696590192 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 @@ -57,6 +57,7 @@ import { type AiAgentReportRuntimeFixtureReadbackSnapshot, type AiAgentReportRuntimeReadinessSnapshot, type AiAgentReportTruthActionabilityReviewSnapshot, + type AiAgentRuntimeReadbackApprovalPackageSnapshot, type AiAgentRuntimeWorkerShadowGateSnapshot, type AiAgentRuntimeVerifierEvidenceReviewSnapshot, type AiAgentRuntimeWriteGateReviewSnapshot, @@ -434,6 +435,7 @@ export function AutomationInventoryTab() { const [criticReviewerResultCapture, setCriticReviewerResultCapture] = useState(null) const [ownerApprovedResultCaptureDryRun, setOwnerApprovedResultCaptureDryRun] = useState(null) const [ownerApprovedResultCaptureReadback, setOwnerApprovedResultCaptureReadback] = useState(null) + const [runtimeReadbackApprovalPackage, setRuntimeReadbackApprovalPackage] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -479,6 +481,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentCriticReviewerResultCapture(), apiClient.getAiAgentOwnerApprovedResultCaptureDryRun(), apiClient.getAiAgentOwnerApprovedResultCaptureReadback(), + apiClient.getAiAgentRuntimeReadbackApprovalPackage(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -523,6 +526,7 @@ export function AutomationInventoryTab() { criticReviewerResultCaptureResult, ownerApprovedResultCaptureDryRunResult, ownerApprovedResultCaptureReadbackResult, + runtimeReadbackApprovalPackageResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -564,6 +568,7 @@ export function AutomationInventoryTab() { setCriticReviewerResultCapture(criticReviewerResultCaptureResult.status === 'fulfilled' ? criticReviewerResultCaptureResult.value : null) setOwnerApprovedResultCaptureDryRun(ownerApprovedResultCaptureDryRunResult.status === 'fulfilled' ? ownerApprovedResultCaptureDryRunResult.value : null) setOwnerApprovedResultCaptureReadback(ownerApprovedResultCaptureReadbackResult.status === 'fulfilled' ? ownerApprovedResultCaptureReadbackResult.value : null) + setRuntimeReadbackApprovalPackage(runtimeReadbackApprovalPackageResult.status === 'fulfilled' ? runtimeReadbackApprovalPackageResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -603,6 +608,7 @@ export function AutomationInventoryTab() { criticReviewerResultCaptureResult, ownerApprovedResultCaptureDryRunResult, ownerApprovedResultCaptureReadbackResult, + runtimeReadbackApprovalPackageResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1484,6 +1490,62 @@ export function AutomationInventoryTab() { .slice(0, 4) }, [ownerApprovedResultCaptureReadback]) + const visibleRuntimeReadbackPackets = useMemo(() => { + if (!runtimeReadbackApprovalPackage) return [] + const statusPriority = { blocked_by_policy: 0, approval_required: 1, ready_for_owner_review: 2 } as Record + const riskPriority = { critical: 0, high: 1, medium: 2 } as Record + return [...runtimeReadbackApprovalPackage.approval_packet_templates] + .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.packet_id.localeCompare(b.packet_id) + }) + .slice(0, 5) + }, [runtimeReadbackApprovalPackage]) + + const visibleRuntimeReadbackPlans = useMemo(() => { + if (!runtimeReadbackApprovalPackage) return [] + const statusPriority = { blocked_by_policy: 0, planned_no_runtime_read: 1 } as Record + return [...runtimeReadbackApprovalPackage.canonical_readback_plans] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 2 + const right = statusPriority[b.status] ?? 2 + if (left !== right) return left - right + return a.plan_id.localeCompare(b.plan_id) + }) + .slice(0, 4) + }, [runtimeReadbackApprovalPackage]) + + const visibleRuntimeReadbackRollbackLanes = useMemo(() => { + if (!runtimeReadbackApprovalPackage) return [] + const statusPriority = { blocked_by_policy: 0, planned_no_write: 1 } as Record + return [...runtimeReadbackApprovalPackage.rollback_drill_lanes] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 2 + const right = statusPriority[b.status] ?? 2 + if (left !== right) return left - right + return a.lane_id.localeCompare(b.lane_id) + }) + .slice(0, 4) + }, [runtimeReadbackApprovalPackage]) + + const visibleRuntimeReadbackTelegramGates = useMemo(() => { + if (!runtimeReadbackApprovalPackage) return [] + const statusPriority = { blocked_by_policy: 0, ready_for_owner_review: 1 } as Record + return [...runtimeReadbackApprovalPackage.telegram_failure_receipt_gates] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 2 + const right = statusPriority[b.status] ?? 2 + if (left !== right) return left - right + return a.gate_id.localeCompare(b.gate_id) + }) + .slice(0, 4) + }, [runtimeReadbackApprovalPackage]) + const visibleReportTruthFindings = useMemo(() => { if (!reportTruthActionabilityReview) return [] const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record @@ -1703,7 +1765,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -2024,6 +2086,35 @@ export function AutomationInventoryTab() { + ownerApprovedResultCaptureReadback.rollups.telegram_send_count + ownerApprovedResultCaptureReadback.rollups.production_write_count ) + const runtimeReadbackPackageOverall = runtimeReadbackApprovalPackage.program_status.overall_completion_percent + const runtimeReadbackPackets = runtimeReadbackApprovalPackage.rollups.approval_packet_count + const runtimeReadbackPlans = runtimeReadbackApprovalPackage.rollups.canonical_readback_plan_count + const runtimeReadbackRollbackLanes = runtimeReadbackApprovalPackage.rollups.rollback_drill_lane_count + const runtimeReadbackTelegramGates = runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_gate_count + const runtimeReadbackActions = runtimeReadbackApprovalPackage.rollups.operator_action_count + const runtimeReadbackApprovalRequired = runtimeReadbackApprovalPackage.rollups.approval_required_packet_count + const runtimeReadbackBlocked = ( + runtimeReadbackApprovalPackage.rollups.blocked_packet_count + + runtimeReadbackApprovalPackage.rollups.blocked_readback_plan_count + + runtimeReadbackApprovalPackage.rollups.blocked_rollback_lane_count + + runtimeReadbackApprovalPackage.rollups.blocked_telegram_gate_count + ) + const runtimeReadbackOwnerApprovals = runtimeReadbackApprovalPackage.rollups.owner_approval_received_count + const runtimeReadbackExecutions = runtimeReadbackApprovalPackage.rollups.runtime_readback_execution_count + const runtimeReadbackTelegramSends = runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_send_count + const runtimeReadbackRollbackWrites = runtimeReadbackApprovalPackage.rollups.rollback_work_item_write_count + const runtimeReadbackLiveWrites = ( + runtimeReadbackApprovalPackage.rollups.result_capture_write_count + + runtimeReadbackApprovalPackage.rollups.score_write_count + + runtimeReadbackApprovalPackage.rollups.learning_write_count + + runtimeReadbackApprovalPackage.rollups.playbook_trust_write_count + + runtimeReadbackApprovalPackage.rollups.reviewer_queue_write_count + + runtimeReadbackApprovalPackage.rollups.gateway_queue_write_count + + runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_send_count + + runtimeReadbackApprovalPackage.rollups.bot_api_call_count + + runtimeReadbackApprovalPackage.rollups.rollback_work_item_write_count + + runtimeReadbackApprovalPackage.rollups.production_write_count + ) const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count @@ -4790,6 +4881,187 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('runtimeReadbackApprovalPackage.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('runtimeReadbackApprovalPackage.priorTitle')} + + {runtimeReadbackApprovalPackage.prior_readback_readiness.readiness_note} + +
+ + + + +
+
+ +
+ {t('runtimeReadbackApprovalPackage.truthTitle')} + + {runtimeReadbackApprovalPackage.approval_package_truth.truth_note} + +
+ + + + + + + + + + + +
+
+
+ +
+ {visibleRuntimeReadbackPackets.map(packet => ( +
+
+ + {packet.display_name} + + +
+ + {packet.operator_guidance} + +
+ + + + + +
+
+ ))} +
+ +
+ {visibleRuntimeReadbackPlans.map(plan => ( +
+
+ + {plan.display_name} + + +
+ + {plan.query_scope} + +
+ + + + + +
+
+ ))} +
+ +
+ {visibleRuntimeReadbackRollbackLanes.map(lane => ( +
+
+ + {lane.display_name} + + +
+ + {lane.failure_if_missing} + +
+ + + + +
+
+ ))} +
+ +
+ {visibleRuntimeReadbackTelegramGates.map(gate => ( +
+
+ + {gate.display_name} + + +
+ + {gate.failure_if_missing} + +
+ + + + + +
+
+ ))} +
+ +
+ {runtimeReadbackApprovalPackage.operator_actions.map(action => ( +
+
+ + {action.display_name} + + +
+ + {action.operator_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index e960d44e8..91faa0086 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -440,6 +440,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentRuntimeReadbackApprovalPackage() { + const res = await fetch(`${API_BASE_URL}/agents/agent-runtime-readback-approval-package`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -3803,6 +3808,175 @@ export interface AiAgentOwnerApprovedResultCaptureReadbackSnapshot { } } +export interface AiAgentRuntimeReadbackApprovalPackageSnapshot { + schema_version: 'ai_agent_runtime_readback_approval_package_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-109' + next_task_id: 'P2-110' + read_only_mode: true + runtime_authority: 'runtime_readback_approval_package_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_readback_readiness: { + source_schema_version: 'ai_agent_owner_approved_result_capture_readback_v1' + readback_at: string + readback_digest_count: number + promotion_review_count: number + failure_lane_count: number + reviewer_queue_preview_count: number + operator_action_count: number + approval_required_digest_count: number + blocked_total_count: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + owner_approval_received_count: number + readback_digest_generated_count: number + promotion_approved_count: number + reviewer_queue_write_count: number + result_capture_write_count: number + score_write_count: number + learning_write_count: number + playbook_trust_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + production_write_count: number + readiness_note: string + } + approval_package_truth: { + p2_107_readback_loaded: true + approval_package_ready: true + canonical_readback_plan_ready: true + rollback_drill_ready: true + telegram_failure_receipt_gate_ready: true + owner_review_required_before_runtime: true + canonical_runtime_readback_enabled: false + runtime_result_capture_write_enabled: false + runtime_score_write_enabled: false + runtime_learning_write_enabled: false + playbook_trust_write_enabled: false + reviewer_queue_write_enabled: false + gateway_queue_write_enabled: false + telegram_failure_receipt_send_enabled: false + bot_api_call_enabled: false + rollback_work_item_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + destructive_operation_enabled: false + owner_approval_received_count: number + runtime_readback_execution_count: number + result_capture_write_count_24h: number + score_write_count_24h: number + learning_write_count_24h: number + playbook_trust_write_count_24h: number + reviewer_queue_write_count_24h: number + gateway_queue_write_count_24h: number + telegram_failure_receipt_send_count_24h: number + bot_api_call_count_24h: number + rollback_work_item_write_count_24h: number + production_write_count_24h: number + secret_read_count_24h: number + destructive_operation_count_24h: number + truth_note: string + } + approval_packet_templates: Array<{ + packet_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + risk_tier: 'medium' | 'high' | 'critical' + required_approval_fields: string[] + blocked_runtime_actions: string[] + operator_guidance: string + approval_required: true + runtime_write_allowed: false + evidence_hash: string + }> + canonical_readback_plans: Array<{ + plan_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'planned_no_runtime_read' | 'blocked_by_policy' + query_scope: string + required_fields: string[] + redaction_checks: string[] + live_query_enabled: false + runtime_write_allowed: false + evidence_hash: string + }> + rollback_drill_lanes: Array<{ + lane_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'planned_no_write' | 'blocked_by_policy' + rollback_owner: string + required_steps: string[] + failure_if_missing: string + rollback_write_enabled: false + creates_runtime_write: false + }> + telegram_failure_receipt_gates: Array<{ + gate_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'blocked_by_policy' + required_evidence: string[] + failure_if_missing: string + queue_write_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + evidence_hash: string + }> + operator_actions: Array<{ + action_id: string + action_type: 'review_approval_package' | 'validate_canonical_plan' | 'review_rollback_drill' | 'review_failure_receipt_gate' | 'reject_or_promote' + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + operator_instruction: string + runtime_write_allowed: false + }> + display_redaction_contract: { + redaction_required: true + frontend_display_policy: string + raw_prompt_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + raw_telegram_payload_display_allowed: false + work_window_transcript_display_allowed: false + } + rollups: { + approval_packet_count: number + canonical_readback_plan_count: number + rollback_drill_lane_count: number + telegram_failure_receipt_gate_count: number + operator_action_count: number + approval_required_packet_count: number + blocked_packet_count: number + blocked_readback_plan_count: number + blocked_rollback_lane_count: number + blocked_telegram_gate_count: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + owner_approval_received_count: number + runtime_readback_execution_count: number + result_capture_write_count: number + score_write_count: number + learning_write_count: number + playbook_trust_write_count: number + reviewer_queue_write_count: number + gateway_queue_write_count: number + telegram_failure_receipt_send_count: number + bot_api_call_count: number + rollback_work_item_write_count: number + production_write_count: number + secret_read_count: number + destructive_operation_count: number + } +} + export interface AiAgentOwnerApprovedFixtureDryRunSnapshot { schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 7edf2d038..ed3f3a7d1 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -104,6 +104,31 @@ **邊界**: - 本段未 SSH、未修改主機、未改 Nginx、未操作 Docker、未改 firewall、未 active scan、未讀 secret 明文。 - UI 可見、CD 成功與 console clean 不代表 runtime 授權、owner response gate 或 S4.9 已完成。 +## 2026-06-13|P2-109 Runtime readback approval package 本地完成,正式驗證待部署 + +**背景**:P2-107 已把 owner-approved result capture readback / promotion readiness 固定成只讀證據,P2-108 已補日週月報與 Agent 工作狀態總覽;但下一步仍不能直接讀 canonical runtime target 或寫入 runtime。P2-109 先把「批准後要如何進入 runtime readback」拆成可審查批准包、canonical readback plan、rollback drill 與 Telegram failure receipt gate。 + +**本地完成內容**: +- 新增 `ai_agent_runtime_readback_approval_package_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-runtime-readback-approval-package`。 +- P2-109 snapshot 固定 5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate 與 5 個 operator action。 +- Governance automation inventory 頁新增 P2-109 區塊,顯示 P2-109 進度 `100%`、批准包 `5`、readback plan `4`、rollback drill `4`、失敗收據 gate `4`、操作選項 `5`、阻擋總數 `5`、owner approval received `0`、runtime readback execution `0`、Telegram failure receipt send `0`、rollback work item write `0` 與 live writes `0`。 +- 更新 MASTER §3.2 / §5、`docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md` 與 `docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md`,將下一步改為 `P2-110`。 + +**本地驗證**: +- JSON parse:P2-109 schema / snapshot、`zh-TW.json`、`en.json` 通過。 +- API/service pytest:P2-109 + P2-107 目標測試 `18 passed`。 +- i18n mirror / placeholder:`10728` leaves,diff `0`。 +- Web typecheck:`pnpm --filter @awoooi/web typecheck` 通過。 + +**正式站狀態**: +- 尚未部署。本段完成後需推 Gitea main、等待 deploy marker,再做 production API readback 與 `/zh-TW/governance?tab=automation-inventory` desktop / mobile / in-app browser smoke。 + +**安全邊界**: +- P2-109 仍是只讀批准包;不讀 canonical runtime target、不寫 reviewer queue、不寫 rollback work item、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action。 +- 前端只顯示公開摘要、hash、count、gate 與狀態,不顯示內部協作逐字內容。 + +**下一步**: +- `P2-110`:runtime readback implementation review,以 P2-109 批准包、canonical readback plan、rollback drill 與 Telegram failure receipt gate 產生下一關 no-write implementation review。 ## 2026-06-13|P2-107 Owner-approved result capture readback / promotion readiness 本地完成與正式驗證 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 88ba98322..0055ee84b 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -12,7 +12,7 @@ | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | | 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 | | 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 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review;P2-403H 已完成 post-write verifier implementation package;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查;P2-403K / L / M / N 已把 SRE 戰情室路由、報表派送啟動前閘門、no-write dry-run 與 fixture/readback/verifier dry-run 固定;P2-404 已完成 runtime worker shadow / no-write evidence;P2-101 已完成操作類別權限模型;P2-102 已完成 13 類候選操作 dry-run 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約;P2-106 已完成 owner-approved result capture dry-run;P2-107 已完成 owner-approved result capture readback / promotion readiness;P2-108 已完成日週月報與 Agent 工作狀態總覽,三種報告可見完成度皆 `100%`,3 個 Agent 狀態報告、3 張圖表、4 張統帥問答卡、工作量 `91`、已完成 `79`、待審核 `12`。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_operation_permission_model_v1`、`ai_agent_candidate_operation_dry_run_evidence_v1`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`ai_agent_owner_approved_result_capture_dry_run_v1`、`ai_agent_owner_approved_result_capture_readback_v1`、`ai_agent_report_status_board_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`、`GET /api/v1/agents/agent-owner-approved-result-capture-readback`、`GET /api/v1/agents/agent-report-status-board`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | +| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review;P2-403H 已完成 post-write verifier implementation package;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查;P2-403K / L / M / N 已把 SRE 戰情室路由、報表派送啟動前閘門、no-write dry-run 與 fixture/readback/verifier dry-run 固定;P2-404 已完成 runtime worker shadow / no-write evidence;P2-101 已完成操作類別權限模型;P2-102 已完成 13 類候選操作 dry-run 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約;P2-106 已完成 owner-approved result capture dry-run;P2-107 已完成 owner-approved result capture readback / promotion readiness;P2-108 已完成日週月報與 Agent 工作狀態總覽;P2-109 已完成 runtime readback approval package,固定 5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate 與 5 個 operator action。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_operation_permission_model_v1`、`ai_agent_candidate_operation_dry_run_evidence_v1`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`ai_agent_owner_approved_result_capture_dry_run_v1`、`ai_agent_owner_approved_result_capture_readback_v1`、`ai_agent_report_status_board_v1`、`ai_agent_runtime_readback_approval_package_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`、`GET /api/v1/agents/agent-owner-approved-result-capture-readback`、`GET /api/v1/agents/agent-report-status-board`、`GET /api/v1/agents/agent-runtime-readback-approval-package`、`/zh-TW/governance?tab=automation-inventory`、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 | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | @@ -20,9 +20,9 @@ AI Agent 自動化工作包目前完成度:**94%**。本工作清單文件本 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K/L/M/N 報表與 SRE 戰情室 dry-run 鏈、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、P2-102 候選操作 dry-run 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查、P2-105 critic / reviewer 評分與 result capture 契約、P2-106 owner-approved result capture dry-run、P2-107 owner-approved result capture readback / promotion readiness,以及 P2-108 日週月報與 Agent 工作狀態總覽。目前 live AgentSession、Agent message、handoff、canonical runtime readback、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-108 已固定三種報告 `100%` 可見、3 個 Agent 狀態報告、3 張圖表與 4 張統帥問答卡;真正下一步是 `P2-109`。 +三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K/L/M/N 報表與 SRE 戰情室 dry-run 鏈、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、P2-102 候選操作 dry-run 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查、P2-105 critic / reviewer 評分與 result capture 契約、P2-106 owner-approved result capture dry-run、P2-107 owner-approved result capture readback / promotion readiness、P2-108 日週月報與 Agent 工作狀態總覽,以及 P2-109 runtime readback approval package。目前 live AgentSession、Agent message、handoff、canonical runtime readback、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-109 已固定 5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate 與 5 個 operator action;真正下一步是 `P2-110`。 -AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-108` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture、owner-approved result capture dry-run、owner-approved result capture readback / promotion readiness 與 Agent report status board。下一步是 `P2-109`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 +AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-109` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture、owner-approved result capture dry-run、owner-approved result capture readback / promotion readiness、Agent report status board 與 runtime readback approval package。下一步是 `P2-110`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: diff --git a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md index 3b2368475..2e2ccac1c 100644 --- a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md +++ b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md @@ -176,18 +176,21 @@ | `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` | P2-107 committed snapshot,完成度 `100%`,5 個 readback digest、5 個 promotion review、4 條 failure lane、4 個 reviewer queue preview、5 個 operator action;canonical runtime readback、promotion approved、reviewer queue write 與所有 live write / send counts 全為 `0` | | `docs/schemas/ai_agent_report_status_board_v1.schema.json` | P2-108 日週月報與 Agent 工作狀態總覽 schema;強制報告 / Agent 狀態 / 圖表可見,但 scheduler、Gateway queue、Telegram send、AI analysis 與 production optimization 維持未啟用 | | `docs/evaluations/ai_agent_report_status_board_2026-06-13.json` | P2-108 committed snapshot,完成度 `100%`,日報 / 週報 / 月報 `100%` 可見、3 個 Agent 狀態報告、3 張圖表、4 張統帥問答卡;live delivery / Telegram send / runtime work / auto optimization 全為 `0` | +| `docs/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json` | P2-109 runtime readback approval package schema;強制 canonical runtime readback、result capture write、score write、learning write、PlayBook trust write、reviewer queue write、Gateway queue、Telegram failure receipt、Bot API、rollback work item、production write、secret read 與 destructive action 維持未授權 | +| `docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json` | P2-109 committed snapshot,完成度 `100%`,5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate、5 個 operator action;runtime readback execution、owner approval received 與所有 live write / send counts 全為 `0` | | `GET /api/v1/agents/agent-critic-reviewer-result-capture` | 只讀 API;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader 與安全驗證 | | `apps/api/src/services/ai_agent_live_read_model_gate.py` | P2-403B 只讀 loader;拒絕 live DB query、Redis consumer、unsafe fields、Telegram 與 writeback | | `GET /api/v1/agents/agent-interaction-learning-proof` | 只讀 API,不啟動 worker、不碰 Redis / DB runtime、不發 Telegram | | `GET /api/v1/agents/agent-live-read-model-gate` | 只讀 API,不連 DB、不讀寫 Redis、不發 Telegram | -| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-108 日週月報與 Agent 工作狀態總覽、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、P2-102 candidate dry-run evidence、P2-103 task result audit trail、P2-104 matched PlayBook learning gap、P2-105 critic / reviewer result capture、P2-106 owner-approved result capture dry-run、P2-107 owner-approved result capture readback / promotion readiness、Agent lane、可觀測訊號、runtime gates、前端 redaction | +| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-108 日週月報與 Agent 工作狀態總覽、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、P2-102 candidate dry-run evidence、P2-103 task result audit trail、P2-104 matched PlayBook learning gap、P2-105 critic / reviewer result capture、P2-106 owner-approved result capture dry-run、P2-107 owner-approved result capture readback / promotion readiness、P2-109 runtime readback approval package、Agent lane、可觀測訊號、runtime gates、前端 redaction | ## 5. 後續優先順序 | 優先 | ID | 工作 | gate | |---:|---|---|---| | 1 | P2-109 | report live delivery approval package | 以 P2-108 report status board 產生下一關批准包,仍需維持 no live send / no live write | +| 2 | P2-110 | runtime readback implementation review | 以 P2-109 批准包、canonical readback plan、rollback drill 與 Telegram failure receipt gate 產生下一關 no-write implementation review | ## 6. 紅線 diff --git a/docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json b/docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json new file mode 100644 index 000000000..2fcc684cc --- /dev/null +++ b/docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json @@ -0,0 +1,509 @@ +{ + "schema_version": "ai_agent_runtime_readback_approval_package_v1", + "generated_at": "2026-06-13T15:12:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-109", + "next_task_id": "P2-110", + "read_only_mode": true, + "runtime_authority": "runtime_readback_approval_package_only_no_live_write", + "status_note": "P2-109 承接 P2-107 readback / promotion readiness 與 P2-108 report status board,固定 runtime readback 批准包、canonical readback plan、rollback drill 與 Telegram 失敗收據閘門;本快照只讀,不啟動 live writer。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json", + "docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json", + "docs/evaluations/ai_agent_post_write_verifier_package_2026-06-12.json", + "docs/evaluations/ai_agent_runtime_verifier_evidence_review_2026-06-12.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md" + ], + "prior_readback_readiness": { + "source_schema_version": "ai_agent_owner_approved_result_capture_readback_v1", + "readback_at": "2026-06-13T14:18:00+08:00", + "readback_digest_count": 5, + "promotion_review_count": 5, + "failure_lane_count": 4, + "reviewer_queue_preview_count": 4, + "operator_action_count": 5, + "approval_required_digest_count": 2, + "blocked_total_count": 7, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "readback_digest_generated_count": 0, + "promotion_approved_count": 0, + "reviewer_queue_write_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + "readiness_note": "P2-107 已固定 fixture readback 與 promotion review;P2-108 只產生下一關 owner review 批准包,不讀 canonical runtime target。" + }, + "approval_package_truth": { + "p2_107_readback_loaded": true, + "approval_package_ready": true, + "canonical_readback_plan_ready": true, + "rollback_drill_ready": true, + "telegram_failure_receipt_gate_ready": true, + "owner_review_required_before_runtime": true, + "canonical_runtime_readback_enabled": false, + "runtime_result_capture_write_enabled": false, + "runtime_score_write_enabled": false, + "runtime_learning_write_enabled": false, + "playbook_trust_write_enabled": false, + "reviewer_queue_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_failure_receipt_send_enabled": false, + "bot_api_call_enabled": false, + "rollback_work_item_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "destructive_operation_enabled": false, + "owner_approval_received_count": 0, + "runtime_readback_execution_count": 0, + "result_capture_write_count_24h": 0, + "score_write_count_24h": 0, + "learning_write_count_24h": 0, + "playbook_trust_write_count_24h": 0, + "reviewer_queue_write_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_failure_receipt_send_count_24h": 0, + "bot_api_call_count_24h": 0, + "rollback_work_item_write_count_24h": 0, + "production_write_count_24h": 0, + "secret_read_count_24h": 0, + "destructive_operation_count_24h": 0, + "truth_note": "此階段只允許 owner review 批准包與 no-write plan 可視化;canonical runtime readback、result/score/learning/trust write、queue write、Telegram 失敗收據與 rollback work item 全部仍為 0。" + }, + "approval_packet_templates": [ + { + "packet_id": "approved_result_capture_runtime_readback", + "display_name": "已批准處置結果 readback 批准包", + "owner_agent": "openclaw", + "status": "approval_required", + "risk_tier": "high", + "required_approval_fields": [ + "incident_id", + "approval_id", + "result_capture_template_id", + "expected_status_chain", + "owner_decision", + "rollback_owner" + ], + "blocked_runtime_actions": [ + "canonical_runtime_readback", + "result_capture_write", + "score_write", + "reviewer_queue_write" + ], + "operator_guidance": "確認結果捕捉欄位、狀態鏈與 rollback owner 後,才能進入下一關 runtime readback 實作審查。", + "approval_required": true, + "runtime_write_allowed": false, + "evidence_hash": "sha256:1111111111111111111111111111111111111111111111111111111111111111" + }, + { + "packet_id": "execution_failed_negative_learning_review", + "display_name": "執行失敗負向學習審查包", + "owner_agent": "hermes", + "status": "approval_required", + "risk_tier": "critical", + "required_approval_fields": [ + "incident_id", + "failure_lane_id", + "matched_playbook_id", + "negative_learning_reason", + "human_review_owner", + "rollback_decision" + ], + "blocked_runtime_actions": [ + "learning_write", + "playbook_trust_write", + "gateway_queue_write", + "telegram_failure_receipt_send" + ], + "operator_guidance": "失敗案例只能先形成可審查負向學習摘要,未經 owner 接受不得寫入 KM 或降低 PlayBook trust。", + "approval_required": true, + "runtime_write_allowed": false, + "evidence_hash": "sha256:2222222222222222222222222222222222222222222222222222222222222222" + }, + { + "packet_id": "pending_human_gate_expiry_review", + "display_name": "人工閘門逾時審查包", + "owner_agent": "openclaw", + "status": "approval_required", + "risk_tier": "medium", + "required_approval_fields": [ + "gate_id", + "pending_duration", + "owner_role", + "next_action", + "blocked_reason" + ], + "blocked_runtime_actions": [ + "reviewer_queue_write", + "gateway_queue_write", + "telegram_failure_receipt_send" + ], + "operator_guidance": "逾時閘門要先產出明確人工下一步與卡點,不得自動轉成執行或通知轟炸。", + "approval_required": true, + "runtime_write_allowed": false, + "evidence_hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333" + }, + { + "packet_id": "manual_or_noop_result_acceptance", + "display_name": "人工或無操作結果接受包", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "risk_tier": "medium", + "required_approval_fields": [ + "incident_id", + "manual_reason", + "no_action_reason", + "evidence_refs", + "accepted_by" + ], + "blocked_runtime_actions": [ + "result_capture_write", + "score_write", + "learning_write" + ], + "operator_guidance": "無操作結果必須說清楚為何沒有修復、要誰接手與下一次判定條件,避免 approved 後流程空轉。", + "approval_required": true, + "runtime_write_allowed": false, + "evidence_hash": "sha256:4444444444444444444444444444444444444444444444444444444444444444" + }, + { + "packet_id": "post_write_verifier_receipt_package", + "display_name": "寫入後 verifier 收據批准包", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "risk_tier": "critical", + "required_approval_fields": [ + "verifier_id", + "readback_plan_id", + "rollback_lane_id", + "telegram_failure_gate_id", + "owner_acceptance_record" + ], + "blocked_runtime_actions": [ + "canonical_runtime_readback", + "rollback_work_item_write", + "telegram_failure_receipt_send", + "production_write" + ], + "operator_guidance": "post-write verifier 仍停在批准包層;沒有 acceptance record 以前不得建立 runtime 收據或 rollback work item。", + "approval_required": true, + "runtime_write_allowed": false, + "evidence_hash": "sha256:5555555555555555555555555555555555555555555555555555555555555555" + } + ], + "canonical_readback_plans": [ + { + "plan_id": "result_capture_row_readback", + "display_name": "result capture row readback plan", + "owner_agent": "openclaw", + "status": "planned_no_runtime_read", + "query_scope": "incident_result_capture_rows", + "required_fields": [ + "incident_id", + "approval_id", + "status_chain", + "repair_outcome", + "verifier_status" + ], + "redaction_checks": [ + "public_evidence_refs_only", + "secret_free_payload", + "operator_safe_summary" + ], + "live_query_enabled": false, + "runtime_write_allowed": false, + "evidence_hash": "sha256:6666666666666666666666666666666666666666666666666666666666666666" + }, + { + "plan_id": "score_fixture_parity_readback", + "display_name": "score fixture parity readback plan", + "owner_agent": "nemotron", + "status": "planned_no_runtime_read", + "query_scope": "critic_reviewer_score_fixtures", + "required_fields": [ + "score_fixture_id", + "expected_score", + "actual_score", + "reviewer_delta", + "promotion_decision" + ], + "redaction_checks": [ + "no_private_inference_trace", + "no_secret_value", + "no_provider_payload" + ], + "live_query_enabled": false, + "runtime_write_allowed": false, + "evidence_hash": "sha256:7777777777777777777777777777777777777777777777777777777777777777" + }, + { + "plan_id": "playbook_trust_noop_readback", + "display_name": "PlayBook trust no-op readback plan", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "query_scope": "playbook_trust_snapshot", + "required_fields": [ + "playbook_id", + "trust_before", + "trust_after", + "reason", + "owner_review_id" + ], + "redaction_checks": [ + "no_unreviewed_learning_write", + "no_bulk_trust_mutation", + "no_secret_source" + ], + "live_query_enabled": false, + "runtime_write_allowed": false, + "evidence_hash": "sha256:8888888888888888888888888888888888888888888888888888888888888888" + }, + { + "plan_id": "verifier_receipt_readback", + "display_name": "verifier receipt readback plan", + "owner_agent": "openclaw", + "status": "planned_no_runtime_read", + "query_scope": "post_write_verifier_receipts", + "required_fields": [ + "verifier_id", + "receipt_id", + "failure_receipt_gate", + "rollback_lane", + "owner_decision" + ], + "redaction_checks": [ + "failure_only_summary", + "sre_war_room_route_only", + "no_bot_api_payload" + ], + "live_query_enabled": false, + "runtime_write_allowed": false, + "evidence_hash": "sha256:9999999999999999999999999999999999999999999999999999999999999999" + } + ], + "rollback_drill_lanes": [ + { + "lane_id": "result_capture_insert_rollback_drill", + "display_name": "result capture insert rollback drill", + "owner_agent": "openclaw", + "status": "planned_no_write", + "rollback_owner": "sre_owner", + "required_steps": [ + "identify_result_capture_row", + "compare_verifier_receipt", + "mark_reversal_plan", + "collect_owner_acceptance" + ], + "failure_if_missing": "沒有 rollback owner 與 verifier receipt 時,不得推進任何 result capture 寫入。", + "rollback_write_enabled": false, + "creates_runtime_write": false + }, + { + "lane_id": "score_write_rollback_drill", + "display_name": "score write rollback drill", + "owner_agent": "nemotron", + "status": "planned_no_write", + "rollback_owner": "ai_quality_owner", + "required_steps": [ + "snapshot_score_before", + "verify_score_delta", + "prepare_reversal_score", + "owner_acceptance_required" + ], + "failure_if_missing": "評分回滾沒有 dry-run parity 前,只能停留在 fixture 層。", + "rollback_write_enabled": false, + "creates_runtime_write": false + }, + { + "lane_id": "learning_write_rollback_drill", + "display_name": "learning write rollback drill", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "rollback_owner": "knowledge_owner", + "required_steps": [ + "extract_learning_candidate", + "review_source_incident", + "prepare_reversal_note", + "owner_review_required" + ], + "failure_if_missing": "學習寫入涉及 KM 固化,沒有 owner review 前不可建立 rollback work item。", + "rollback_write_enabled": false, + "creates_runtime_write": false + }, + { + "lane_id": "telegram_receipt_rollback_drill", + "display_name": "Telegram failure receipt rollback drill", + "owner_agent": "openclaw", + "status": "blocked_by_policy", + "rollback_owner": "sre_notification_owner", + "required_steps": [ + "dedupe_receipt_candidate", + "verify_route_lock", + "prepare_suppression_key", + "owner_acceptance_required" + ], + "failure_if_missing": "失敗收據未通過 dedupe 與路由鎖定時,不得發送或回寫 queue。", + "rollback_write_enabled": false, + "creates_runtime_write": false + } + ], + "telegram_failure_receipt_gates": [ + { + "gate_id": "receipt_payload_redaction", + "display_name": "Telegram failure receipt redaction gate", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "required_evidence": [ + "incident_id", + "failure_lane_id", + "next_action", + "blocked_reason", + "public_evidence_refs" + ], + "failure_if_missing": "缺少 redaction digest 時不得建立失敗收據。", + "queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "evidence_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "gate_id": "sre_war_room_route_lock", + "display_name": "AwoooI SRE 戰情室路由鎖定", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "required_evidence": [ + "route_id", + "channel_policy", + "legacy_route_suppression", + "dedupe_key" + ], + "failure_if_missing": "沒有路由鎖定與 legacy route suppression,不得送出 failure receipt。", + "queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "evidence_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + { + "gate_id": "delivery_correlation", + "display_name": "失敗收據 delivery correlation gate", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "required_evidence": [ + "message_correlation_id", + "incident_timeline_id", + "owner_review_id", + "readback_plan_id" + ], + "failure_if_missing": "delivery correlation 未設計完成時,不得宣稱告警鏈路打通。", + "queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "evidence_hash": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + }, + { + "gate_id": "duplicate_suppression", + "display_name": "失敗收據重複抑制 gate", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "required_evidence": [ + "fingerprint", + "recurrence_window", + "suppression_reason", + "last_receipt_state" + ], + "failure_if_missing": "缺少 dedupe / recurrence window 時只能進 reviewer queue preview,不得發 Telegram。", + "queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "evidence_hash": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + } + ], + "operator_actions": [ + { + "action_id": "op_review_runtime_readback_package", + "action_type": "review_approval_package", + "display_name": "審查 runtime readback 批准包", + "owner_agent": "openclaw", + "operator_instruction": "先確認五個批准包是否都有 owner、欄位、rollback owner 與 blocked actions,再判定是否可進 P2-109。", + "runtime_write_allowed": false + }, + { + "action_id": "op_validate_canonical_readback_plan", + "action_type": "validate_canonical_plan", + "display_name": "檢查 canonical readback plan", + "owner_agent": "nemotron", + "operator_instruction": "比對 readback 欄位、redaction check 與 evidence hash;未通過時只退回設計,不讀 live target。", + "runtime_write_allowed": false + }, + { + "action_id": "op_review_rollback_drill", + "action_type": "review_rollback_drill", + "display_name": "審查 rollback drill", + "owner_agent": "hermes", + "operator_instruction": "確認每條 rollback lane 都有 owner、步驟與失敗條件;沒有 acceptance record 不建立 work item。", + "runtime_write_allowed": false + }, + { + "action_id": "op_review_failure_receipt_gate", + "action_type": "review_failure_receipt_gate", + "display_name": "審查 Telegram 失敗收據閘門", + "owner_agent": "openclaw", + "operator_instruction": "確認只允許 AwoooI SRE 戰情室、具備 dedupe 與 correlation;本階段不得發送。", + "runtime_write_allowed": false + }, + { + "action_id": "op_reject_or_promote_p2_109", + "action_type": "reject_or_promote", + "display_name": "退回或推進 P2-109", + "owner_agent": "openclaw", + "operator_instruction": "若批准包、readback plan、rollback drill、failure receipt gate 都完整,才可建立下一關 no-write implementation review。", + "runtime_write_allowed": false + } + ], + "display_redaction_contract": { + "redaction_required": true, + "frontend_display_policy": "只顯示 owner-safe summary、redacted evidence hash、count 與 blocked action;不顯示內部協作原文、機密值、原始 Telegram payload 或 provider 私有推理。", + "raw_prompt_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_telegram_payload_display_allowed": false, + "work_window_transcript_display_allowed": false + }, + "rollups": { + "approval_packet_count": 5, + "canonical_readback_plan_count": 4, + "rollback_drill_lane_count": 4, + "telegram_failure_receipt_gate_count": 4, + "operator_action_count": 5, + "approval_required_packet_count": 3, + "blocked_packet_count": 1, + "blocked_readback_plan_count": 1, + "blocked_rollback_lane_count": 2, + "blocked_telegram_gate_count": 1, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "runtime_readback_execution_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_failure_receipt_send_count": 0, + "bot_api_call_count": 0, + "rollback_work_item_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json b/docs/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json new file mode 100644 index 000000000..84a1503ae --- /dev/null +++ b/docs/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json @@ -0,0 +1,745 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json", + "title": "AI Agent Runtime Readback Approval Package v1", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_readback_readiness", + "approval_package_truth", + "approval_packet_templates", + "canonical_readback_plans", + "rollback_drill_lanes", + "telegram_failure_receipt_gates", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_runtime_readback_approval_package_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object", + "additionalProperties": false, + "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": { + "const": "P2" + }, + "current_task_id": { + "const": "P2-109" + }, + "next_task_id": { + "const": "P2-110" + }, + "read_only_mode": { + "const": true + }, + "runtime_authority": { + "const": "runtime_readback_approval_package_only_no_live_write" + }, + "status_note": { + "type": "string", + "minLength": 1 + } + } + }, + "source_refs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "prior_readback_readiness": { + "$ref": "#/$defs/prior_readback_readiness" + }, + "approval_package_truth": { + "$ref": "#/$defs/approval_package_truth" + }, + "approval_packet_templates": { + "type": "array", + "minItems": 5, + "items": { + "$ref": "#/$defs/approval_packet" + } + }, + "canonical_readback_plans": { + "type": "array", + "minItems": 4, + "items": { + "$ref": "#/$defs/canonical_readback_plan" + } + }, + "rollback_drill_lanes": { + "type": "array", + "minItems": 4, + "items": { + "$ref": "#/$defs/rollback_drill_lane" + } + }, + "telegram_failure_receipt_gates": { + "type": "array", + "minItems": 4, + "items": { + "$ref": "#/$defs/telegram_failure_receipt_gate" + } + }, + "operator_actions": { + "type": "array", + "minItems": 5, + "items": { + "$ref": "#/$defs/operator_action" + } + }, + "display_redaction_contract": { + "$ref": "#/$defs/display_redaction_contract" + }, + "rollups": { + "$ref": "#/$defs/rollups" + } + }, + "$defs": { + "string_array": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "redacted_sha256": { + "type": "string", + "pattern": "^sha256:[0-9a-f]{64}$" + }, + "prior_readback_readiness": { + "type": "object", + "additionalProperties": false, + "required": [ + "source_schema_version", + "readback_at", + "readback_digest_count", + "promotion_review_count", + "failure_lane_count", + "reviewer_queue_preview_count", + "operator_action_count", + "approval_required_digest_count", + "blocked_total_count", + "approved_without_execution_meta_24h", + "execution_failed_with_matched_24h", + "owner_approval_received_count", + "readback_digest_generated_count", + "promotion_approved_count", + "reviewer_queue_write_count", + "result_capture_write_count", + "score_write_count", + "learning_write_count", + "playbook_trust_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "production_write_count", + "readiness_note" + ], + "properties": { + "source_schema_version": { + "const": "ai_agent_owner_approved_result_capture_readback_v1" + }, + "readback_at": { + "type": "string" + }, + "readback_digest_count": { + "const": 5 + }, + "promotion_review_count": { + "const": 5 + }, + "failure_lane_count": { + "const": 4 + }, + "reviewer_queue_preview_count": { + "const": 4 + }, + "operator_action_count": { + "const": 5 + }, + "approval_required_digest_count": { + "const": 2 + }, + "blocked_total_count": { + "const": 7 + }, + "approved_without_execution_meta_24h": { + "type": "integer", + "minimum": 0 + }, + "execution_failed_with_matched_24h": { + "type": "integer", + "minimum": 0 + }, + "owner_approval_received_count": { + "const": 0 + }, + "readback_digest_generated_count": { + "const": 0 + }, + "promotion_approved_count": { + "const": 0 + }, + "reviewer_queue_write_count": { + "const": 0 + }, + "result_capture_write_count": { + "const": 0 + }, + "score_write_count": { + "const": 0 + }, + "learning_write_count": { + "const": 0 + }, + "playbook_trust_write_count": { + "const": 0 + }, + "gateway_queue_write_count": { + "const": 0 + }, + "telegram_send_count": { + "const": 0 + }, + "production_write_count": { + "const": 0 + }, + "readiness_note": { + "type": "string", + "minLength": 1 + } + } + }, + "approval_package_truth": { + "type": "object", + "required": [ + "p2_107_readback_loaded", + "approval_package_ready", + "canonical_readback_plan_ready", + "rollback_drill_ready", + "telegram_failure_receipt_gate_ready", + "owner_review_required_before_runtime", + "canonical_runtime_readback_enabled", + "runtime_result_capture_write_enabled", + "runtime_score_write_enabled", + "runtime_learning_write_enabled", + "playbook_trust_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_failure_receipt_send_enabled", + "bot_api_call_enabled", + "rollback_work_item_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled" + ], + "properties": { + "p2_107_readback_loaded": { + "const": true + }, + "approval_package_ready": { + "const": true + }, + "canonical_readback_plan_ready": { + "const": true + }, + "rollback_drill_ready": { + "const": true + }, + "telegram_failure_receipt_gate_ready": { + "const": true + }, + "owner_review_required_before_runtime": { + "const": true + }, + "canonical_runtime_readback_enabled": { + "const": false + }, + "runtime_result_capture_write_enabled": { + "const": false + }, + "runtime_score_write_enabled": { + "const": false + }, + "runtime_learning_write_enabled": { + "const": false + }, + "playbook_trust_write_enabled": { + "const": false + }, + "reviewer_queue_write_enabled": { + "const": false + }, + "gateway_queue_write_enabled": { + "const": false + }, + "telegram_failure_receipt_send_enabled": { + "const": false + }, + "bot_api_call_enabled": { + "const": false + }, + "rollback_work_item_write_enabled": { + "const": false + }, + "production_write_enabled": { + "const": false + }, + "secret_read_enabled": { + "const": false + }, + "destructive_operation_enabled": { + "const": false + } + }, + "additionalProperties": true + }, + "approval_packet": { + "type": "object", + "required": [ + "packet_id", + "display_name", + "owner_agent", + "status", + "risk_tier", + "required_approval_fields", + "blocked_runtime_actions", + "operator_guidance", + "approval_required", + "runtime_write_allowed", + "evidence_hash" + ], + "properties": { + "packet_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "owner_agent": { + "enum": [ + "openclaw", + "hermes", + "nemotron" + ] + }, + "status": { + "enum": [ + "ready_for_owner_review", + "approval_required", + "blocked_by_policy" + ] + }, + "risk_tier": { + "enum": [ + "medium", + "high", + "critical" + ] + }, + "required_approval_fields": { + "$ref": "#/$defs/string_array" + }, + "blocked_runtime_actions": { + "$ref": "#/$defs/string_array" + }, + "operator_guidance": { + "type": "string" + }, + "approval_required": { + "const": true + }, + "runtime_write_allowed": { + "const": false + }, + "evidence_hash": { + "$ref": "#/$defs/redacted_sha256" + } + }, + "additionalProperties": false + }, + "canonical_readback_plan": { + "type": "object", + "required": [ + "plan_id", + "display_name", + "owner_agent", + "status", + "query_scope", + "required_fields", + "redaction_checks", + "live_query_enabled", + "runtime_write_allowed", + "evidence_hash" + ], + "properties": { + "plan_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "owner_agent": { + "enum": [ + "openclaw", + "hermes", + "nemotron" + ] + }, + "status": { + "enum": [ + "planned_no_runtime_read", + "blocked_by_policy" + ] + }, + "query_scope": { + "type": "string" + }, + "required_fields": { + "$ref": "#/$defs/string_array" + }, + "redaction_checks": { + "$ref": "#/$defs/string_array" + }, + "live_query_enabled": { + "const": false + }, + "runtime_write_allowed": { + "const": false + }, + "evidence_hash": { + "$ref": "#/$defs/redacted_sha256" + } + }, + "additionalProperties": false + }, + "rollback_drill_lane": { + "type": "object", + "required": [ + "lane_id", + "display_name", + "owner_agent", + "status", + "rollback_owner", + "required_steps", + "failure_if_missing", + "rollback_write_enabled", + "creates_runtime_write" + ], + "properties": { + "lane_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "owner_agent": { + "enum": [ + "openclaw", + "hermes", + "nemotron" + ] + }, + "status": { + "enum": [ + "planned_no_write", + "blocked_by_policy" + ] + }, + "rollback_owner": { + "type": "string" + }, + "required_steps": { + "$ref": "#/$defs/string_array" + }, + "failure_if_missing": { + "type": "string" + }, + "rollback_write_enabled": { + "const": false + }, + "creates_runtime_write": { + "const": false + } + }, + "additionalProperties": false + }, + "telegram_failure_receipt_gate": { + "type": "object", + "required": [ + "gate_id", + "display_name", + "owner_agent", + "status", + "required_evidence", + "failure_if_missing", + "queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "evidence_hash" + ], + "properties": { + "gate_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "owner_agent": { + "enum": [ + "openclaw", + "hermes", + "nemotron" + ] + }, + "status": { + "enum": [ + "ready_for_owner_review", + "blocked_by_policy" + ] + }, + "required_evidence": { + "$ref": "#/$defs/string_array" + }, + "failure_if_missing": { + "type": "string" + }, + "queue_write_enabled": { + "const": false + }, + "telegram_send_enabled": { + "const": false + }, + "bot_api_call_enabled": { + "const": false + }, + "evidence_hash": { + "$ref": "#/$defs/redacted_sha256" + } + }, + "additionalProperties": false + }, + "operator_action": { + "type": "object", + "required": [ + "action_id", + "action_type", + "display_name", + "owner_agent", + "operator_instruction", + "runtime_write_allowed" + ], + "properties": { + "action_id": { + "type": "string" + }, + "action_type": { + "enum": [ + "review_approval_package", + "validate_canonical_plan", + "review_rollback_drill", + "review_failure_receipt_gate", + "reject_or_promote" + ] + }, + "display_name": { + "type": "string" + }, + "owner_agent": { + "enum": [ + "openclaw", + "hermes", + "nemotron" + ] + }, + "operator_instruction": { + "type": "string" + }, + "runtime_write_allowed": { + "const": false + } + }, + "additionalProperties": false + }, + "display_redaction_contract": { + "type": "object", + "required": [ + "redaction_required", + "frontend_display_policy", + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed" + ], + "properties": { + "redaction_required": { + "const": true + }, + "frontend_display_policy": { + "type": "string" + }, + "raw_prompt_display_allowed": { + "const": false + }, + "private_reasoning_display_allowed": { + "const": false + }, + "secret_value_display_allowed": { + "const": false + }, + "raw_telegram_payload_display_allowed": { + "const": false + }, + "work_window_transcript_display_allowed": { + "const": false + } + }, + "additionalProperties": false + }, + "rollups": { + "type": "object", + "required": [ + "approval_packet_count", + "canonical_readback_plan_count", + "rollback_drill_lane_count", + "telegram_failure_receipt_gate_count", + "operator_action_count", + "approval_required_packet_count", + "blocked_packet_count", + "blocked_readback_plan_count", + "blocked_rollback_lane_count", + "blocked_telegram_gate_count", + "owner_approval_received_count", + "runtime_readback_execution_count", + "result_capture_write_count", + "score_write_count", + "learning_write_count", + "playbook_trust_write_count", + "reviewer_queue_write_count", + "gateway_queue_write_count", + "telegram_failure_receipt_send_count", + "bot_api_call_count", + "rollback_work_item_write_count", + "production_write_count", + "secret_read_count", + "destructive_operation_count" + ], + "properties": { + "approval_packet_count": { + "const": 5 + }, + "canonical_readback_plan_count": { + "const": 4 + }, + "rollback_drill_lane_count": { + "const": 4 + }, + "telegram_failure_receipt_gate_count": { + "const": 4 + }, + "operator_action_count": { + "const": 5 + }, + "approval_required_packet_count": { + "type": "integer", + "minimum": 0 + }, + "blocked_packet_count": { + "type": "integer", + "minimum": 0 + }, + "blocked_readback_plan_count": { + "type": "integer", + "minimum": 0 + }, + "blocked_rollback_lane_count": { + "type": "integer", + "minimum": 0 + }, + "blocked_telegram_gate_count": { + "type": "integer", + "minimum": 0 + }, + "approved_without_execution_meta_24h": { + "type": "integer", + "minimum": 0 + }, + "execution_failed_with_matched_24h": { + "type": "integer", + "minimum": 0 + }, + "owner_approval_received_count": { + "const": 0 + }, + "runtime_readback_execution_count": { + "const": 0 + }, + "result_capture_write_count": { + "const": 0 + }, + "score_write_count": { + "const": 0 + }, + "learning_write_count": { + "const": 0 + }, + "playbook_trust_write_count": { + "const": 0 + }, + "reviewer_queue_write_count": { + "const": 0 + }, + "gateway_queue_write_count": { + "const": 0 + }, + "telegram_failure_receipt_send_count": { + "const": 0 + }, + "bot_api_call_count": { + "const": 0 + }, + "rollback_work_item_write_count": { + "const": 0 + }, + "production_write_count": { + "const": 0 + }, + "secret_read_count": { + "const": 0 + }, + "destructive_operation_count": { + "const": 0 + } + }, + "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 191ef66f1..4f2a3b382 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 @@ -646,6 +646,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 owner-approved result capture dry-run;承接 P2-105 scorecard / result capture contract / promotion gate,建立 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action;owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,已由 P2-107 承接 | | `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-readback` | P2-107 owner-approved result capture readback / promotion readiness;承接 P2-106 no-write dry-run,建立 5 個 readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action;canonical runtime readback、owner approval received、readback digest generated、promotion approved、reviewer queue write、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-108 | | `docs/evaluations/ai_agent_report_status_board_2026-06-13.json` + `GET /api/v1/agents/agent-report-status-board` | P2-108 日週月報與 Agent 工作狀態總覽;承接 P2-403J / P2-403L-M-N / P2-107,將日報、週報、月報、OpenClaw / Hermes / NemoTron 工作狀態、工作量、圖表與 operator answer cards 收斂到治理頁;三種報告可見完成度 `100%`,Agent 狀態 `3`、圖表 `3`、工作量 `91`、已完成 `79`、待審核 `12`;scheduler、Gateway queue write、Telegram send、讀報回執、AI analysis run、中低風險 auto execution、production optimization write 全部 `0 / false`,下一步 P2-109 | +| `docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json` + `GET /api/v1/agents/agent-runtime-readback-approval-package` | P2-109 runtime readback approval package;承接 P2-107 readback / promotion readiness 與 P2-108 report status board,建立 5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate 與 5 個 operator action;canonical runtime readback、runtime readback execution、owner approval received、reviewer queue write、rollback work item write、Gateway queue、Telegram failure receipt、Bot API、score / result capture / learning / trust / production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-110 | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -743,6 +744,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 27. 建立 owner-approved result capture dry-run。✅ P2-106 已完成;result capture dry-run template `5`、score fixture `5`、dry-run gate `7`、operator action `5`,owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-107 承接。 28. 建立 owner-approved result capture readback / promotion readiness。✅ P2-107 已完成;readback digest `5`、promotion review `5`、failure lane `4`、reviewer queue preview `4`、operator action `5`,canonical runtime readback、owner approval received、readback digest generated、promotion approved、reviewer queue write、score / result capture / learning / trust / Gateway / Telegram / production write 仍為 `0 / false`。下一步 P2-108。 29. 建立日週月報與 Agent 工作狀態總覽。✅ P2-108 已完成;日報 / 週報 / 月報可見完成度皆 `100%`,OpenClaw / Hermes / NemoTron 工作狀態報告 `3`,圖表 `3`,operator answer card `4`,工作量 `91`、已完成 `79`、待審核 `12`;scheduler、Gateway queue write、Telegram send、report receipt、AI analysis run、中低風險 auto worker、production optimization write 仍為 `0 / false`。下一步 P2-109。 +30. 建立 runtime readback approval package。✅ P2-109 已完成;批准包 `5`、canonical readback plan `4`、rollback drill `4`、Telegram failure receipt gate `4`、operator action `5`,canonical runtime readback、runtime readback execution、owner approval received、reviewer queue write、rollback work item write、Gateway / Telegram failure receipt / Bot API / production write 仍為 `0 / false`。下一步 P2-110。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -783,6 +785,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence | `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-readback` | P2-107 committed snapshot;5 個 fixture-only readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview、5 個 operator action;不讀 canonical runtime target、不寫 reviewer queue、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不讀 secret、不執行 destructive action | | `docs/schemas/ai_agent_report_status_board_v1.schema.json` | P2-108 日週月報與 Agent 工作狀態總覽 schema;強制日報 / 週報 / 月報、每個 Agent 狀態、工作量與圖表可見,同時保持 scheduler、Gateway queue write、Telegram send、report receipt、AI analysis run、中低風險 auto execution、production optimization write 全部未啟用 | | `docs/evaluations/ai_agent_report_status_board_2026-06-13.json` + `GET /api/v1/agents/agent-report-status-board` | P2-108 committed snapshot;三種報告 `100%` 可見、3 個 Agent 狀態報告、3 張圖表、4 張統帥問答卡;工作量 `91`、已完成 `79`、待審核 `12`,live delivery / Telegram send / runtime work / auto optimization 全部 `0` | +| `docs/schemas/ai_agent_runtime_readback_approval_package_v1.schema.json` | P2-109 runtime readback approval package schema;強制 canonical runtime readback、runtime readback execution、owner approval received、reviewer queue write、rollback work item write、Gateway queue、Telegram failure receipt、Bot API、score write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部維持 `0 / false` | +| `docs/evaluations/ai_agent_runtime_readback_approval_package_2026-06-13.json` + `GET /api/v1/agents/agent-runtime-readback-approval-package` | P2-109 committed snapshot;5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate、5 個 operator action;不讀 canonical runtime target、不寫 reviewer queue、不寫 rollback work item、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret、不執行 destructive action | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader;強制 live flags / DB / Redis / Telegram / transcript / 私有推理全部關閉 | | `GET /api/v1/agents/agent-interaction-learning-proof` | 治理 API;只回傳證據面,不啟動 worker、不碰 live DB/Redis、不發 Telegram | | `docs/schemas/ai_agent_live_read_model_gate_v1.schema.json` | P2-403B live read model gate schema;強制 DB / Redis / worker / Telegram / learning writeback 仍需批准 | @@ -1979,6 +1983,13 @@ Phase 6 完成後 - 政策裁決:P2-106 只允許在統帥批准後產生 no-write preview 與 verifier fixture;不得把批准包、score fixture 或 dry-run template 解讀成 runtime score / result capture / learning 已寫入。 - 本波仍不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;已由 P2-107 承接。 +### 2026-06-13 15:12 (台北) — §3.2 / §5 — 完成 P2-109 runtime readback approval package — 把下一關 runtime readback 固定成批准包 + +- 新增 `ai_agent_runtime_readback_approval_package_v1` schema / committed snapshot / loader / API / 測試,承接 P2-107 的 readback digest、promotion review、failure lane、reviewer queue preview 與 P2-108 report status board,定義 5 個批准包、4 個 canonical readback plan、4 條 rollback drill、4 個 Telegram failure receipt gate 與 5 個 operator action。 +- `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-runtime-readback-approval-package`,治理頁顯示 P2-109 進度 `100%`、批准包 `5`、readback plan `4`、rollback drill `4`、Telegram failure receipt gate `4`、操作選項 `5`、阻擋總數 `5`、owner approval received `0`、runtime readback execution `0`、Telegram failure receipt send `0`、rollback work item write `0` 與 live writes `0`。 +- 政策裁決:P2-109 只允許 owner review 批准包、canonical readback plan、rollback drill 與 failure receipt gate 可視化;不得把批准包或 gate 解讀成 canonical runtime readback、reviewer queue write、Telegram 實發、Bot API 呼叫、rollback work item write 或 live writer 已啟用。 +- 本波仍不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 rollback work item、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;下一步 P2-110。 + ### 2026-06-13 14:18 (台北) — §3.2 / §5 — 完成 P2-107 owner-approved result capture readback / promotion readiness — 把批准後 readback 與升級準備固定成只讀閘門 - 新增 `ai_agent_owner_approved_result_capture_readback_v1` schema / committed snapshot / loader / API / 測試,承接 P2-106 的 no-write template、score fixture 與 dry-run gate,定義 5 個 readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action。