diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 541d6a179..32a8118b6 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -88,6 +88,9 @@ from src.services.ai_agent_12_agent_war_room import ( from src.services.ai_agent_professional_task_expansion import ( load_latest_ai_agent_professional_task_expansion, ) +from src.services.ai_agent_receipt_readback_owner_review import ( + load_latest_ai_agent_receipt_readback_owner_review, +) from src.services.ai_agent_matched_playbook_learning_gap import ( load_latest_ai_agent_matched_playbook_learning_gap, ) @@ -796,6 +799,36 @@ async def get_agent_professional_task_expansion() -> dict[str, Any]: ) from exc +@router.get( + "/agent-receipt-readback-owner-review", + response_model=dict[str, Any], + summary="取得 P2-406B AI Agent receipt readback owner review", + description=( + "讀取最新已提交的 P2-406B receipt readback owner review 只讀快照;" + "此端點只呈現日報 / 週報 / 月報、Telegram receipt、P2-004 供應鏈漂移與報表真相 gate 的 owner review," + "不啟用排程、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 receipt production target、" + "不啟動 AI analysis worker、不執行中低風險自動處理、不寫 production、不讀 secret、" + "不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。" + ), +) +async def get_agent_receipt_readback_owner_review() -> dict[str, Any]: + """回傳最新 P2-406B receipt readback owner review 只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_receipt_readback_owner_review) + 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_receipt_readback_owner_review_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P2-406B AI Agent receipt readback owner review 快照無效", + ) from exc + + @router.get( "/agent-communication-learning-contract", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_receipt_readback_owner_review.py b/apps/api/src/services/ai_agent_receipt_readback_owner_review.py new file mode 100644 index 000000000..6253efafa --- /dev/null +++ b/apps/api/src/services/ai_agent_receipt_readback_owner_review.py @@ -0,0 +1,329 @@ +""" +P2-406B AI Agent receipt readback owner review snapshot. + +Loads the latest committed, read-only owner review surface that links daily / +weekly / monthly reports, Telegram receipt review, P2-004 supply-chain drift, +and P2-403J report-truth gates. This service intentionally does not send +Telegram messages, write Gateway queues, call the Bot API, read secrets, run +AI analysis workers, or mutate production. +""" + +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_receipt_readback_owner_review_*.json" +_SCHEMA_VERSION = "ai_agent_receipt_readback_owner_review_v1" +_RUNTIME_AUTHORITY = "receipt_readback_owner_review_only_no_send_or_write" +_EXPECTED_CURRENT_TASK = "P2-406B" +_EXPECTED_NEXT_TASK = "P2-407" +_EXPECTED_CANONICAL_ROOM = "AwoooI SRE 戰情室" +_EXPECTED_CANONICAL_ROOM_ENV = "SRE_GROUP_CHAT_ID" +_EXPECTED_SOURCE_SCHEMAS = { + "ai_agent_report_status_board_v1", + "ai_agent_report_live_delivery_approval_package_v1", + "ai_agent_telegram_receipt_approval_package_v1", + "ai_agent_professional_task_expansion_v1", + "dependency_supply_chain_drift_monitor_v1", + "ai_agent_report_truth_actionability_review_v1", +} +_FALSE_BOUNDARY_FLAGS = { + "scheduler_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "delivery_receipt_write_enabled", + "report_receipt_write_enabled", + "receipt_production_write_enabled", + "ai_analysis_run_enabled", + "medium_low_auto_execution_enabled", + "production_optimization_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", + "external_lookup_enabled", +} +_TRUE_BOUNDARY_FLAGS = { + "owner_review_required_before_canary", + "read_only_review_allowed", +} +_ZERO_ROLLUP_FIELDS = { + "live_write_count", + "telegram_send_count", + "gateway_queue_write_count", + "bot_api_call_count", + "production_write_count", + "secret_read_count", + "paid_api_call_count", + "host_write_count", + "kubectl_action_count", +} +_FORBIDDEN_PUBLIC_TERMS = { + "工作視窗", + "對話內容", + "批准!繼續", + "In app browser", + "My request for Codex", + "browser_context", + "codex_user_message", + "prompt_text", + "chain_of_thought", + "chain-of-thought", + "private reasoning", + "authorization_header", + "authorization header", + "secret_value", + "secret value", + "raw_payload", + "raw prompt", + "telegram token", +} + + +def load_latest_ai_agent_receipt_readback_owner_review( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed P2-406B receipt readback owner review.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent receipt readback owner review snapshots found in {directory}" + ) + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + + label = str(latest) + _require_schema(payload, label) + _require_source_readbacks(payload, label) + _require_receipt_readback_plan(payload, label) + _require_owner_review_gates(payload, label) + _require_drift_and_report_truth(payload, label) + _require_activation_boundaries(payload, label) + _require_rollups(payload, label) + _require_no_forbidden_public_terms(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + + status = payload.get("program_status") or {} + expected = { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": _EXPECTED_CURRENT_TASK, + "next_task_id": _EXPECTED_NEXT_TASK, + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_source_readbacks(payload: dict[str, Any], label: str) -> None: + source_refs = payload.get("source_refs") or [] + readbacks = payload.get("source_readbacks") or [] + if not source_refs: + raise ValueError(f"{label}: source_refs must not be empty") + if len(readbacks) < len(_EXPECTED_SOURCE_SCHEMAS): + raise ValueError(f"{label}: source_readbacks must include all required sources") + + schemas = {item.get("source_schema_version") for item in readbacks} + missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas) + if missing: + raise ValueError(f"{label}: missing source readback schemas: {missing}") + + for item in readbacks: + readback_id = item.get("readback_id") or "" + for field in ("source_ref", "endpoint", "owner_agent", "status", "evidence_status", "key_readback", "next_action"): + if not item.get(field): + raise ValueError(f"{label}: source readback {readback_id} missing {field}") + + +def _require_receipt_readback_plan(payload: dict[str, Any], label: str) -> None: + plan = payload.get("receipt_readback_plan") or {} + expected = { + "canonical_room": _EXPECTED_CANONICAL_ROOM, + "canonical_room_env": _EXPECTED_CANONICAL_ROOM_ENV, + "gateway_owner": "telegram_ops", + "arbiter": "openclaw", + "receipt_owner": "hermes", + "replay_owner": "nemotron", + "dry_run_receipt_only": True, + "owner_review_required_before_canary": True, + "canary_send_approved": False, + "receipt_production_write_enabled": False, + } + mismatches = _mismatches(plan, expected) + if mismatches: + raise ValueError(f"{label}: receipt_readback_plan mismatch: {mismatches}") + + checks = plan.get("readback_checks") or [] + if len(checks) != 8: + raise ValueError(f"{label}: receipt_readback_plan.readback_checks must contain 8 checks") + for check in checks: + check_id = check.get("check_id") or "" + if not check.get("evidence_refs"): + raise ValueError(f"{label}: readback check {check_id} must include evidence_refs") + if not check.get("blocked_runtime_action"): + raise ValueError(f"{label}: readback check {check_id} must include blocked_runtime_action") + + telegram_policy = payload.get("telegram_policy") or {} + expected_policy = { + "canonical_room": _EXPECTED_CANONICAL_ROOM, + "canonical_room_env": _EXPECTED_CANONICAL_ROOM_ENV, + "gateway_queue_write_allowed": False, + "direct_bot_api_allowed": False, + "telegram_send_allowed": False, + "receipt_write_allowed": False, + } + policy_mismatches = _mismatches(telegram_policy, expected_policy) + if policy_mismatches: + raise ValueError(f"{label}: telegram_policy mismatch: {policy_mismatches}") + + +def _require_owner_review_gates(payload: dict[str, Any], label: str) -> None: + gates = payload.get("owner_review_gates") or [] + if len(gates) != 6: + raise ValueError(f"{label}: owner_review_gates must contain 6 gates") + + for gate in gates: + gate_id = gate.get("gate_id") or "" + if gate.get("approval_required") is not True: + raise ValueError(f"{label}: owner review gate {gate_id} must require approval") + if gate.get("status") != "owner_review_required": + raise ValueError(f"{label}: owner review gate {gate_id} must remain owner_review_required") + for field in ( + "required_owner_fields", + "acceptance_checks", + "rejection_reasons", + "blocked_runtime_actions", + "evidence_refs", + ): + if not gate.get(field): + raise ValueError(f"{label}: owner review gate {gate_id} missing {field}") + + +def _require_drift_and_report_truth(payload: dict[str, Any], label: str) -> None: + drift = payload.get("drift_monitor_owner_review") or {} + expected_drift = { + "source_task_id": "P2-004", + "drift_candidate_count": 9, + "action_required_candidate_count": 9, + "external_lookup_allowed": False, + "package_upgrade_allowed": False, + } + drift_mismatches = _mismatches(drift, expected_drift) + if drift_mismatches: + raise ValueError(f"{label}: drift_monitor_owner_review mismatch: {drift_mismatches}") + if not drift.get("owner_actions"): + raise ValueError(f"{label}: drift_monitor_owner_review.owner_actions must not be empty") + + report_truth = payload.get("report_truth_owner_review") or {} + expected_truth = { + "source_task_id": "P2-403J", + "all_zero_weekly_report_is_actionable_anomaly": True, + "all_zero_weekly_report_confidence": "low_trust_actionable_anomaly", + "zero_signal_finding_count": 5, + "telegram_report_send_allowed": False, + "cronjob_change_allowed": False, + "freshness_gate_implemented": False, + "source_confidence_gate_implemented": False, + "actionability_score_implemented": False, + } + truth_mismatches = _mismatches(report_truth, expected_truth) + if truth_mismatches: + raise ValueError(f"{label}: report_truth_owner_review mismatch: {truth_mismatches}") + if not report_truth.get("owner_actions"): + raise ValueError(f"{label}: report_truth_owner_review.owner_actions must not be empty") + + +def _require_activation_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = payload.get("activation_boundaries") or {} + wrong_true = sorted(flag for flag in _TRUE_BOUNDARY_FLAGS if boundaries.get(flag) is not True) + if wrong_true: + raise ValueError(f"{label}: activation boundaries must remain true: {wrong_true}") + + wrong_false = sorted(flag for flag in _FALSE_BOUNDARY_FLAGS if boundaries.get(flag) is not False) + if wrong_false: + raise ValueError(f"{label}: activation boundaries must remain false: {wrong_false}") + + +def _require_rollups(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + report_cadences = (payload.get("report_owner_review") or {}).get("cadences") or [] + readback_checks = (payload.get("receipt_readback_plan") or {}).get("readback_checks") or [] + owner_review_gates = payload.get("owner_review_gates") or [] + source_readbacks = payload.get("source_readbacks") or [] + boundaries = payload.get("activation_boundaries") or {} + drift = payload.get("drift_monitor_owner_review") or {} + report_truth = payload.get("report_truth_owner_review") or {} + + expected_counts = { + "source_readback_count": len(source_readbacks), + "report_cadence_count": len(report_cadences), + "owner_review_gate_count": len(owner_review_gates), + "receipt_readback_check_count": len(readback_checks), + "drift_candidate_count": drift.get("drift_candidate_count"), + "report_truth_blocker_count": report_truth.get("zero_signal_finding_count"), + "approval_required_count": sum(1 for gate in owner_review_gates if gate.get("approval_required") is True), + "blocked_runtime_action_count": sum(1 for flag in _FALSE_BOUNDARY_FLAGS if boundaries.get(flag) is False), + } + mismatched = { + key: {"expected": expected, "actual": rollups.get(key)} + for key, expected in expected_counts.items() + if rollups.get(key) != expected + } + if mismatched: + raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}") + + non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: rollup live/write counters must remain zero: {non_zero}") + + +def _require_no_forbidden_public_terms(payload: Any, label: str) -> None: + offenders: list[str] = [] + + def visit(value: Any, path: str) -> None: + if isinstance(value, dict): + for key, child in value.items(): + visit(child, f"{path}.{key}") + elif isinstance(value, list): + for index, child in enumerate(value): + visit(child, f"{path}[{index}]") + elif isinstance(value, str): + lowered = value.lower() + for term in _FORBIDDEN_PUBLIC_TERMS: + if term.lower() in lowered: + offenders.append(f"{path}: {term}") + + visit(payload, "$") + if offenders: + raise ValueError(f"{label}: forbidden public terms present: {offenders[:5]}") + + +def _mismatches(actual: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + key: {"expected": expected_value, "actual": actual.get(key)} + for key, expected_value in expected.items() + if actual.get(key) != expected_value + } diff --git a/apps/api/tests/test_ai_agent_receipt_readback_owner_review.py b/apps/api/tests/test_ai_agent_receipt_readback_owner_review.py new file mode 100644 index 000000000..2f6155a70 --- /dev/null +++ b/apps/api/tests/test_ai_agent_receipt_readback_owner_review.py @@ -0,0 +1,314 @@ +from __future__ import annotations + +import json + +import pytest + +from src.services.ai_agent_receipt_readback_owner_review import ( + load_latest_ai_agent_receipt_readback_owner_review, +) + + +def test_load_latest_ai_agent_receipt_readback_owner_review_reads_newest_file(tmp_path): + older = _snapshot(generated_at="2026-06-17T00:00:00+08:00") + newer = _snapshot(generated_at="2026-06-18T00:00:00+08:00") + (tmp_path / "ai_agent_receipt_readback_owner_review_2026-06-17.json").write_text( + json.dumps(older), + encoding="utf-8", + ) + (tmp_path / "ai_agent_receipt_readback_owner_review_2026-06-18.json").write_text( + json.dumps(newer), + encoding="utf-8", + ) + + loaded = load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + assert loaded["generated_at"] == "2026-06-18T00:00:00+08:00" + assert loaded["program_status"]["current_task_id"] == "P2-406B" + assert loaded["program_status"]["next_task_id"] == "P2-407" + assert loaded["receipt_readback_plan"]["receipt_owner"] == "hermes" + assert loaded["receipt_readback_plan"]["arbiter"] == "openclaw" + assert loaded["receipt_readback_plan"]["replay_owner"] == "nemotron" + assert loaded["activation_boundaries"]["telegram_send_enabled"] is False + assert loaded["activation_boundaries"]["gateway_queue_write_enabled"] is False + + +def test_ai_agent_receipt_readback_owner_review_requires_read_only_mode(tmp_path): + snapshot = _snapshot() + snapshot["program_status"]["read_only_mode"] = False + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="read_only_mode"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_blocks_telegram_send(tmp_path): + snapshot = _snapshot() + snapshot["activation_boundaries"]["telegram_send_enabled"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="activation boundaries"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_blocks_bot_api_call(tmp_path): + snapshot = _snapshot() + snapshot["telegram_policy"]["direct_bot_api_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="telegram_policy"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_requires_rollup_consistency(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["owner_review_gate_count"] = 99 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_requires_gate_evidence(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_gates"][0]["evidence_refs"] = [] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="evidence_refs"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_rejects_private_terms(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_gates"][0]["next_action"] = "不要顯示工作視窗內容" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden public terms"): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def test_ai_agent_receipt_readback_owner_review_fails_when_missing(tmp_path): + with pytest.raises(FileNotFoundError): + load_latest_ai_agent_receipt_readback_owner_review(tmp_path) + + +def _write_snapshot(tmp_path, payload: dict) -> None: + (tmp_path / "ai_agent_receipt_readback_owner_review_2026-06-18.json").write_text( + json.dumps(payload), + encoding="utf-8", + ) + + +def _snapshot(*, generated_at: str = "2026-06-18T00:00:00+08:00") -> dict: + boundaries = { + "owner_review_required_before_canary": True, + "read_only_review_allowed": True, + "scheduler_enabled": False, + "gateway_queue_write_enabled": False, + "telegram_send_enabled": False, + "bot_api_call_enabled": False, + "delivery_receipt_write_enabled": False, + "report_receipt_write_enabled": False, + "receipt_production_write_enabled": False, + "ai_analysis_run_enabled": False, + "medium_low_auto_execution_enabled": False, + "production_optimization_write_enabled": False, + "production_write_enabled": False, + "secret_read_enabled": False, + "paid_api_enabled": False, + "host_write_enabled": False, + "kubectl_action_enabled": False, + "destructive_operation_enabled": False, + "external_lookup_enabled": False, + } + return { + "schema_version": "ai_agent_receipt_readback_owner_review_v1", + "generated_at": generated_at, + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-406B", + "next_task_id": "P2-407", + "read_only_mode": True, + "runtime_authority": "receipt_readback_owner_review_only_no_send_or_write", + "status_note": "只讀 owner review。", + }, + "source_refs": ["docs/evaluations/example.json"], + "source_readbacks": [ + _source("status", "ai_agent_report_status_board_v1", "openclaw"), + _source("delivery", "ai_agent_report_live_delivery_approval_package_v1", "hermes"), + _source("receipt", "ai_agent_telegram_receipt_approval_package_v1", "hermes"), + _source("professional", "ai_agent_professional_task_expansion_v1", "nemotron"), + _source("drift", "dependency_supply_chain_drift_monitor_v1", "openclaw"), + _source("truth", "ai_agent_report_truth_actionability_review_v1", "openclaw"), + ], + "rollups": { + "source_readback_count": 6, + "report_cadence_count": 3, + "owner_review_gate_count": 6, + "receipt_readback_check_count": 8, + "drift_candidate_count": 9, + "report_truth_blocker_count": 5, + "approval_required_count": 6, + "blocked_runtime_action_count": 17, + "live_write_count": 0, + "telegram_send_count": 0, + "gateway_queue_write_count": 0, + "bot_api_call_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "paid_api_call_count": 0, + "host_write_count": 0, + "kubectl_action_count": 0, + }, + "report_owner_review": { + "cadences": [ + _cadence("daily", "hermes"), + _cadence("weekly", "openclaw"), + _cadence("monthly", "nemotron"), + ] + }, + "receipt_readback_plan": { + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_owner": "telegram_ops", + "arbiter": "openclaw", + "receipt_owner": "hermes", + "replay_owner": "nemotron", + "dry_run_receipt_only": True, + "owner_review_required_before_canary": True, + "canary_send_approved": False, + "receipt_production_write_enabled": False, + "readback_checks": [_check(index) for index in range(8)], + }, + "owner_review_gates": [ + _gate("daily_report_receipt_owner_review", "hermes"), + _gate("weekly_report_truth_owner_review", "openclaw"), + _gate("monthly_report_owner_review", "nemotron"), + _gate("telegram_canary_receipt_owner_review", "hermes"), + _gate("supply_chain_drift_owner_review", "openclaw"), + _gate("report_truth_data_chain_owner_review", "openclaw"), + ], + "drift_monitor_owner_review": { + "source_task_id": "P2-004", + "drift_candidate_count": 9, + "action_required_candidate_count": 9, + "stale_source_snapshot_count": 5, + "external_lookup_allowed": False, + "package_upgrade_allowed": False, + "owner_actions": ["prepare_packet"], + }, + "report_truth_owner_review": { + "source_task_id": "P2-403J", + "all_zero_weekly_report_is_actionable_anomaly": True, + "all_zero_weekly_report_confidence": "low_trust_actionable_anomaly", + "zero_signal_finding_count": 5, + "critical_finding_count": 1, + "high_finding_count": 3, + "telegram_report_send_allowed": False, + "cronjob_change_allowed": False, + "freshness_gate_implemented": False, + "source_confidence_gate_implemented": False, + "actionability_score_implemented": False, + "owner_actions": ["keep_anomaly_visible"], + }, + "activation_boundaries": boundaries, + "telegram_policy": { + "status": "owner_review_only_no_send", + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_queue_write_allowed": False, + "direct_bot_api_allowed": False, + "telegram_send_allowed": False, + "receipt_write_allowed": False, + }, + "agent_roles": [ + _role("openclaw"), + _role("hermes"), + _role("nemotron"), + ], + "operator_decision": { + "status": "owner_review_required_before_canary", + "approval_packet_required": True, + "rollback_plan_required": True, + "mute_plan_required": True, + "required_fields": ["owner_identity"], + "stop_conditions": ["telegram_send_count 非 0"], + "next_decision_point": "P2-407", + }, + "next_actions": [ + { + "task_id": "P2-407", + "priority": "P2", + "summary": "下一步", + "gate": "no write", + } + ], + } + + +def _source(readback_id: str, schema: str, owner: str) -> dict: + return { + "readback_id": readback_id, + "source_schema_version": schema, + "source_ref": f"docs/evaluations/{readback_id}.json", + "endpoint": f"GET /api/v1/agents/{readback_id}", + "owner_agent": owner, + "status": "readback_ready", + "evidence_status": "ready", + "key_readback": "zero live write", + "next_action": "owner review", + } + + +def _cadence(cadence_id: str, owner: str) -> dict: + return { + "cadence_id": cadence_id, + "display_name": cadence_id, + "owner_agent": owner, + "completion_percent": 100, + "delivery_state": "draft_only", + "source_confidence": "visible", + "live_delivery_count": 0, + "review_status": "owner_review_required_before_send", + "next_gate": f"{cadence_id}_gate", + } + + +def _check(index: int) -> dict: + return { + "check_id": f"check_{index}", + "owner_agent": "hermes", + "status": "ready_no_send", + "evidence_refs": ["GET /api/v1/agents/example"], + "blocked_runtime_action": "telegram_send", + } + + +def _gate(gate_id: str, owner: str) -> dict: + return { + "gate_id": gate_id, + "title": gate_id, + "owner_agent": owner, + "risk_tier": "high", + "status": "owner_review_required", + "approval_required": True, + "required_owner_fields": ["owner_identity"], + "acceptance_checks": ["check"], + "rejection_reasons": ["missing owner"], + "blocked_runtime_actions": ["telegram_send_enabled"], + "evidence_refs": ["GET /api/v1/agents/example"], + "next_action": "owner review", + } + + +def _role(agent_id: str) -> dict: + return { + "agent_id": agent_id, + "role": "role", + "specialty": "specialty", + "autonomy_level": "L2", + "approval_gate": "human", + "runtime_write_allowed": False, + "outputs": ["packet"], + } diff --git a/apps/api/tests/test_ai_agent_receipt_readback_owner_review_api.py b/apps/api/tests/test_ai_agent_receipt_readback_owner_review_api.py new file mode 100644 index 000000000..b06ef1f8e --- /dev/null +++ b/apps/api/tests/test_ai_agent_receipt_readback_owner_review_api.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_receipt_readback_owner_review_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-receipt-readback-owner-review") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_receipt_readback_owner_review_v1" + assert data["program_status"]["current_task_id"] == "P2-406B" + assert data["program_status"]["next_task_id"] == "P2-407" + assert data["program_status"]["read_only_mode"] is True + assert data["rollups"]["source_readback_count"] == len(data["source_readbacks"]) == 6 + assert data["rollups"]["report_cadence_count"] == 3 + assert data["rollups"]["owner_review_gate_count"] == len(data["owner_review_gates"]) == 6 + assert data["rollups"]["receipt_readback_check_count"] == 8 + assert data["rollups"]["drift_candidate_count"] == 9 + assert data["rollups"]["report_truth_blocker_count"] == 5 + assert data["rollups"]["approval_required_count"] == 6 + assert data["rollups"]["live_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["secret_read_count"] == 0 + assert data["rollups"]["paid_api_call_count"] == 0 + assert data["rollups"]["host_write_count"] == 0 + assert data["rollups"]["kubectl_action_count"] == 0 + assert data["receipt_readback_plan"]["canonical_room"] == "AwoooI SRE 戰情室" + assert data["receipt_readback_plan"]["canonical_room_env"] == "SRE_GROUP_CHAT_ID" + assert data["receipt_readback_plan"]["arbiter"] == "openclaw" + assert data["receipt_readback_plan"]["receipt_owner"] == "hermes" + assert data["receipt_readback_plan"]["replay_owner"] == "nemotron" + assert data["receipt_readback_plan"]["canary_send_approved"] is False + assert data["activation_boundaries"]["gateway_queue_write_enabled"] is False + assert data["activation_boundaries"]["telegram_send_enabled"] is False + assert data["activation_boundaries"]["bot_api_call_enabled"] is False + assert data["activation_boundaries"]["receipt_production_write_enabled"] is False + assert data["activation_boundaries"]["production_write_enabled"] is False + assert data["activation_boundaries"]["secret_read_enabled"] is False + assert data["activation_boundaries"]["paid_api_enabled"] is False + assert data["activation_boundaries"]["host_write_enabled"] is False + assert data["activation_boundaries"]["kubectl_action_enabled"] is False + assert data["telegram_policy"]["telegram_send_allowed"] is False + assert data["telegram_policy"]["gateway_queue_write_allowed"] is False + assert data["telegram_policy"]["direct_bot_api_allowed"] is False diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index ee60245c9..359e48855 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -3653,6 +3653,60 @@ "production": "Production write" } }, + "receiptOwnerReview": { + "title": "P2-406B Receipt Readback Owner Review", + "subtitle": "{current} → {next};Owner gates {gates};關閉中的 runtime 操作 {blocked}。", + "source": "{generated} · {current} → {next}", + "badges": { + "room": "戰情室 {room}", + "owner": "receipt owner {owner}", + "canary": "canary approved {value}" + }, + "metrics": { + "overall": "完成度", + "sources": "來源讀回", + "cadences": "報告節奏", + "gates": "Owner gates", + "checks": "Receipt checks", + "approvals": "待批准", + "liveWrites": "Live writes" + }, + "sections": { + "ownerGates": "Owner review gates", + "receiptPlan": "Receipt readback plan", + "boundaries": "仍維持關閉的 live 操作" + }, + "labels": { + "owner": "Owner", + "requiredFields": "必填欄位", + "acceptance": "驗收", + "blockedActions": "阻擋操作", + "evidence": "證據", + "arbiter": "arbiter {value}", + "receiptOwner": "receipt {value}", + "replayOwner": "replay {value}", + "env": "env {value}", + "cadenceDetail": "owner {owner} · {state} · confidence {confidence}", + "boundaryClosed": "保持 false / 0", + "boundaryOpen": "異常開啟", + "truthBlockers": "truth blockers {count}", + "driftCandidates": "drift candidates {count}" + }, + "boundaries": { + "gateway": "Gateway queue write", + "telegram": "Telegram send", + "botApi": "Bot API call", + "receipt": "Receipt production write", + "production": "Production write", + "secret": "Secret / paid / host / kubectl" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index ee60245c9..359e48855 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -3653,6 +3653,60 @@ "production": "Production write" } }, + "receiptOwnerReview": { + "title": "P2-406B Receipt Readback Owner Review", + "subtitle": "{current} → {next};Owner gates {gates};關閉中的 runtime 操作 {blocked}。", + "source": "{generated} · {current} → {next}", + "badges": { + "room": "戰情室 {room}", + "owner": "receipt owner {owner}", + "canary": "canary approved {value}" + }, + "metrics": { + "overall": "完成度", + "sources": "來源讀回", + "cadences": "報告節奏", + "gates": "Owner gates", + "checks": "Receipt checks", + "approvals": "待批准", + "liveWrites": "Live writes" + }, + "sections": { + "ownerGates": "Owner review gates", + "receiptPlan": "Receipt readback plan", + "boundaries": "仍維持關閉的 live 操作" + }, + "labels": { + "owner": "Owner", + "requiredFields": "必填欄位", + "acceptance": "驗收", + "blockedActions": "阻擋操作", + "evidence": "證據", + "arbiter": "arbiter {value}", + "receiptOwner": "receipt {value}", + "replayOwner": "replay {value}", + "env": "env {value}", + "cadenceDetail": "owner {owner} · {state} · confidence {confidence}", + "boundaryClosed": "保持 false / 0", + "boundaryOpen": "異常開啟", + "truthBlockers": "truth blockers {count}", + "driftCandidates": "drift candidates {count}" + }, + "boundaries": { + "gateway": "Gateway queue write", + "telegram": "Telegram send", + "botApi": "Bot API call", + "receipt": "Receipt production write", + "production": "Production write", + "secret": "Secret / paid / host / kubectl" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index d599fa246..b75052333 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 @@ -46,6 +46,7 @@ import { apiClient, type AiAgent12AgentWarRoomSnapshot, type AiAgentProfessionalTaskExpansionSnapshot, + type AiAgentReceiptReadbackOwnerReviewSnapshot, type AiAgentCandidateOperationDryRunEvidenceSnapshot, type AiAgentCriticReviewerResultCaptureSnapshot, type AiAgentDeploymentLayoutSnapshot, @@ -569,6 +570,7 @@ export function AutomationInventoryTab() { const [deploymentLayout, setDeploymentLayout] = useState(null) const [warRoom, setWarRoom] = useState(null) const [professionalTaskExpansion, setProfessionalTaskExpansion] = useState(null) + const [receiptReadbackOwnerReview, setReceiptReadbackOwnerReview] = useState(null) const [proactiveOperations, setProactiveOperations] = useState(null) const [interactionLearningProof, setInteractionLearningProof] = useState(null) const [liveReadModelGate, setLiveReadModelGate] = useState(null) @@ -653,6 +655,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentDeploymentLayout(), apiClient.getAiAgent12AgentWarRoom(), apiClient.getAiAgentProfessionalTaskExpansion(), + apiClient.getAiAgentReceiptReadbackOwnerReview(), apiClient.getAiAgentProactiveOperationsContract(), apiClient.getAiAgentInteractionLearningProof(), apiClient.getAiAgentLiveReadModelGate(), @@ -736,6 +739,7 @@ export function AutomationInventoryTab() { deploymentLayoutResult, warRoomResult, professionalTaskExpansionResult, + receiptReadbackOwnerReviewResult, proactiveOperationsResult, interactionLearningProofResult, liveReadModelGateResult, @@ -816,6 +820,7 @@ export function AutomationInventoryTab() { setDeploymentLayout(deploymentLayoutResult.status === 'fulfilled' ? deploymentLayoutResult.value : null) setWarRoom(warRoomResult.status === 'fulfilled' ? warRoomResult.value : null) setProfessionalTaskExpansion(professionalTaskExpansionResult.status === 'fulfilled' ? professionalTaskExpansionResult.value : null) + setReceiptReadbackOwnerReview(receiptReadbackOwnerReviewResult.status === 'fulfilled' ? receiptReadbackOwnerReviewResult.value : null) setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null) setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null) setLiveReadModelGate(liveReadModelGateResult.status === 'fulfilled' ? liveReadModelGateResult.value : null) @@ -894,6 +899,7 @@ export function AutomationInventoryTab() { deploymentLayoutResult, warRoomResult, professionalTaskExpansionResult, + receiptReadbackOwnerReviewResult, proactiveOperationsResult, interactionLearningProofResult, liveReadModelGateResult, @@ -2235,6 +2241,19 @@ export function AutomationInventoryTab() { })) }, [visibleServiceHealthTargets]) + const visibleReceiptOwnerReviewGates = useMemo(() => { + if (!receiptReadbackOwnerReview) return [] + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...receiptReadbackOwnerReview.owner_review_gates] + .sort((a, b) => { + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.gate_id.localeCompare(b.gate_id) + }) + .slice(0, 6) + }, [receiptReadbackOwnerReview]) + const visibleSupplyChainDriftCandidates = useMemo(() => { if (!dependencySupplyChainDriftMonitor) return [] const severityPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record @@ -2265,7 +2284,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -2455,6 +2474,60 @@ export function AutomationInventoryTab() { active: dependencySupplyChainDriftMonitor.monitor_boundaries.production_write_allowed, }, ] + const receiptOwnerReviewOverall = receiptReadbackOwnerReview.program_status.overall_completion_percent + const receiptOwnerReviewSources = receiptReadbackOwnerReview.rollups.source_readback_count + const receiptOwnerReviewCadences = receiptReadbackOwnerReview.rollups.report_cadence_count + const receiptOwnerReviewGates = receiptReadbackOwnerReview.rollups.owner_review_gate_count + const receiptOwnerReviewChecks = receiptReadbackOwnerReview.rollups.receipt_readback_check_count + const receiptOwnerReviewApprovals = receiptReadbackOwnerReview.rollups.approval_required_count + const receiptOwnerReviewBlocked = receiptReadbackOwnerReview.rollups.blocked_runtime_action_count + const receiptOwnerReviewLiveWrites = ( + receiptReadbackOwnerReview.rollups.live_write_count + + receiptReadbackOwnerReview.rollups.telegram_send_count + + receiptReadbackOwnerReview.rollups.gateway_queue_write_count + + receiptReadbackOwnerReview.rollups.bot_api_call_count + + receiptReadbackOwnerReview.rollups.production_write_count + + receiptReadbackOwnerReview.rollups.secret_read_count + + receiptReadbackOwnerReview.rollups.paid_api_call_count + + receiptReadbackOwnerReview.rollups.host_write_count + + receiptReadbackOwnerReview.rollups.kubectl_action_count + ) + const receiptOwnerReviewBoundaryRows = [ + { + key: 'gateway', + label: t('receiptOwnerReview.boundaries.gateway'), + active: receiptReadbackOwnerReview.activation_boundaries.gateway_queue_write_enabled, + }, + { + key: 'telegram', + label: t('receiptOwnerReview.boundaries.telegram'), + active: receiptReadbackOwnerReview.activation_boundaries.telegram_send_enabled, + }, + { + key: 'botApi', + label: t('receiptOwnerReview.boundaries.botApi'), + active: receiptReadbackOwnerReview.activation_boundaries.bot_api_call_enabled, + }, + { + key: 'receipt', + label: t('receiptOwnerReview.boundaries.receipt'), + active: receiptReadbackOwnerReview.activation_boundaries.receipt_production_write_enabled + || receiptReadbackOwnerReview.activation_boundaries.report_receipt_write_enabled, + }, + { + key: 'production', + label: t('receiptOwnerReview.boundaries.production'), + active: receiptReadbackOwnerReview.activation_boundaries.production_write_enabled, + }, + { + key: 'secret', + label: t('receiptOwnerReview.boundaries.secret'), + active: receiptReadbackOwnerReview.activation_boundaries.secret_read_enabled + || receiptReadbackOwnerReview.activation_boundaries.paid_api_enabled + || receiptReadbackOwnerReview.activation_boundaries.host_write_enabled + || receiptReadbackOwnerReview.activation_boundaries.kubectl_action_enabled, + }, + ] const reportRuntimeOverall = reportRuntimeReadiness.program_status.overall_completion_percent const reportRuntimeLanes = reportRuntimeReadiness.rollups.runtime_lane_count const reportRuntimeReady = reportRuntimeReadiness.rollups.ready_contract_count @@ -4439,6 +4512,134 @@ export function AutomationInventoryTab() {
+ +
+
+
+
+ +
+
+ + {t('receiptOwnerReview.title')} + + + {t('receiptOwnerReview.subtitle', { + current: receiptReadbackOwnerReview.program_status.current_task_id, + next: receiptReadbackOwnerReview.program_status.next_task_id, + gates: receiptOwnerReviewGates, + blocked: receiptOwnerReviewBlocked, + })} + +
+
+
+ + + +
+
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('receiptOwnerReview.sections.ownerGates')} + {visibleReceiptOwnerReviewGates.map(gate => ( +
+
+ + {redactPublicText(gate.title)} + + +
+
+ {redactPublicText(gate.next_action)} +
+
+ + + + + +
+
+ ))} +
+ +
+
+ {t('receiptOwnerReview.sections.receiptPlan')} +
+ + + + +
+
+ {receiptReadbackOwnerReview.report_owner_review.cadences.map(cadence => ( + + ))} +
+
+ +
+ {t('receiptOwnerReview.sections.boundaries')} +
+ {receiptOwnerReviewBoundaryRows.map(row => ( + + ))} +
+
+ + + +
+
+
+
+
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 681359adb..6a8649b66 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -339,6 +339,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentReceiptReadbackOwnerReview() { + const res = await fetch(`${API_BASE_URL}/agents/agent-receipt-readback-owner-review`) + return handleResponse(res) + }, + async getAiAgentProactiveOperationsContract() { const res = await fetch(`${API_BASE_URL}/agents/agent-proactive-operations-contract`) return handleResponse(res) @@ -1895,6 +1900,157 @@ export interface AiAgentProfessionalTaskExpansionSnapshot { } } +export interface AiAgentReceiptReadbackOwnerReviewSnapshot { + schema_version: 'ai_agent_receipt_readback_owner_review_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-406B' + next_task_id: 'P2-407' + read_only_mode: true + runtime_authority: 'receipt_readback_owner_review_only_no_send_or_write' + status_note: string + } + source_refs: string[] + source_readbacks: Array<{ + readback_id: string + source_schema_version: string + source_ref: string + endpoint: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: string + evidence_status: string + key_readback: string + next_action: string + }> + rollups: { + source_readback_count: number + report_cadence_count: number + owner_review_gate_count: number + receipt_readback_check_count: number + drift_candidate_count: number + report_truth_blocker_count: number + approval_required_count: number + blocked_runtime_action_count: number + live_write_count: number + telegram_send_count: number + gateway_queue_write_count: number + bot_api_call_count: number + production_write_count: number + secret_read_count: number + paid_api_call_count: number + host_write_count: number + kubectl_action_count: number + } + report_owner_review: { + cadences: Array<{ + cadence_id: 'daily' | 'weekly' | 'monthly' + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + completion_percent: number + delivery_state: string + source_confidence: string + live_delivery_count: number + review_status: string + next_gate: string + }> + } + receipt_readback_plan: { + canonical_room: string + canonical_room_env: string + gateway_owner: string + arbiter: 'openclaw' + receipt_owner: 'hermes' + replay_owner: 'nemotron' + dry_run_receipt_only: boolean + owner_review_required_before_canary: boolean + canary_send_approved: boolean + receipt_production_write_enabled: boolean + readback_checks: Array<{ + check_id: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: string + evidence_refs: string[] + blocked_runtime_action: string + }> + } + owner_review_gates: Array<{ + gate_id: string + title: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + status: string + approval_required: true + required_owner_fields: string[] + acceptance_checks: string[] + rejection_reasons: string[] + blocked_runtime_actions: string[] + evidence_refs: string[] + next_action: string + }> + drift_monitor_owner_review: { + source_task_id: 'P2-004' + drift_candidate_count: number + action_required_candidate_count: number + stale_source_snapshot_count: number + external_lookup_allowed: false + package_upgrade_allowed: false + owner_actions: string[] + } + report_truth_owner_review: { + source_task_id: 'P2-403J' + all_zero_weekly_report_is_actionable_anomaly: true + all_zero_weekly_report_confidence: 'low_trust_actionable_anomaly' + zero_signal_finding_count: number + critical_finding_count: number + high_finding_count: number + telegram_report_send_allowed: false + cronjob_change_allowed: false + freshness_gate_implemented: false + source_confidence_gate_implemented: false + actionability_score_implemented: false + owner_actions: string[] + } + activation_boundaries: Record + telegram_policy: { + status: string + canonical_room: string + canonical_room_env: string + gateway_queue_write_allowed: false + direct_bot_api_allowed: false + telegram_send_allowed: false + receipt_write_allowed: false + owner_review_required?: boolean + canary_message_limit?: number + canary_approval_task?: string + } + agent_roles: Array<{ + agent_id: 'openclaw' | 'hermes' | 'nemotron' + role: string + specialty: string + autonomy_level: string + approval_gate: string + runtime_write_allowed: false + outputs: string[] + }> + operator_decision: { + status: 'owner_review_required_before_canary' + approval_packet_required: true + rollback_plan_required: true + mute_plan_required: true + required_fields: string[] + stop_conditions: string[] + next_decision_point: string + } + next_actions: Array<{ + task_id: string + priority: 'P0' | 'P1' | 'P2' | 'P3' + summary: string + gate: string + }> +} + export interface AiAgentProactiveOperationsContractSnapshot { schema_version: 'ai_agent_proactive_operations_contract_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 687bb4cf6..d99529167 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,24 @@ +## 2026-06-18|P2-406B Receipt Readback Owner Review 本地完成 + +**背景**:P2-004 已把依賴 / 供應鏈漂移收斂成只讀監控讀回;統帥要求每次推進都不能忘記目標與方向,因此本段把日報 / 週報 / 月報、Telegram receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成同一個 owner review surface,讓治理頁可以直接看到 AI Agent 分工、互審與仍被關閉的 runtime 邊界。 + +**完成內容**: +- 新增 `ai_agent_receipt_readback_owner_review_v1` schema 與 committed snapshot:`docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json`、`docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json`。 +- 新增 `apps/api/src/services/ai_agent_receipt_readback_owner_review.py` 與 `GET /api/v1/agents/agent-receipt-readback-owner-review`。 +- 新增 API regression tests:`apps/api/tests/test_ai_agent_receipt_readback_owner_review.py`、`apps/api/tests/test_ai_agent_receipt_readback_owner_review_api.py`。 +- `apps/web/src/lib/api-client.ts` 新增 `getAiAgentReceiptReadbackOwnerReview()` 與 snapshot type。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-406B 卡片,顯示 6 個 source readback、3 個報告節奏、6 個 owner review gate、8 個 receipt check、Telegram canonical room、OpenClaw / Hermes / Nemotron 分工、truth blockers、drift candidates 與 false boundary。 +- `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md` 與 MASTER §8 已同步 P2-406B 完成狀態、P2-407 下一步與禁止邊界。 + +**本地驗證**: +- `python3 -m json.tool docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json`、`docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json`:通過。 +- `python3 -m json.tool apps/web/messages/zh-TW.json`、`apps/web/messages/en.json`:通過。 +- `python3 -m py_compile apps/api/src/services/ai_agent_receipt_readback_owner_review.py`:通過。 +- `DATABASE_URL=postgresql://test:test@localhost:5432/test pytest apps/api/tests/test_ai_agent_receipt_readback_owner_review.py apps/api/tests/test_ai_agent_receipt_readback_owner_review_api.py`:`9 passed`。 +- `pnpm --filter @awoooi/web typecheck`:本 worktree 缺 `node_modules`,`tsc: command not found`;本輪未安裝套件、未寫 lockfile。 + +**邊界**:本段未發 Telegram、未寫 Gateway queue、未呼叫 Bot API、未寫 receipt production target、未啟動 AI analysis runtime、未啟動中低風險 auto worker、未讀 secret、未呼叫 paid API、未 host write、未 kubectl、未 production write、未替換 OpenClaw。P2-406B 是只讀 owner review;下一步是 P2-407 no-write 報表分析。 + ## 2026-06-18|P0-A 資安資產控制總帳本地收斂 **背景**:使用者要求 IwoooS 必須完整盤點所有主機、服務、套件、工具、網站前後台、Nginx、網路、Wazuh、Kali、告警監控、備份復原、供應鏈、AI provider 與跨專案 runtime,並優先處理會造成即時資安危害的配置控管。此段先把範圍收成 repo-side 只讀總帳與前台可視卡,避免再用零散長文或單一事故卡追蹤。 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 afe852099..e471f6dbe 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -13,13 +13,13 @@ | 項目 | 目前完成度 | 本次判讀 | 下一個有效動作 | |---|---:|---|---| | 本工作清單細化 | 100% | 已把所有工作流拆成 P0 / P1 / P2 / P3 | 同步 LOGBOOK 與 MASTER §8 | -| AgentOps 治理與可觀測基礎 | 68% | 已有 schema / snapshot / API / UI / gate,但 runtime 真正執行仍低 | P2-406B 起繼續 owner receipt readback | +| AgentOps 治理與可觀測基礎 | 70% | 已有 schema / snapshot / API / UI / gate;P2-406B 已把報表、Telegram receipt 與 P2-004 drift monitor 串成只讀 owner review,但 runtime 真正執行仍低 | P2-407 no-write 報表分析 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | 目前是只讀 layout 與治理頁可視化,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | | Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 35% | 互動證據、War Room 與 readback gate 已齊;Event Bus、RAG writeback、PlayBook trust 寫入仍未開 | 先做 no-write event stream,再做 Owner-approved writeback | -| 日報 / 週報 / 月報 | 可視化 100%,實發 0% | 報表批准包與治理頁已可見;Telegram 實發、receipt、AI analysis runtime 仍為 0 | P2-406B / P2-407 先補 receipt readback 與 no-send analyst | -| Telegram Bot / TG 群組 | 契約 40%,實發 0% | no-send preview、dry-run、owner review gate 有;live send / Bot API / Gateway queue 未批准 | 先完成 canary receipt readback,收到合格 owner approval 後才單訊息 canary | +| 日報 / 週報 / 月報 | 可視化 100%,實發 0% | 報表批准包、P2-406B owner review 與治理頁已可見;Telegram 實發、receipt production write、AI analysis runtime 仍為 0 | P2-407 先做 no-write analyst | +| Telegram Bot / TG 群組 | 契約 44%,實發 0% | no-send preview、dry-run、owner review gate 與 P2-406B receipt readback owner review 已有;live send / Bot API / Gateway queue 未批准 | 收到合格 owner approval 後才評估 P2-406C one-message canary | | 中低風險自動化 | 30% | 政策方向已明確,但實際 auto worker 未開 | 建立 low / medium whitelist、dry-run verifier、rollback proof | -| 高風險審核 | 65% | Owner Gate 與拒收規則已多層化;仍缺真實 owner response accepted ledger | P2-145 / P2-406B 類 gate 繼續只讀讀回 | +| 高風險審核 | 68% | Owner Gate、拒收規則與 P2-406B receipt owner review 已多層化;仍缺真實 owner response accepted ledger | P2-145 / P2-406C 類 gate 繼續只讀讀回 | | 市場主流 Agent 追蹤 | 55% | 已有市場治理頁與 weekly watch;需擴充成固定外部來源評分與回放 | P2-412 週期性 market watch + scorecard | | 版本生命週期自動化 | 45% | repo-only snapshot 與採用批准包已完成;安裝、升級、PR creation、host update 仍未開 | P2-413 版本情報與 no-write upgrade proposal | @@ -59,7 +59,7 @@ | 順序 | ID | 優先級 | 工作項目 | 主責 Agent | 狀態 | 驗收條件 | |---:|---|---|---|---|---|---| -| 1 | P2-406B | P0 | Receipt readback owner review production verification | Hermes + Telegram | 立即推進,只讀 | production API / browser 顯示 owner receipt readback;send / queue / write 維持 0 | +| 1 | P2-406B | P0 | Receipt readback owner review production verification | Hermes + Telegram | 本地完成,待本輪正式驗證 | production API / browser 顯示 owner receipt readback;send / queue / write 維持 0 | | 2 | P2-406C | P0 | Telegram canary route lock 與 one-message approval intake | OpenClaw + Telegram | 待 P2-406B | 只接受遮罩後 owner approval;缺欄位拒收 | | 3 | P2-406D | P0 | Telegram Gateway envelope dry-run receipt ledger | Hermes + Telegram | 待辦 | 產生 no-send envelope、dedup、receipt expectation;Gateway write 0 | | 4 | P2-406E | P0 | Failure-only Telegram digest route | SRE + Telegram | 待辦 | success quiet、failure action-required、rate limit、mute / rollback 可讀 | @@ -148,12 +148,13 @@ |---|---:|---|---| | Agent 市場治理 | 72% | 進行中 | `agent_market_governance_snapshot_v1`、API、UI 分頁、每週觀察流程 | | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | -| 工具 / 服務 / 套件 AI 自動化 | 94% | P2-004 依賴 / 供應鏈漂移監控讀回已完成;下一主線是 P2-406B,把 drift monitor 與日週月報 / Telegram receipt owner review 串接成只讀審核視圖 | 新增 `dependency_supply_chain_drift_monitor_v1` schema / snapshot / API / tests / governance UI 卡片;9 個 drift candidate、7 個 monitor check、9 個 owner action、20 個 blocked operation;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | +| 工具 / 服務 / 套件 AI 自動化 | 96% | P2-004 依賴 / 供應鏈漂移監控讀回已完成,且已納入 P2-406B receipt readback owner review;下一主線是 P2-407 no-write 報表分析 | 新增 `dependency_supply_chain_drift_monitor_v1` 與 `ai_agent_receipt_readback_owner_review_v1` schema / snapshot / API / tests / governance UI 卡片;9 個 drift candidate、7 個 monitor check、6 個 source readback、6 個 owner gate、8 個 receipt check、17 個 runtime false boundary;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | | OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 到 P2-144 已完成只讀證據面、runtime / report / result-capture gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold / readback gate、release verifier preflight / owner review packet、release decision hold / readback、release decision next handoff、release decision input prep、12-Agent War Room、owner response 預檢與 owner response 回讀;P2-141 基線與 S4.9 owner release packet 補強皆已正式驗證,P2-142 12-Agent War Room 已完成 production readback 與 desktop / mobile smoke,P2-143 owner response 預檢已完成 production readback 與 in-app browser smoke,P2-144 owner response 回讀已完成 production API readback 與 desktop / mobile smoke。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、live query、runtime score、result capture write、Telegram 實發、delivery receipt E2E、live report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、`docs/evaluations/ai_agent_result_capture_release_decision_owner_response_readback_2026-06-14.json`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個回覆讀回 lane、18 個 owner 必填欄位、6 個 readback validation check、6 個 rejection guard、5 個 operator action、等待外部回覆 `5`、未收件 lane `5`、正式寫入 / 發送 `0`;P2-142 feature commit `5de4b3f3`、deploy marker `1a2c9e36`、Gitea CD run `4232` success、production API readback、desktop / mobile in-app browser smoke;P2-143 feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、production API readback、desktop / mobile in-app browser smoke;MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | | 12-Agent War Room 編組 | 72% | 12 個邏輯工位與分批派工規則已正式部署;OpenClaw / Hermes / NemoTron / SRE / Security / DevOps / Data/DR / Supply Chain / Product/UI / QA / Market / Telegram 共 12 份只讀審查已回收;schema / committed snapshot / API / tests / governance UI 區塊 / production API readback / desktop + mobile in-app browser smoke 已完成;runtime writer、Telegram send、Bot API、production write 仍未批准 | `ai_agent_12_agent_war_room_v1`、`docs/evaluations/ai_agent_12_agent_war_room_2026-06-14.json`、`GET /api/v1/agents/agent-12-agent-war-room`、feature commit `5de4b3f3`、deploy marker `1a2c9e36`、Gitea CD run `4232` success、`/zh-TW/governance?tab=automation-inventory`、12 份 Codex sub-agent 只讀回饋 | | AI Agent 專業任務擴展與 Telegram Runtime Bridge | 99% | P2-405F 已完成只讀契約、API service guard、治理頁 P2-405F owner review gate、9 個 owner 必填欄位、9 個 acceptance check、8 個 rejection reason、6 個 reviewer action、8 個 receipt readback check,且 redaction / i18n production closeout 已完成;P2-405E 已正式驗證 dry-run delivery rehearsal;P2-406A 已把 P2-111 日報 / 週報 / 月報實發批准包、AwoooI SRE 戰情室 route、TG Bot / Gateway / receipt / AI analysis 邊界拉到治理頁前段主看板;前端公開 payload sanitizer 已改成純繁中安全標籤,避免 `redacted_*` 替換後仍殘留工作視窗 / raw / private / authorization 類敏感 key;redaction gate lookup 已正規化 `已遮罩機密欄位 -> redacted_secret_value`,正式頁 console `MISSING_MESSAGE=0`;24 類專業任務、8 個領域、5 段 Telegram bridge、6 種訊息類型、MCP/RAG stack、日報 / 週報 / 月報 / action-required 報告契約已固定;owner review received / accepted、Telegram 實發、Gateway queue、Bot API、delivery receipt production write、secret read、paid API、host write、kubectl action 仍全部關閉 | `ai_agent_professional_task_expansion_v1`、`docs/evaluations/ai_agent_professional_task_expansion_2026-06-18_1430_p2_405f.json`、`docs/evaluations/ai_agent_professional_task_expansion_2026-06-18_1200_p2_405e.json`、`GET /api/v1/agents/agent-professional-task-expansion`、`GET /api/v1/agents/agent-report-live-delivery-approval-package`、`/zh-TW/governance?tab=automation-inventory`、feature commit `2500496f`、P2-405E deploy marker `f5be4cb8`、P2-405F redaction / i18n fix commit `795ed91f`、final deploy marker `e9cf0c35`、Gitea code-review `3098` success、final CD `3103` success、production API readback current `P2-405F` / next `P2-406B` / completion `99%`、desktop `1440x1100` / mobile `390x844` browser smoke `MISSING_MESSAGE=0`、console / page / HTTP error `0`、水平溢位 `0`、危險操作控制 `0`、P2-405F local API regression `26 passed`、Web typecheck、Web production build、公開 sanitizer 輸出掃描、`docs/ai/AI_AGENT_PROFESSIONAL_TASK_EXPANSION_2026-06-15.md`、需批准任務 `19`、no-send preview `6`、dedup key `6`、receipt expectation `6`、canary package `1`、canary send approval packet `1`、delivery gate `1`、dry-run rehearsal `1`、owner review gate `1`、P2-111 delivery approval packet `5`、route gate `4`、no-send receipt `4`、owner review received / accepted `0 / 0`、live delivery approved / attempt allowed `0 / 0`、preview / canary / delivery / rehearsal / owner review live write `0`;下一步 P2-406B receipt readback owner review,仍不得實發 | +| P2-406B Receipt readback owner review | 100%(本地) | 已把日報 / 週報 / 月報、P2-405F owner gate、Telegram receipt approval package、P2-004 drift monitor 與 P2-403J 報表真相整合成只讀 owner review;正式驗證待本輪推版 | `ai_agent_receipt_readback_owner_review_v1` schema、`docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json`、`GET /api/v1/agents/agent-receipt-readback-owner-review`、governance `automation-inventory` P2-406B 卡片;6 個 source readback、3 個報告節奏、6 個 owner gate、8 個 receipt check、9 個 drift candidate、5 個 report truth blocker、17 個 runtime false boundary;本地 API/service regression `9 passed`;Telegram send / Gateway queue / Bot API / receipt production write / production write / secret read / paid API / host write / kubectl action 全部 `0 / false` | | Owner response 預檢與拒收邊界 | 100% | P2-143 已完成正式部署與 production readback;承接 P2-141 input prep 與 P2-142 War Room,只建立 owner / verifier / rollback / maintenance / live-apply 五類外部回覆的 intake 預檢、必填欄位與拒收規則;正式 owner response 尚未收到、未接受、未寫入 | `ai_agent_result_capture_release_decision_owner_response_preflight_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-preflight`、feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、5 個 response intake lane、18 個 required owner field、6 個 validation check、6 個 rejection guard、5 個 operator action;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | Owner response 回讀狀態 | 100% | P2-144 已完成正式部署與 production readback;承接 P2-143 preflight,只讀回五類外部回覆仍未收到、未接受、未拒絕、未保存 | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個 response readback lane、18 個 required owner field、6 個 readback validation check、6 個 readback rejection guard、5 個 operator action、waiting external response `5`、no external response received `5`;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | @@ -1658,8 +1659,8 @@ UI: ## 13. 立即執行順序 1. P2-004:依賴 / 供應鏈漂移監控讀回已完成;維持只讀觀察與批准包邊界,下一輪不得把完成誤讀成可外部掃描或自動升級。 -2. P2-406B:AI Agent Telegram receipt readback owner review / production verification,保持只讀;目標是確認日報 / 週報 / 月報、P2-004 drift monitor 與 Telegram canary receipt readback gate 在正式 API / governance UI 可讀,且 Telegram send、Gateway queue、Bot API、receipt production write 全部仍為 `0`。只有收到合格 owner review、receipt readback owner、mute / rollback plan、停止條件與可驗證證據後,才可進一步評估 canary live delivery;未批准前仍不得實發。 -3. P2-407:AI 報表自動分析 no-write runtime;讓 OpenClaw / Hermes / NemoTron 讀取日報 / 週報 / 月報與 P2-004 drift monitor 後產生建議與風險分級,但只寫 committed snapshot / 草稿,不寫 production。 +2. P2-406B:AI Agent Telegram receipt readback owner review / production verification 已本地完成並等待本輪正式驗證;它只確認日報 / 週報 / 月報、P2-004 drift monitor 與 Telegram canary receipt readback gate 在 API / governance UI 可讀,且 Telegram send、Gateway queue、Bot API、receipt production write 全部仍為 `0`。只有收到合格 owner review、receipt readback owner、mute / rollback plan、停止條件與可驗證證據後,才可進一步評估 canary live delivery;未批准前仍不得實發。 +3. P2-407:AI 報表自動分析 no-write runtime 是下一個有效動作;讓 OpenClaw / Hermes / NemoTron 讀取日報 / 週報 / 月報與 P2-004 drift monitor 後產生建議與風險分級,但只寫 committed snapshot / 草稿,不寫 production。 4. P2-408:中 / 低風險自動處理白名單與高風險審核分流;低風險先 dry-run,中風險需 verifier,高風險進 Owner Review Queue。 5. P2-411:Agent Event Bus / handoff protocol / RAG memory 的 no-write 基線;先讓 Agent 的觀察、判斷、交接、拒收與學習提案可追蹤。 6. P2-412:市場主流 AI Agent 定期評估;刷新 NVIDIA Nemotron、NeMo Agent Toolkit、OpenAI Agents、LangGraph、A2A、MCP、OpenTelemetry GenAI 等 primary sources,形成 scorecard 與候選整合提案。 diff --git a/docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json b/docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json new file mode 100644 index 000000000..88ab1b08d --- /dev/null +++ b/docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json @@ -0,0 +1,419 @@ +{ + "schema_version": "ai_agent_receipt_readback_owner_review_v1", + "generated_at": "2026-06-18T23:40:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-406B", + "next_task_id": "P2-407", + "read_only_mode": true, + "runtime_authority": "receipt_readback_owner_review_only_no_send_or_write", + "status_note": "P2-406B 將日報 / 週報 / 月報、Telegram receipt owner review、P2-004 供應鏈漂移與 P2-403J 報表真相收斂成只讀 owner review。此快照只允許 production readback 與 governance UI 呈現,不啟用任何發送、queue、receipt write、AI runtime 或 production write。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_report_status_board_2026-06-13.json", + "docs/evaluations/ai_agent_report_live_delivery_approval_package_2026-06-13.json", + "docs/evaluations/ai_agent_telegram_receipt_approval_package_2026-06-11.json", + "docs/evaluations/ai_agent_professional_task_expansion_2026-06-18_1430_p2_405f.json", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/ai_agent_report_truth_actionability_review_2026-06-12.json" + ], + "source_readbacks": [ + { + "readback_id": "report_status_board_readback", + "source_schema_version": "ai_agent_report_status_board_v1", + "source_ref": "docs/evaluations/ai_agent_report_status_board_2026-06-13.json", + "endpoint": "GET /api/v1/agents/agent-report-status-board", + "owner_agent": "openclaw", + "status": "readback_ready", + "evidence_status": "日報 / 週報 / 月報與 Agent 工作量已在 committed snapshot 可讀,live delivery 與 live optimization 仍為 0。", + "key_readback": "3 個報告、3 個 Agent 狀態、3 張圖表、工作量 91、待審核 12。", + "next_action": "納入 P2-406B owner review,等待 receipt 與 canary gate 檢查完成。" + }, + { + "readback_id": "report_delivery_approval_readback", + "source_schema_version": "ai_agent_report_live_delivery_approval_package_v1", + "source_ref": "docs/evaluations/ai_agent_report_live_delivery_approval_package_2026-06-13.json", + "endpoint": "GET /api/v1/agents/agent-report-live-delivery-approval-package", + "owner_agent": "hermes", + "status": "owner_review_ready", + "evidence_status": "日報 / 週報 / 月報實發批准包、route lock、redaction 與 dry-run receipt 可讀。", + "key_readback": "scheduler、Gateway queue、Telegram send、Bot API、receipt write、AI analysis、auto optimization 全部為 false / 0。", + "next_action": "維持 no-send,只把可審核欄位放入 owner review。" + }, + { + "readback_id": "telegram_receipt_approval_readback", + "source_schema_version": "ai_agent_telegram_receipt_approval_package_v1", + "source_ref": "docs/evaluations/ai_agent_telegram_receipt_approval_package_2026-06-11.json", + "endpoint": "GET /api/v1/agents/agent-telegram-receipt-approval-package", + "owner_agent": "hermes", + "status": "receipt_gate_ready", + "evidence_status": "4 個 receipt gate、3 條 receipt lane、live receipt total 0。", + "key_readback": "gateway_queue_write_allowed=false、telegram_send_allowed=false、direct_bot_api_allowed=false。", + "next_action": "P2-406B 僅讀取 receipt gate,不寫 Gateway queue。" + }, + { + "readback_id": "professional_task_expansion_readback", + "source_schema_version": "ai_agent_professional_task_expansion_v1", + "source_ref": "docs/evaluations/ai_agent_professional_task_expansion_2026-06-18_1430_p2_405f.json", + "endpoint": "GET /api/v1/agents/agent-professional-task-expansion", + "owner_agent": "nemotron", + "status": "owner_review_baseline_ready", + "evidence_status": "P2-405F 已固定 9 個 owner 必填欄位、9 個 acceptance check、8 個 rejection reason、6 個 reviewer action 與 8 個 receipt readback check。", + "key_readback": "receipt owner=hermes、arbiter=openclaw、replay owner=nemotron;live owner review accepted=0。", + "next_action": "把 owner gate 基線收斂成 P2-406B receipt readback owner review。" + }, + { + "readback_id": "dependency_supply_chain_drift_readback", + "source_schema_version": "dependency_supply_chain_drift_monitor_v1", + "source_ref": "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "endpoint": "GET /api/v1/agents/dependency-supply-chain-drift-monitor", + "owner_agent": "openclaw", + "status": "drift_owner_review_ready", + "evidence_status": "9 個 drift candidate、9 個 owner action、20 個 blocked operation 已可讀。", + "key_readback": "外部 CVE / license / registry / Agent market lookup、package upgrade、lockfile write、Docker build、Telegram send 全部未啟用。", + "next_action": "把漂移候選項納入日週月報與 canary 前 owner review。" + }, + { + "readback_id": "report_truth_actionability_readback", + "source_schema_version": "ai_agent_report_truth_actionability_review_v1", + "source_ref": "docs/evaluations/ai_agent_report_truth_actionability_review_2026-06-12.json", + "endpoint": "GET /api/v1/agents/agent-report-truth-actionability-review", + "owner_agent": "openclaw", + "status": "truth_gate_action_required", + "evidence_status": "全 0 週報已標示為低可信可處置異常,不能再視為綠燈。", + "key_readback": "zero-signal findings 5、critical 1、high 3;freshness gate、source confidence gate、actionability score 尚未啟用。", + "next_action": "P2-407 需先做 no-write analyst,再考慮任何實發。" + } + ], + "rollups": { + "source_readback_count": 6, + "report_cadence_count": 3, + "owner_review_gate_count": 6, + "receipt_readback_check_count": 8, + "drift_candidate_count": 9, + "report_truth_blocker_count": 5, + "approval_required_count": 6, + "blocked_runtime_action_count": 17, + "live_write_count": 0, + "telegram_send_count": 0, + "gateway_queue_write_count": 0, + "bot_api_call_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "paid_api_call_count": 0, + "host_write_count": 0, + "kubectl_action_count": 0 + }, + "report_owner_review": { + "cadences": [ + { + "cadence_id": "daily", + "display_name": "AI Agent 日報", + "owner_agent": "hermes", + "completion_percent": 100, + "delivery_state": "draft_only", + "source_confidence": "visible_contract_ready", + "live_delivery_count": 0, + "review_status": "owner_review_required_before_send", + "next_gate": "daily_report_receipt_owner_review" + }, + { + "cadence_id": "weekly", + "display_name": "AI Agent 週報", + "owner_agent": "openclaw", + "completion_percent": 100, + "delivery_state": "draft_only", + "source_confidence": "low_trust_actionable_anomaly", + "live_delivery_count": 0, + "review_status": "truth_gate_action_required", + "next_gate": "weekly_report_truth_owner_review" + }, + { + "cadence_id": "monthly", + "display_name": "AI Agent 月報", + "owner_agent": "nemotron", + "completion_percent": 100, + "delivery_state": "draft_only", + "source_confidence": "visible_contract_ready", + "live_delivery_count": 0, + "review_status": "owner_review_required_before_send", + "next_gate": "monthly_report_owner_review" + } + ] + }, + "receipt_readback_plan": { + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_owner": "telegram_ops", + "arbiter": "openclaw", + "receipt_owner": "hermes", + "replay_owner": "nemotron", + "dry_run_receipt_only": true, + "owner_review_required_before_canary": true, + "canary_send_approved": false, + "receipt_production_write_enabled": false, + "readback_checks": [ + { + "check_id": "canonical_room_lock_check", + "owner_agent": "openclaw", + "status": "ready_no_send", + "evidence_refs": ["GET /api/v1/agents/agent-report-truth-actionability-review"], + "blocked_runtime_action": "route_change" + }, + { + "check_id": "gateway_queue_write_zero_check", + "owner_agent": "hermes", + "status": "ready_no_write", + "evidence_refs": ["GET /api/v1/agents/agent-report-live-delivery-approval-package"], + "blocked_runtime_action": "gateway_queue_write" + }, + { + "check_id": "telegram_send_zero_check", + "owner_agent": "hermes", + "status": "ready_no_send", + "evidence_refs": ["GET /api/v1/agents/agent-telegram-receipt-approval-package"], + "blocked_runtime_action": "telegram_send" + }, + { + "check_id": "bot_api_call_zero_check", + "owner_agent": "hermes", + "status": "ready_no_call", + "evidence_refs": ["GET /api/v1/agents/agent-professional-task-expansion"], + "blocked_runtime_action": "bot_api_call" + }, + { + "check_id": "receipt_production_write_zero_check", + "owner_agent": "nemotron", + "status": "ready_no_write", + "evidence_refs": ["docs/evaluations/ai_agent_professional_task_expansion_2026-06-18_1430_p2_405f.json"], + "blocked_runtime_action": "receipt_production_write" + }, + { + "check_id": "report_truth_low_trust_check", + "owner_agent": "openclaw", + "status": "action_required_before_send", + "evidence_refs": ["GET /api/v1/agents/agent-report-truth-actionability-review"], + "blocked_runtime_action": "report_send" + }, + { + "check_id": "dependency_drift_gate_check", + "owner_agent": "openclaw", + "status": "action_required_before_upgrade", + "evidence_refs": ["GET /api/v1/agents/dependency-supply-chain-drift-monitor"], + "blocked_runtime_action": "package_upgrade" + }, + { + "check_id": "redaction_no_sensitive_display_check", + "owner_agent": "nemotron", + "status": "ready_for_public_governance_ui", + "evidence_refs": ["apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx"], + "blocked_runtime_action": "sensitive_detail_display" + } + ] + }, + "owner_review_gates": [ + { + "gate_id": "daily_report_receipt_owner_review", + "title": "日報 receipt owner review", + "owner_agent": "hermes", + "risk_tier": "medium", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["target_room", "cadence", "dedup_key", "preview_hash", "receipt_expectation", "failure_mode", "mute_window", "rollback_plan", "stop_condition"], + "acceptance_checks": ["目標房間為 AwoooI SRE 戰情室", "payload 已脫敏", "dedup key 唯一", "receipt expectation 可讀", "live send 仍為 false"], + "rejection_reasons": ["缺目標房間", "缺 rollback plan", "缺 stop condition", "出現 live send 權限"], + "blocked_runtime_actions": ["scheduler_enabled", "gateway_queue_write_enabled", "telegram_send_enabled", "report_receipt_write_enabled"], + "evidence_refs": ["GET /api/v1/agents/agent-report-status-board", "GET /api/v1/agents/agent-report-live-delivery-approval-package"], + "next_action": "等待 owner 填齊必填欄位後,再進 P2-407 no-write analyst。" + }, + { + "gate_id": "weekly_report_truth_owner_review", + "title": "週報 truth owner review", + "owner_agent": "openclaw", + "risk_tier": "high", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["truth_source", "zero_signal_explanation", "freshness_gate", "source_confidence", "actionability_score", "owner_decision", "mute_window", "rollback_plan", "stop_condition"], + "acceptance_checks": ["全 0 週報不得被標成綠燈", "低可信異常需保留", "freshness gate 設計需先審核", "Telegram 實發仍為 false"], + "rejection_reasons": ["把全 0 視為通過", "缺 source confidence", "缺 actionability score", "試圖啟用 CronJob"], + "blocked_runtime_actions": ["telegram_report_send_allowed", "cronjob_change_allowed", "ai_analysis_run_enabled", "production_optimization_write_enabled"], + "evidence_refs": ["GET /api/v1/agents/agent-report-truth-actionability-review", "docs/LOGBOOK.md"], + "next_action": "P2-407 先產生 no-write 報表分析建議,不實發。" + }, + { + "gate_id": "monthly_report_owner_review", + "title": "月報 owner review", + "owner_agent": "nemotron", + "risk_tier": "medium", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["cadence", "source_refs", "trend_window", "agent_workload_summary", "drift_summary", "recommendation_scope", "approval_lane", "rollback_plan", "stop_condition"], + "acceptance_checks": ["月報只做治理彙整", "不得觸發 production optimization", "不得使用付費 API", "不得寫 KM 或 PlayBook trust"], + "rejection_reasons": ["缺趨勢窗口", "缺資料來源", "包含自動優化命令", "包含未批准外部查詢"], + "blocked_runtime_actions": ["production_optimization_write_enabled", "paid_api_enabled", "external_lookup_enabled", "learning_writeback"], + "evidence_refs": ["GET /api/v1/agents/agent-report-status-board", "GET /api/v1/agents/dependency-supply-chain-drift-monitor"], + "next_action": "收斂成月報草案與 owner decision packet。" + }, + { + "gate_id": "telegram_canary_receipt_owner_review", + "title": "Telegram canary receipt owner review", + "owner_agent": "hermes", + "risk_tier": "critical", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["owner_identity", "target_room", "message_type", "preview_hash", "dedup_key", "send_window", "receipt_expectation", "mute_window", "rollback_plan", "stop_condition"], + "acceptance_checks": ["owner review received=true 才能進下一關", "owner review accepted=true 才能評估 canary", "canary send approved 必須獨立為 true", "Bot API 仍為 false"], + "rejection_reasons": ["缺 owner identity", "缺 send window", "缺 receipt expectation", "試圖直接呼叫 Bot API"], + "blocked_runtime_actions": ["gateway_queue_write_enabled", "bot_api_call_enabled", "telegram_send_enabled", "receipt_production_write_enabled"], + "evidence_refs": ["GET /api/v1/agents/agent-professional-task-expansion", "GET /api/v1/agents/agent-telegram-receipt-approval-package"], + "next_action": "P2-406C 才能評估 one-message approval intake;本輪仍不送。" + }, + { + "gate_id": "supply_chain_drift_owner_review", + "title": "供應鏈漂移 owner review", + "owner_agent": "openclaw", + "risk_tier": "high", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["candidate_id", "risk_tier", "evidence_refs", "affected_surface", "proposed_action", "rollback_plan", "test_plan", "maintenance_window", "stop_condition"], + "acceptance_checks": ["不得自動升級", "不得寫 lockfile", "不得查未批准外部來源", "不得 docker build"], + "rejection_reasons": ["缺 evidence refs", "缺 rollback plan", "要求 package upgrade", "要求 registry lookup"], + "blocked_runtime_actions": ["external_lookup_enabled", "package_upgrade", "lockfile_write", "docker_build"], + "evidence_refs": ["GET /api/v1/agents/dependency-supply-chain-drift-monitor"], + "next_action": "產生 owner packet;批准前維持 read-only drift monitor。" + }, + { + "gate_id": "report_truth_data_chain_owner_review", + "title": "報表資料鏈 owner review", + "owner_agent": "openclaw", + "risk_tier": "high", + "status": "owner_review_required", + "approval_required": true, + "required_owner_fields": ["data_source", "freshness_threshold", "source_confidence", "failure_policy", "actionability_score", "owner_decision", "test_plan", "rollback_plan", "stop_condition"], + "acceptance_checks": ["資料鏈失敗不可轉成 0", "failure policy 要可讀", "actionability score 需先 no-write 驗證", "中低風險自動化仍為 false"], + "rejection_reasons": ["資料鏈失敗仍輸出綠燈", "缺 failure policy", "缺 no-write verifier", "啟用中低風險自動化"], + "blocked_runtime_actions": ["medium_low_auto_execution_enabled", "ai_analysis_run_enabled", "production_write_enabled", "report_receipt_write_enabled"], + "evidence_refs": ["GET /api/v1/agents/agent-report-truth-actionability-review", "GET /api/v1/agents/agent-report-status-board"], + "next_action": "P2-407 將此 gate 轉成 no-write analyst 檢查清單。" + } + ], + "drift_monitor_owner_review": { + "source_task_id": "P2-004", + "drift_candidate_count": 9, + "action_required_candidate_count": 9, + "stale_source_snapshot_count": 5, + "external_lookup_allowed": false, + "package_upgrade_allowed": false, + "owner_actions": [ + "prepare_python_manifest_authority_packet", + "prepare_js_high_impact_upgrade_packet", + "prepare_docker_digest_pin_packet", + "prepare_external_source_activation_packet" + ] + }, + "report_truth_owner_review": { + "source_task_id": "P2-403J", + "all_zero_weekly_report_is_actionable_anomaly": true, + "all_zero_weekly_report_confidence": "low_trust_actionable_anomaly", + "zero_signal_finding_count": 5, + "critical_finding_count": 1, + "high_finding_count": 3, + "telegram_report_send_allowed": false, + "cronjob_change_allowed": false, + "freshness_gate_implemented": false, + "source_confidence_gate_implemented": false, + "actionability_score_implemented": false, + "owner_actions": [ + "週報全 0 需維持 action-required", + "日週月報需共用 freshness gate", + "正式 Telegram 報表需先完成 no-write analyst", + "中低風險自動處理需另走批准包" + ] + }, + "activation_boundaries": { + "owner_review_required_before_canary": true, + "read_only_review_allowed": true, + "scheduler_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "delivery_receipt_write_enabled": false, + "report_receipt_write_enabled": false, + "receipt_production_write_enabled": false, + "ai_analysis_run_enabled": false, + "medium_low_auto_execution_enabled": false, + "production_optimization_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false, + "external_lookup_enabled": false + }, + "telegram_policy": { + "status": "owner_review_only_no_send", + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_queue_write_allowed": false, + "direct_bot_api_allowed": false, + "telegram_send_allowed": false, + "receipt_write_allowed": false, + "owner_review_required": true, + "canary_message_limit": 1, + "canary_approval_task": "P2-406C" + }, + "agent_roles": [ + { + "agent_id": "openclaw", + "role": "仲裁者", + "specialty": "風險裁決、報表真相、供應鏈漂移 gate、OpenClaw 替代決策包守門", + "autonomy_level": "L2_review_only", + "approval_gate": "human_owner_required_for_high_risk_or_replacement", + "runtime_write_allowed": false, + "outputs": ["owner decision packet", "truth gate", "risk adjudication"] + }, + { + "agent_id": "hermes", + "role": "執行協調者", + "specialty": "Telegram receipt、Gateway queue 草案、日報 / 週報 / 月報派送前檢查", + "autonomy_level": "L2_no_send_preview", + "approval_gate": "owner_review_required_before_canary", + "runtime_write_allowed": false, + "outputs": ["receipt readback checklist", "no-send delivery packet", "failure digest draft"] + }, + { + "agent_id": "nemotron", + "role": "研究與 replay 執行者", + "specialty": "no-write replay、市場 / 版本 / 月報分析、MCP/RAG 候選整理", + "autonomy_level": "L2_no_write_analysis", + "approval_gate": "OpenClaw arbitration plus owner review", + "runtime_write_allowed": false, + "outputs": ["analysis draft", "replay summary", "integration proposal"] + } + ], + "operator_decision": { + "status": "owner_review_required_before_canary", + "approval_packet_required": true, + "rollback_plan_required": true, + "mute_plan_required": true, + "required_fields": ["owner_identity", "target_room", "message_type", "preview_hash", "dedup_key", "receipt_expectation", "send_window", "rollback_plan", "stop_condition"], + "stop_conditions": ["任何 queue write 非 0", "任何 Telegram send 非 0", "任何 Bot API call 非 0", "任何 receipt production write 非 0", "週報全 0 被標成綠燈", "payload 未完成脫敏"], + "next_decision_point": "P2-407 先做 no-write analyst,通過後才評估 P2-406C one-message approval intake。" + }, + "next_actions": [ + { + "task_id": "P2-407", + "priority": "P2", + "summary": "AI Agent no-write 報表分析與 actionability score dry-run", + "gate": "不得實發、不得自動優化、不得寫 production" + }, + { + "task_id": "P2-406C", + "priority": "P2", + "summary": "Telegram canary route lock 與 one-message approval intake", + "gate": "需 owner review accepted、preview hash、mute / rollback / stop condition 全齊" + } + ] +} diff --git a/docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json b/docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json new file mode 100644 index 000000000..886c088bc --- /dev/null +++ b/docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json @@ -0,0 +1,458 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-agent-receipt-readback-owner-review-v1", + "title": "AWOOOI AI Agent receipt readback owner review v1", + "description": "P2-406B 將日報 / 週報 / 月報、Telegram receipt owner review、P2-004 供應鏈漂移與 P2-403J 報表真相收斂成只讀 owner review。此 schema 不授權排程、Gateway queue 寫入、Telegram 實發、Bot API、receipt production write、AI runtime、production write、secret 讀取、付費 API、host write、kubectl 或不可逆操作。", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "source_readbacks", + "rollups", + "report_owner_review", + "receipt_readback_plan", + "owner_review_gates", + "drift_monitor_owner_review", + "report_truth_owner_review", + "activation_boundaries", + "telegram_policy", + "agent_roles", + "operator_decision", + "next_actions" + ], + "properties": { + "schema_version": { + "type": "string", + "const": "ai_agent_receipt_readback_owner_review_v1" + }, + "generated_at": { + "type": "string", + "minLength": 1 + }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_priority", + "current_task_id", + "next_task_id", + "read_only_mode", + "runtime_authority", + "status_note" + ], + "properties": { + "overall_completion_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "current_priority": { "type": "string", "enum": ["P0", "P1", "P2", "P3"] }, + "current_task_id": { "type": "string", "const": "P2-406B" }, + "next_task_id": { "type": "string", "const": "P2-407" }, + "read_only_mode": { "type": "boolean", "const": true }, + "runtime_authority": { + "type": "string", + "const": "receipt_readback_owner_review_only_no_send_or_write" + }, + "status_note": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "source_refs": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "source_readbacks": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/source_readback" } + }, + "rollups": { + "type": "object", + "required": [ + "source_readback_count", + "report_cadence_count", + "owner_review_gate_count", + "receipt_readback_check_count", + "drift_candidate_count", + "report_truth_blocker_count", + "approval_required_count", + "blocked_runtime_action_count", + "live_write_count", + "telegram_send_count", + "gateway_queue_write_count", + "bot_api_call_count", + "production_write_count", + "secret_read_count", + "paid_api_call_count", + "host_write_count", + "kubectl_action_count" + ], + "properties": { + "source_readback_count": { "type": "integer", "minimum": 0 }, + "report_cadence_count": { "type": "integer", "minimum": 0 }, + "owner_review_gate_count": { "type": "integer", "minimum": 0 }, + "receipt_readback_check_count": { "type": "integer", "minimum": 0 }, + "drift_candidate_count": { "type": "integer", "minimum": 0 }, + "report_truth_blocker_count": { "type": "integer", "minimum": 0 }, + "approval_required_count": { "type": "integer", "minimum": 0 }, + "blocked_runtime_action_count": { "type": "integer", "minimum": 0 }, + "live_write_count": { "type": "integer", "const": 0 }, + "telegram_send_count": { "type": "integer", "const": 0 }, + "gateway_queue_write_count": { "type": "integer", "const": 0 }, + "bot_api_call_count": { "type": "integer", "const": 0 }, + "production_write_count": { "type": "integer", "const": 0 }, + "secret_read_count": { "type": "integer", "const": 0 }, + "paid_api_call_count": { "type": "integer", "const": 0 }, + "host_write_count": { "type": "integer", "const": 0 }, + "kubectl_action_count": { "type": "integer", "const": 0 } + }, + "additionalProperties": false + }, + "report_owner_review": { + "type": "object", + "required": ["cadences"], + "properties": { + "cadences": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/report_cadence" } + } + }, + "additionalProperties": false + }, + "receipt_readback_plan": { + "type": "object", + "required": [ + "canonical_room", + "canonical_room_env", + "gateway_owner", + "arbiter", + "receipt_owner", + "replay_owner", + "dry_run_receipt_only", + "owner_review_required_before_canary", + "canary_send_approved", + "receipt_production_write_enabled", + "readback_checks" + ], + "properties": { + "canonical_room": { "type": "string", "const": "AwoooI SRE 戰情室" }, + "canonical_room_env": { "type": "string", "const": "SRE_GROUP_CHAT_ID" }, + "gateway_owner": { "type": "string", "const": "telegram_ops" }, + "arbiter": { "type": "string", "const": "openclaw" }, + "receipt_owner": { "type": "string", "const": "hermes" }, + "replay_owner": { "type": "string", "const": "nemotron" }, + "dry_run_receipt_only": { "type": "boolean", "const": true }, + "owner_review_required_before_canary": { "type": "boolean", "const": true }, + "canary_send_approved": { "type": "boolean", "const": false }, + "receipt_production_write_enabled": { "type": "boolean", "const": false }, + "readback_checks": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/readback_check" } + } + }, + "additionalProperties": false + }, + "owner_review_gates": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/owner_review_gate" } + }, + "drift_monitor_owner_review": { "$ref": "#/$defs/drift_monitor_review" }, + "report_truth_owner_review": { "$ref": "#/$defs/report_truth_review" }, + "activation_boundaries": { + "type": "object", + "additionalProperties": { "type": "boolean" } + }, + "telegram_policy": { + "type": "object", + "required": [ + "status", + "canonical_room", + "canonical_room_env", + "gateway_queue_write_allowed", + "direct_bot_api_allowed", + "telegram_send_allowed", + "receipt_write_allowed" + ], + "properties": { + "status": { "type": "string", "minLength": 1 }, + "canonical_room": { "type": "string", "const": "AwoooI SRE 戰情室" }, + "canonical_room_env": { "type": "string", "const": "SRE_GROUP_CHAT_ID" }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "direct_bot_api_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "receipt_write_allowed": { "type": "boolean", "const": false } + }, + "additionalProperties": true + }, + "agent_roles": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/agent_role" } + }, + "operator_decision": { + "type": "object", + "required": [ + "status", + "approval_packet_required", + "rollback_plan_required", + "mute_plan_required", + "required_fields", + "stop_conditions", + "next_decision_point" + ], + "properties": { + "status": { "type": "string", "const": "owner_review_required_before_canary" }, + "approval_packet_required": { "type": "boolean", "const": true }, + "rollback_plan_required": { "type": "boolean", "const": true }, + "mute_plan_required": { "type": "boolean", "const": true }, + "required_fields": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "stop_conditions": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "next_decision_point": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "next_actions": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["task_id", "priority", "summary", "gate"], + "properties": { + "task_id": { "type": "string", "minLength": 1 }, + "priority": { "type": "string", "enum": ["P0", "P1", "P2", "P3"] }, + "summary": { "type": "string", "minLength": 1 }, + "gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + } + }, + "$defs": { + "source_readback": { + "type": "object", + "required": [ + "readback_id", + "source_schema_version", + "source_ref", + "endpoint", + "owner_agent", + "status", + "evidence_status", + "key_readback", + "next_action" + ], + "properties": { + "readback_id": { "type": "string", "minLength": 1 }, + "source_schema_version": { "type": "string", "minLength": 1 }, + "source_ref": { "type": "string", "minLength": 1 }, + "endpoint": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "type": "string", "minLength": 1 }, + "evidence_status": { "type": "string", "minLength": 1 }, + "key_readback": { "type": "string", "minLength": 1 }, + "next_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "report_cadence": { + "type": "object", + "required": [ + "cadence_id", + "display_name", + "owner_agent", + "completion_percent", + "delivery_state", + "source_confidence", + "live_delivery_count", + "review_status", + "next_gate" + ], + "properties": { + "cadence_id": { "type": "string", "enum": ["daily", "weekly", "monthly"] }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron"] }, + "completion_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "delivery_state": { "type": "string", "minLength": 1 }, + "source_confidence": { "type": "string", "minLength": 1 }, + "live_delivery_count": { "type": "integer", "const": 0 }, + "review_status": { "type": "string", "minLength": 1 }, + "next_gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "readback_check": { + "type": "object", + "required": [ + "check_id", + "owner_agent", + "status", + "evidence_refs", + "blocked_runtime_action" + ], + "properties": { + "check_id": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "type": "string", "minLength": 1 }, + "evidence_refs": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "blocked_runtime_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "owner_review_gate": { + "type": "object", + "required": [ + "gate_id", + "title", + "owner_agent", + "risk_tier", + "status", + "approval_required", + "required_owner_fields", + "acceptance_checks", + "rejection_reasons", + "blocked_runtime_actions", + "evidence_refs", + "next_action" + ], + "properties": { + "gate_id": { "type": "string", "minLength": 1 }, + "title": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron"] }, + "risk_tier": { "type": "string", "enum": ["low", "medium", "high", "critical"] }, + "status": { "type": "string", "minLength": 1 }, + "approval_required": { "type": "boolean", "const": true }, + "required_owner_fields": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "acceptance_checks": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "rejection_reasons": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "blocked_runtime_actions": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "evidence_refs": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + }, + "next_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "drift_monitor_review": { + "type": "object", + "required": [ + "source_task_id", + "drift_candidate_count", + "action_required_candidate_count", + "stale_source_snapshot_count", + "external_lookup_allowed", + "package_upgrade_allowed", + "owner_actions" + ], + "properties": { + "source_task_id": { "type": "string", "const": "P2-004" }, + "drift_candidate_count": { "type": "integer", "minimum": 0 }, + "action_required_candidate_count": { "type": "integer", "minimum": 0 }, + "stale_source_snapshot_count": { "type": "integer", "minimum": 0 }, + "external_lookup_allowed": { "type": "boolean", "const": false }, + "package_upgrade_allowed": { "type": "boolean", "const": false }, + "owner_actions": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + } + }, + "additionalProperties": false + }, + "report_truth_review": { + "type": "object", + "required": [ + "source_task_id", + "all_zero_weekly_report_is_actionable_anomaly", + "all_zero_weekly_report_confidence", + "zero_signal_finding_count", + "critical_finding_count", + "high_finding_count", + "telegram_report_send_allowed", + "cronjob_change_allowed", + "freshness_gate_implemented", + "source_confidence_gate_implemented", + "actionability_score_implemented", + "owner_actions" + ], + "properties": { + "source_task_id": { "type": "string", "const": "P2-403J" }, + "all_zero_weekly_report_is_actionable_anomaly": { "type": "boolean", "const": true }, + "all_zero_weekly_report_confidence": { "type": "string", "const": "low_trust_actionable_anomaly" }, + "zero_signal_finding_count": { "type": "integer", "minimum": 0 }, + "critical_finding_count": { "type": "integer", "minimum": 0 }, + "high_finding_count": { "type": "integer", "minimum": 0 }, + "telegram_report_send_allowed": { "type": "boolean", "const": false }, + "cronjob_change_allowed": { "type": "boolean", "const": false }, + "freshness_gate_implemented": { "type": "boolean", "const": false }, + "source_confidence_gate_implemented": { "type": "boolean", "const": false }, + "actionability_score_implemented": { "type": "boolean", "const": false }, + "owner_actions": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + } + }, + "additionalProperties": false + }, + "agent_role": { + "type": "object", + "required": [ + "agent_id", + "role", + "specialty", + "autonomy_level", + "approval_gate", + "runtime_write_allowed", + "outputs" + ], + "properties": { + "agent_id": { "type": "string", "enum": ["openclaw", "hermes", "nemotron"] }, + "role": { "type": "string", "minLength": 1 }, + "specialty": { "type": "string", "minLength": 1 }, + "autonomy_level": { "type": "string", "minLength": 1 }, + "approval_gate": { "type": "string", "minLength": 1 }, + "runtime_write_allowed": { "type": "boolean", "const": false }, + "outputs": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + } + }, + "additionalProperties": false + } + }, + "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 807b4a188..d8e07db9f 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 @@ -5060,3 +5060,17 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 - 驗證:P2-004 JSON schema / snapshot parse 通過;`python3 -m py_compile src/services/dependency_supply_chain_drift_monitor.py` 通過;目標 service / API tests `8 passed`;zh-TW / en i18n JSON parse 與 key parity 通過;source-control / security mirror guards 與 `git diff --check` 通過。Web typecheck 因乾淨 worktree 缺 `node_modules` 未執行,本輪未安裝套件或寫 lockfile。 **裁決:** P2-004 是依賴 / 供應鏈漂移監控讀回,不是外部 CVE / license / registry / Agent market 查詢,不是套件升級、lockfile 寫入、Docker build、PR 建立、Telegram 實發、secret 讀取、host probe 或 production write。下一步維持 `P2-406B`:把日週月報、Telegram receipt owner review 與 P2-004 drift monitor 串接成只讀審核視圖;所有 live send / queue write / Bot API / receipt write 仍為 `0`。 + +### 2026-06-18 23:40 (台北) — §8 / P2-406B — 新增 Receipt Readback Owner Review 只讀控制面 — 把日週月報、Telegram receipt 與供應鏈漂移串成同一個 owner gate + +**觸發**:P2-004 已把依賴 / 供應鏈漂移監控收斂成 repo-only 讀回,但下一步不能只停在漂移卡片;必須把日報 / 週報 / 月報、P2-405F Telegram owner gate、P2-403J 報表真相與 P2-004 drift monitor 串成同一個 owner review surface,讓統帥可以看到 AI Agent 是否真的在互相檢查、互相接手、並且在未批准前維持 `0 / false` runtime 邊界。 + +**已推進:** +- 新增 `docs/schemas/ai_agent_receipt_readback_owner_review_v1.schema.json` 與 `docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json`。 +- 新增 `apps/api/src/services/ai_agent_receipt_readback_owner_review.py` 與 `GET /api/v1/agents/agent-receipt-readback-owner-review`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-406B 卡片,顯示 source readback、日週月報 cadence、owner review gate、receipt readback plan、Telegram canonical room、OpenClaw / Hermes / Nemotron 分工、truth blockers、drift candidates 與 false boundary。 +- P2-406B 固定 6 個 source readback、3 個報告節奏、6 個 owner review gate、8 個 receipt readback check、9 個 drift candidate、5 個 report truth blocker、17 個 runtime false boundary。 +- OpenClaw 固定為 arbiter 與 high-risk truth / supply-chain gate;Hermes 固定為 receipt owner 與 no-send delivery packet owner;Nemotron 固定為 replay / no-write analyst,不替換 OpenClaw、不接 production route。 +- 本地驗證:JSON schema / snapshot parse 通過;`python3 -m py_compile apps/api/src/services/ai_agent_receipt_readback_owner_review.py` 通過;目標 service / API tests `9 passed`;zh-TW / en i18n JSON parse 通過。Web typecheck 因乾淨 worktree 缺 `node_modules` 未執行,本輪未安裝套件或寫 lockfile。 + +**裁決:** P2-406B 是 receipt readback owner review 與 governance 可視化,不是 Telegram send、Gateway queue write、Bot API call、receipt production write、AI analysis runtime、中低風險 auto execution、production optimization、secret read、paid API、host write、kubectl action、destructive operation 或 OpenClaw 替換。下一步是 `P2-407`:AI 報表自動分析 no-write runtime,只產生 committed snapshot / 草稿與 actionability score,不得實發或寫 production。