From a5b1f355c4d68fb54108bd14278ad414a91bc511 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Jun 2026 13:53:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=96=B0=E5=A2=9E=20owner?= =?UTF-8?q?=20approved=20result=20capture=20readback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 33 ++ ..._owner_approved_result_capture_readback.py | 391 ++++++++++++++ ..._owner_approved_result_capture_readback.py | 116 +++++ ...er_approved_result_capture_readback_api.py | 37 ++ apps/web/messages/zh-TW.json | 87 ++++ .../tabs/automation-inventory-tab.tsx | 276 +++++++++- apps/web/src/lib/api-client.ts | 168 ++++++ docs/LOGBOOK.md | 22 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 7 +- ...T_INTERACTION_LEARNING_PROOF_2026-06-11.md | 8 +- ...ed_result_capture_readback_2026-06-13.json | 480 ++++++++++++++++++ ...ved_result_capture_readback_v1.schema.json | 172 +++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 17 +- 13 files changed, 1805 insertions(+), 9 deletions(-) create mode 100644 apps/api/src/services/ai_agent_owner_approved_result_capture_readback.py create mode 100644 apps/api/tests/test_ai_agent_owner_approved_result_capture_readback.py create mode 100644 apps/api/tests/test_ai_agent_owner_approved_result_capture_readback_api.py create mode 100644 docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json create mode 100644 docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index af9d7689a..573f7cf2f 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_owner_approved_learning_dry_run import ( from src.services.ai_agent_owner_approved_result_capture_dry_run import ( load_latest_ai_agent_owner_approved_result_capture_dry_run, ) +from src.services.ai_agent_owner_approved_result_capture_readback import ( + load_latest_ai_agent_owner_approved_result_capture_readback, +) from src.services.ai_agent_operation_permission_model import ( load_latest_ai_agent_operation_permission_model, ) @@ -1227,6 +1230,36 @@ async def get_agent_owner_approved_result_capture_dry_run() -> dict[str, Any]: ) from exc +@router.get( + "/agent-owner-approved-result-capture-readback", + response_model=dict[str, Any], + summary="取得 AI Agent owner-approved result capture readback", + description=( + "讀取最新已提交的 P2-107 owner-approved result capture readback;" + "此端點只回傳 result capture readback digest、promotion readiness review、failure lane、" + "reviewer queue preview 與 operator action," + "不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、" + "不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret。" + ), +) +async def get_agent_owner_approved_result_capture_readback() -> dict[str, Any]: + """Return the latest read-only owner-approved result capture readback contract.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_result_capture_readback) + 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_owner_approved_result_capture_readback_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent owner-approved result capture readback 無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_owner_approved_result_capture_readback.py b/apps/api/src/services/ai_agent_owner_approved_result_capture_readback.py new file mode 100644 index 000000000..3d1e0abac --- /dev/null +++ b/apps/api/src/services/ai_agent_owner_approved_result_capture_readback.py @@ -0,0 +1,391 @@ +""" +AI Agent owner-approved result capture readback snapshot. + +Loads the latest committed P2-107 owner-approved result capture readback +contract. This module validates repo-committed evidence only; it never writes +scores, result capture rows, learning state, PlayBook trust, reviewer queues, +Gateway queues, Telegram messages, production targets, or secrets. +""" + +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_owner_approved_result_capture_readback_*.json" +_SCHEMA_VERSION = "ai_agent_owner_approved_result_capture_readback_v1" +_RUNTIME_AUTHORITY = "owner_approved_result_capture_readback_only_no_live_write" + + +def load_latest_ai_agent_owner_approved_result_capture_readback( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed owner-approved result capture readback contract.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent owner-approved result capture readback snapshots found in {directory}" + ) + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + _require_schema(payload, str(latest)) + _require_prior_dry_run(payload, str(latest)) + _require_readback_truth(payload, str(latest)) + _require_readback_digests(payload, str(latest)) + _require_promotion_reviews(payload, str(latest)) + _require_failure_lanes(payload, str(latest)) + _require_reviewer_queue_preview(payload, str(latest)) + _require_operator_actions(payload, str(latest)) + _require_display_redaction(payload, str(latest)) + _require_no_forbidden_display_terms(payload, str(latest)) + _require_rollup_consistency(payload, str(latest)) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + if status.get("read_only_mode") is not True: + raise ValueError(f"{label}: program_status.read_only_mode must be true") + if status.get("runtime_authority") != _RUNTIME_AUTHORITY: + raise ValueError(f"{label}: runtime_authority must remain {_RUNTIME_AUTHORITY}") + if status.get("current_task_id") != "P2-107": + raise ValueError(f"{label}: current_task_id must be P2-107") + if status.get("next_task_id") != "P2-108": + raise ValueError(f"{label}: next_task_id must be P2-108") + + +def _require_prior_dry_run(payload: dict[str, Any], label: str) -> None: + prior = payload.get("prior_dry_run_readback") or {} + if prior.get("source_schema_version") != "ai_agent_owner_approved_result_capture_dry_run_v1": + raise ValueError(f"{label}: prior_dry_run_readback must chain from P2-106") + required_counts = { + "result_capture_template_count": 5, + "score_fixture_count": 5, + "dry_run_gate_count": 7, + "operator_action_count": 5, + "approval_required_gate_count": 2, + "blocked_gate_count": 1, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "dry_run_preview_generated_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + } + mismatches = { + key: {"expected": expected, "actual": prior.get(key)} + for key, expected in required_counts.items() + if prior.get(key) != expected + } + if mismatches: + raise ValueError(f"{label}: P2-106 prior dry-run counts mismatch: {mismatches}") + + +def _require_readback_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("readback_truth") or {} + required_true = { + "p2_106_dry_run_loaded", + "fixture_readback_allowed", + "readback_digest_ready", + "promotion_readiness_review_ready", + "owner_review_required_before_promotion", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: readback readiness flags must remain true: {missing}") + + required_false = { + "canonical_runtime_readback_enabled", + "runtime_result_capture_write_enabled", + "runtime_score_write_enabled", + "runtime_learning_write_enabled", + "playbook_trust_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled", + } + unsafe = sorted(field for field in required_false if truth.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: runtime read/write/send flags must remain false: {unsafe}") + + zero_counts = { + "owner_approval_received_count", + "readback_digest_generated_count", + "promotion_approved_count", + "reviewer_queue_write_count", + "result_capture_write_count_24h", + "score_write_count_24h", + "learning_write_count_24h", + "playbook_trust_write_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "production_write_count_24h", + "secret_read_count_24h", + "destructive_operation_count_24h", + } + non_zero = sorted(field for field in zero_counts if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: readback live counters must remain zero: {non_zero}") + + +def _require_readback_digests(payload: dict[str, Any], label: str) -> None: + digests = payload.get("result_capture_readback_digests") or [] + digest_ids = {digest.get("digest_id") for digest in digests} + required = { + "readback_digest_approved_execution_result", + "readback_digest_execution_failed_candidate", + "readback_digest_pending_human_gate", + "readback_digest_manual_or_noop", + "readback_digest_post_write_verifier_receipt", + } + if digest_ids != required: + raise ValueError(f"{label}: readback digests must match {sorted(required)}") + + valid_statuses = {"ready_for_owner_review", "approval_required", "blocked_by_policy"} + for digest in digests: + digest_id = digest.get("digest_id") + if digest.get("status") not in valid_statuses: + raise ValueError(f"{label}: digest {digest_id} status is invalid") + if digest.get("fixture_only") is not True: + raise ValueError(f"{label}: digest {digest_id} fixture_only must remain true") + if digest.get("runtime_read_enabled") is not False: + raise ValueError(f"{label}: digest {digest_id} runtime_read_enabled must remain false") + if digest.get("write_enabled") is not False: + raise ValueError(f"{label}: digest {digest_id} write_enabled must remain false") + if not digest.get("readback_fields") or not digest.get("verifier_checks"): + raise ValueError(f"{label}: digest {digest_id} must list fields and verifier checks") + if not digest.get("promotion_blocker"): + raise ValueError(f"{label}: digest {digest_id} must include promotion_blocker") + if not _is_redacted_sha256(digest.get("evidence_hash")): + raise ValueError(f"{label}: digest {digest_id} must expose evidence_hash") + + +def _require_promotion_reviews(payload: dict[str, Any], label: str) -> None: + reviews = payload.get("promotion_readiness_reviews") or [] + review_ids = {review.get("review_id") for review in reviews} + required = { + "promotion_review_owner_scope", + "promotion_review_score_fixture_parity", + "promotion_review_redaction_digest", + "promotion_review_failure_candidate", + "promotion_review_live_writer_gate", + } + if review_ids != required: + raise ValueError(f"{label}: promotion reviews must match {sorted(required)}") + + valid_states = {"ready_for_owner_review", "needs_owner_review", "blocked_by_policy"} + valid_tiers = {"low", "medium", "high", "critical"} + for review in reviews: + review_id = review.get("review_id") + if review.get("readiness_state") not in valid_states: + raise ValueError(f"{label}: promotion review {review_id} readiness_state is invalid") + if review.get("risk_tier") not in valid_tiers: + raise ValueError(f"{label}: promotion review {review_id} risk_tier is invalid") + if review.get("promotion_allowed") is not False: + raise ValueError(f"{label}: promotion review {review_id} promotion_allowed must remain false") + if review.get("creates_runtime_write") is not False: + raise ValueError(f"{label}: promotion review {review_id} creates_runtime_write must remain false") + if not review.get("required_before_promotion") or not review.get("failure_if_missing"): + raise ValueError(f"{label}: promotion review {review_id} must list promotion requirements") + + +def _require_failure_lanes(payload: dict[str, Any], label: str) -> None: + lanes = payload.get("failure_lanes") or [] + lane_ids = {lane.get("lane_id") for lane in lanes} + required = { + "failure_lane_missing_owner_scope", + "failure_lane_redaction_mismatch", + "failure_lane_verifier_fixture_missing", + "failure_lane_agent_disagreement", + } + if lane_ids != required: + raise ValueError(f"{label}: failure lanes must match {sorted(required)}") + + for lane in lanes: + lane_id = lane.get("lane_id") + if lane.get("status") not in {"ready_for_review", "blocked_by_policy"}: + raise ValueError(f"{label}: failure lane {lane_id} status is invalid") + if lane.get("aborts_promotion") is not True: + raise ValueError(f"{label}: failure lane {lane_id} aborts_promotion must remain true") + if lane.get("creates_runtime_write") is not False: + raise ValueError(f"{label}: failure lane {lane_id} creates_runtime_write must remain false") + if not lane.get("trigger_condition") or not lane.get("required_response"): + raise ValueError(f"{label}: failure lane {lane_id} must include trigger and response") + + +def _require_reviewer_queue_preview(payload: dict[str, Any], label: str) -> None: + queue_items = payload.get("reviewer_queue_preview") or [] + queue_ids = {item.get("queue_id") for item in queue_items} + required = { + "review_queue_owner_scope", + "review_queue_public_redaction", + "review_queue_failure_candidate", + "review_queue_live_writer_blocker", + } + if queue_ids != required: + raise ValueError(f"{label}: reviewer queue preview must match {sorted(required)}") + + for item in queue_items: + queue_id = item.get("queue_id") + if item.get("status") not in {"queued_for_owner_review", "blocked_by_policy"}: + raise ValueError(f"{label}: reviewer queue {queue_id} status is invalid") + if item.get("queue_write_enabled") is not False: + raise ValueError(f"{label}: reviewer queue {queue_id} queue_write_enabled must remain false") + if item.get("telegram_send_enabled") is not False: + raise ValueError(f"{label}: reviewer queue {queue_id} telegram_send_enabled must remain false") + if not item.get("required_inputs"): + raise ValueError(f"{label}: reviewer queue {queue_id} must list required_inputs") + if not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: reviewer queue {queue_id} must expose evidence_hash") + + +def _require_operator_actions(payload: dict[str, Any], label: str) -> None: + actions = payload.get("operator_actions") or [] + action_types = {action.get("action_type") for action in actions} + required = { + "review_readback", + "compare_digest", + "review_failure_lane", + "reject_or_rework", + "promote_to_next_gate", + } + if action_types != required: + raise ValueError(f"{label}: operator actions must match {sorted(required)}") + for action in actions: + if action.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: operator action {action.get('action_id')} must not allow runtime write") + if not action.get("operator_instruction"): + raise ValueError(f"{label}: operator action {action.get('action_id')} must include instruction") + + +def _require_display_redaction(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + if contract.get("redaction_required") is not True: + raise ValueError(f"{label}: display redaction must remain required") + required_false = { + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed", + } + unsafe = sorted(field for field in required_false if contract.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: display redaction fields must remain false: {unsafe}") + + +def _require_no_forbidden_display_terms(payload: dict[str, Any], label: str) -> None: + forbidden_terms = { + "工作視窗", + "對話內容", + "批准!繼續", + "In app browser", + "My request for Codex", + "browser_context", + "codex_user_message", + "prompt_text", + "raw prompt", + "private reasoning", + "chain of thought", + "private_reasoning", + "chain_of_thought", + "authorization_header", + "work window transcript", + "internal collaboration transcript", + } + hits: list[str] = [] + + def walk(value: Any, path: str) -> None: + if isinstance(value, dict): + for key, nested in value.items(): + walk(nested, f"{path}.{key}" if path else str(key)) + return + if isinstance(value, list): + for index, nested in enumerate(value): + walk(nested, f"{path}[{index}]") + return + if isinstance(value, str): + matched = sorted(term for term in forbidden_terms if term in value) + if matched: + hits.append(f"{path}: {', '.join(matched)}") + + walk(payload, "") + if hits: + raise ValueError(f"{label}: forbidden display terms found: {hits}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + truth = payload.get("readback_truth") or {} + prior = payload.get("prior_dry_run_readback") or {} + digests = payload.get("result_capture_readback_digests") or [] + reviews = payload.get("promotion_readiness_reviews") or [] + lanes = payload.get("failure_lanes") or [] + queue_items = payload.get("reviewer_queue_preview") or [] + actions = payload.get("operator_actions") or [] + expected = { + "readback_digest_count": len(digests), + "promotion_review_count": len(reviews), + "failure_lane_count": len(lanes), + "reviewer_queue_preview_count": len(queue_items), + "operator_action_count": len(actions), + "approval_required_digest_count": sum(1 for item in digests if item.get("status") == "approval_required"), + "blocked_digest_count": sum(1 for item in digests if item.get("status") == "blocked_by_policy"), + "ready_review_count": sum(1 for item in reviews if item.get("readiness_state") == "ready_for_owner_review"), + "blocked_review_count": sum(1 for item in reviews if item.get("readiness_state") == "blocked_by_policy"), + "blocked_failure_lane_count": sum(1 for item in lanes if item.get("status") == "blocked_by_policy"), + "queued_reviewer_preview_count": sum( + 1 for item in queue_items if item.get("status") == "queued_for_owner_review" + ), + "blocked_reviewer_preview_count": sum(1 for item in queue_items if item.get("status") == "blocked_by_policy"), + "approved_without_execution_meta_24h": prior.get("approved_without_execution_meta_24h"), + "execution_failed_with_matched_24h": prior.get("execution_failed_with_matched_24h"), + "owner_approval_received_count": truth.get("owner_approval_received_count"), + "readback_digest_generated_count": truth.get("readback_digest_generated_count"), + "promotion_approved_count": truth.get("promotion_approved_count"), + "reviewer_queue_write_count": truth.get("reviewer_queue_write_count"), + "result_capture_write_count": truth.get("result_capture_write_count_24h"), + "score_write_count": truth.get("score_write_count_24h"), + "learning_write_count": truth.get("learning_write_count_24h"), + "playbook_trust_write_count": truth.get("playbook_trust_write_count_24h"), + "gateway_queue_write_count": truth.get("gateway_queue_write_count_24h"), + "telegram_send_count": truth.get("telegram_send_count_24h"), + "production_write_count": truth.get("production_write_count_24h"), + "secret_read_count": truth.get("secret_read_count_24h"), + "destructive_operation_count": truth.get("destructive_operation_count_24h"), + } + mismatches = { + key: {"expected": expected_value, "actual": rollups.get(key)} + for key, expected_value in expected.items() + if rollups.get(key) != expected_value + } + if mismatches: + raise ValueError(f"{label}: rollup counts mismatch: {mismatches}") + + +def _is_redacted_sha256(value: Any) -> bool: + if not isinstance(value, str): + return False + if not value.startswith("sha256:") or len(value) != 71: + return False + return all(char in "0123456789abcdef" for char in value.removeprefix("sha256:")) diff --git a/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback.py b/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback.py new file mode 100644 index 000000000..75f43074a --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback.py @@ -0,0 +1,116 @@ +import copy +import json + +import pytest + +from src.services.ai_agent_owner_approved_result_capture_readback import ( + load_latest_ai_agent_owner_approved_result_capture_readback, +) + + +def _write_snapshot(tmp_path, payload): + path = tmp_path / "ai_agent_owner_approved_result_capture_readback_2026-06-13.json" + path.write_text(json.dumps(payload), encoding="utf-8") + return path + + +def test_load_latest_ai_agent_owner_approved_result_capture_readback(): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + + assert data["schema_version"] == "ai_agent_owner_approved_result_capture_readback_v1" + assert data["program_status"]["current_task_id"] == "P2-107" + assert data["program_status"]["next_task_id"] == "P2-108" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_dry_run_readback"]["source_schema_version"] == "ai_agent_owner_approved_result_capture_dry_run_v1" + assert data["prior_dry_run_readback"]["result_capture_template_count"] == 5 + assert data["prior_dry_run_readback"]["approved_without_execution_meta_24h"] == 63 + assert data["readback_truth"]["p2_106_dry_run_loaded"] is True + assert data["readback_truth"]["fixture_readback_allowed"] is True + assert data["readback_truth"]["readback_digest_ready"] is True + assert data["readback_truth"]["canonical_runtime_readback_enabled"] is False + assert data["readback_truth"]["reviewer_queue_write_enabled"] is False + assert data["readback_truth"]["owner_approval_received_count"] == 0 + assert data["readback_truth"]["readback_digest_generated_count"] == 0 + assert data["readback_truth"]["promotion_approved_count"] == 0 + assert data["rollups"]["readback_digest_count"] == 5 + assert data["rollups"]["promotion_review_count"] == 5 + assert data["rollups"]["failure_lane_count"] == 4 + assert data["rollups"]["reviewer_queue_preview_count"] == 4 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["reviewer_queue_write_count"] == 0 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + + +def test_rejects_readback_digest_generated_count(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["readback_truth"]["readback_digest_generated_count"] = 1 + bad["rollups"]["readback_digest_generated_count"] = 1 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live counters"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_canonical_runtime_read_enabled(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["readback_truth"]["canonical_runtime_readback_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="runtime read/write/send flags"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_digest_write_enabled(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["result_capture_readback_digests"][0]["write_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="write_enabled"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_promotion_allowed(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["promotion_readiness_reviews"][0]["promotion_allowed"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="promotion_allowed"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_reviewer_queue_write(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["reviewer_queue_preview"][0]["queue_write_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="queue_write_enabled"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["operator_actions"][0]["operator_instruction"] = "不得顯示工作視窗內容" + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="forbidden display terms"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) + + +def test_rejects_rollup_mismatch(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_readback() + bad = copy.deepcopy(data) + bad["rollups"]["readback_digest_count"] = 999 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_owner_approved_result_capture_readback(tmp_path) diff --git a/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback_api.py b/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback_api.py new file mode 100644 index 000000000..5190b7ead --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_result_capture_readback_api.py @@ -0,0 +1,37 @@ +from fastapi.testclient import TestClient + +from src.main import app + + +def test_get_ai_agent_owner_approved_result_capture_readback_api(): + client = TestClient(app) + response = client.get("/api/v1/agents/agent-owner-approved-result-capture-readback") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_owner_approved_result_capture_readback_v1" + assert data["program_status"]["current_task_id"] == "P2-107" + assert data["program_status"]["next_task_id"] == "P2-108" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_dry_run_readback"]["source_schema_version"] == "ai_agent_owner_approved_result_capture_dry_run_v1" + assert data["prior_dry_run_readback"]["result_capture_template_count"] == 5 + assert data["prior_dry_run_readback"]["dry_run_gate_count"] == 7 + assert data["readback_truth"]["p2_106_dry_run_loaded"] is True + assert data["readback_truth"]["fixture_readback_allowed"] is True + assert data["readback_truth"]["canonical_runtime_readback_enabled"] is False + assert data["readback_truth"]["runtime_result_capture_write_enabled"] is False + assert data["readback_truth"]["reviewer_queue_write_enabled"] is False + assert data["readback_truth"]["telegram_send_enabled"] is False + assert data["readback_truth"]["readback_digest_generated_count"] == 0 + assert data["readback_truth"]["promotion_approved_count"] == 0 + assert data["rollups"]["readback_digest_count"] == 5 + assert data["rollups"]["promotion_review_count"] == 5 + assert data["rollups"]["failure_lane_count"] == 4 + assert data["rollups"]["reviewer_queue_preview_count"] == 4 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["reviewer_queue_write_count"] == 0 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 7750a4abd..86acc4cfd 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4790,6 +4790,93 @@ "reject_or_rework": "退回重整", "promote_to_next_gate": "推進下一閘門" } + }, + "ownerApprovedResultCaptureReadback": { + "title": "P2-107 結果捕捉 readback / promotion readiness", + "source": "{generated} · {current} → {next}", + "truthTitle": "readback 真相", + "priorTitle": "P2-106 承接", + "metrics": { + "overall": "P2-107 進度", + "digests": "readback digest", + "promotionReviews": "promotion review", + "failureLanes": "failure lane", + "queuePreview": "reviewer queue", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋總數", + "ownerApprovals": "已收批准", + "generated": "runtime 產出", + "promotionApproved": "promotion 核准", + "queueWrites": "queue 寫入", + "liveWrites": "正式寫入" + }, + "flags": { + "dryRunLoaded": "P2-106 dry-run: {value}", + "fixtureReadback": "fixture readback: {value}", + "digestReady": "digest ready: {value}", + "promotionReady": "promotion review: {value}", + "ownerReview": "需統帥審核: {value}", + "canonicalRead": "canonical read: {value}", + "resultWrite": "結果寫入: {value}", + "scoreWrite": "評分寫入: {value}", + "queueWrite": "queue 寫入: {value}", + "telegramSend": "Telegram 發送: {value}", + "secretRead": "機密讀取: {value}" + }, + "labels": { + "candidateCount": "24h 候選 {value}", + "readbackFields": "readback 欄位 {count}", + "verifierChecks": "verifier 檢查 {count}", + "fixtureOnly": "僅 fixture: {value}", + "runtimeRead": "runtime 讀取: {value}", + "writeEnabled": "寫入啟用: {value}", + "requiredBefore": "promotion 前置 {count}", + "promotionAllowed": "promotion allowed: {value}", + "runtimeWrite": "runtime 寫入: {value}", + "abortsPromotion": "中止 promotion: {value}", + "requiredInputs": "必填輸入 {count}", + "queueWrite": "queue write: {value}", + "telegramSend": "Telegram send: {value}", + "evidenceHash": "evidence: {value}" + }, + "statuses": { + "ready_for_owner_review": "待 owner 審查", + "approval_required": "需批准", + "blocked_by_policy": "政策阻擋" + }, + "readinessStates": { + "ready_for_owner_review": "待 owner 審查", + "needs_owner_review": "需 owner", + "blocked_by_policy": "政策阻擋" + }, + "riskTiers": { + "low": "低風險", + "medium": "中風險", + "high": "高風險", + "critical": "關鍵阻擋" + }, + "failureStatuses": { + "ready_for_review": "可審查", + "blocked_by_policy": "政策阻擋" + }, + "queueStatuses": { + "queued_for_owner_review": "待 owner review", + "blocked_by_policy": "政策阻擋" + }, + "reviewerRoles": { + "arbiter": "仲裁", + "reporter": "報告", + "verifier": "驗證", + "safety_reviewer": "安全審查" + }, + "actionTypes": { + "review_readback": "審查 readback", + "compare_digest": "比對 digest", + "review_failure_lane": "審查 failure lane", + "reject_or_rework": "退回重整", + "promote_to_next_gate": "推進下一閘門" + } } } }, 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 be4873121..18ceceb3b 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 @@ -50,6 +50,7 @@ import { type AiAgentProactiveOperationsContractSnapshot, type AiAgentRedisDryRunGateSnapshot, type AiAgentOwnerApprovedResultCaptureDryRunSnapshot, + type AiAgentOwnerApprovedResultCaptureReadbackSnapshot, type AiAgentReportAutomationReviewSnapshot, type AiAgentReportRuntimeDryRunSnapshot, type AiAgentReportRuntimeFixtureReadbackSnapshot, @@ -430,6 +431,7 @@ export function AutomationInventoryTab() { const [matchedPlaybookLearningGap, setMatchedPlaybookLearningGap] = useState(null) const [criticReviewerResultCapture, setCriticReviewerResultCapture] = useState(null) const [ownerApprovedResultCaptureDryRun, setOwnerApprovedResultCaptureDryRun] = useState(null) + const [ownerApprovedResultCaptureReadback, setOwnerApprovedResultCaptureReadback] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -473,6 +475,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentMatchedPlaybookLearningGap(), apiClient.getAiAgentCriticReviewerResultCapture(), apiClient.getAiAgentOwnerApprovedResultCaptureDryRun(), + apiClient.getAiAgentOwnerApprovedResultCaptureReadback(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -515,6 +518,7 @@ export function AutomationInventoryTab() { matchedPlaybookLearningGapResult, criticReviewerResultCaptureResult, ownerApprovedResultCaptureDryRunResult, + ownerApprovedResultCaptureReadbackResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -554,6 +558,7 @@ export function AutomationInventoryTab() { setMatchedPlaybookLearningGap(matchedPlaybookLearningGapResult.status === 'fulfilled' ? matchedPlaybookLearningGapResult.value : null) setCriticReviewerResultCapture(criticReviewerResultCaptureResult.status === 'fulfilled' ? criticReviewerResultCaptureResult.value : null) setOwnerApprovedResultCaptureDryRun(ownerApprovedResultCaptureDryRunResult.status === 'fulfilled' ? ownerApprovedResultCaptureDryRunResult.value : null) + setOwnerApprovedResultCaptureReadback(ownerApprovedResultCaptureReadbackResult.status === 'fulfilled' ? ownerApprovedResultCaptureReadbackResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -591,6 +596,7 @@ export function AutomationInventoryTab() { matchedPlaybookLearningGapResult, criticReviewerResultCaptureResult, ownerApprovedResultCaptureDryRunResult, + ownerApprovedResultCaptureReadbackResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1411,6 +1417,62 @@ export function AutomationInventoryTab() { .slice(0, 5) }, [ownerApprovedResultCaptureDryRun]) + const visibleOwnerResultReadbackDigests = useMemo(() => { + if (!ownerApprovedResultCaptureReadback) return [] + const statusPriority = { blocked_by_policy: 0, approval_required: 1, ready_for_owner_review: 2 } as Record + return [...ownerApprovedResultCaptureReadback.result_capture_readback_digests] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 3 + const right = statusPriority[b.status] ?? 3 + if (left !== right) return left - right + return b.candidate_count_24h - a.candidate_count_24h + }) + .slice(0, 5) + }, [ownerApprovedResultCaptureReadback]) + + const visibleOwnerResultReadbackPromotionReviews = useMemo(() => { + if (!ownerApprovedResultCaptureReadback) return [] + const statePriority = { blocked_by_policy: 0, needs_owner_review: 1, ready_for_owner_review: 2 } as Record + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...ownerApprovedResultCaptureReadback.promotion_readiness_reviews] + .sort((a, b) => { + const leftState = statePriority[a.readiness_state] ?? 3 + const rightState = statePriority[b.readiness_state] ?? 3 + if (leftState !== rightState) return leftState - rightState + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.review_id.localeCompare(b.review_id) + }) + .slice(0, 5) + }, [ownerApprovedResultCaptureReadback]) + + const visibleOwnerResultReadbackFailureLanes = useMemo(() => { + if (!ownerApprovedResultCaptureReadback) return [] + const statusPriority = { blocked_by_policy: 0, ready_for_review: 1 } as Record + return [...ownerApprovedResultCaptureReadback.failure_lanes] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 2 + const right = statusPriority[b.status] ?? 2 + if (left !== right) return left - right + return a.lane_id.localeCompare(b.lane_id) + }) + .slice(0, 4) + }, [ownerApprovedResultCaptureReadback]) + + const visibleOwnerResultReadbackQueuePreview = useMemo(() => { + if (!ownerApprovedResultCaptureReadback) return [] + const statusPriority = { blocked_by_policy: 0, queued_for_owner_review: 1 } as Record + return [...ownerApprovedResultCaptureReadback.reviewer_queue_preview] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 2 + const right = statusPriority[b.status] ?? 2 + if (left !== right) return left - right + return a.queue_id.localeCompare(b.queue_id) + }) + .slice(0, 4) + }, [ownerApprovedResultCaptureReadback]) + const visibleReportTruthFindings = useMemo(() => { if (!reportTruthActionabilityReview) return [] const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record @@ -1630,7 +1692,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -1916,6 +1978,32 @@ export function AutomationInventoryTab() { + ownerApprovedResultCaptureDryRun.rollups.telegram_send_count + ownerApprovedResultCaptureDryRun.rollups.production_write_count ) + const ownerResultReadbackOverall = ownerApprovedResultCaptureReadback.program_status.overall_completion_percent + const ownerResultReadbackDigests = ownerApprovedResultCaptureReadback.rollups.readback_digest_count + const ownerResultReadbackPromotionReviews = ownerApprovedResultCaptureReadback.rollups.promotion_review_count + const ownerResultReadbackFailureLanes = ownerApprovedResultCaptureReadback.rollups.failure_lane_count + const ownerResultReadbackQueuePreview = ownerApprovedResultCaptureReadback.rollups.reviewer_queue_preview_count + const ownerResultReadbackActions = ownerApprovedResultCaptureReadback.rollups.operator_action_count + const ownerResultReadbackApprovalRequired = ownerApprovedResultCaptureReadback.rollups.approval_required_digest_count + const ownerResultReadbackBlocked = ( + ownerApprovedResultCaptureReadback.rollups.blocked_digest_count + + ownerApprovedResultCaptureReadback.rollups.blocked_review_count + + ownerApprovedResultCaptureReadback.rollups.blocked_failure_lane_count + + ownerApprovedResultCaptureReadback.rollups.blocked_reviewer_preview_count + ) + const ownerResultReadbackOwnerApprovals = ownerApprovedResultCaptureReadback.rollups.owner_approval_received_count + const ownerResultReadbackGenerated = ownerApprovedResultCaptureReadback.rollups.readback_digest_generated_count + const ownerResultReadbackPromotionApproved = ownerApprovedResultCaptureReadback.rollups.promotion_approved_count + const ownerResultReadbackQueueWrites = ownerApprovedResultCaptureReadback.rollups.reviewer_queue_write_count + const ownerResultReadbackWrites = ( + ownerApprovedResultCaptureReadback.rollups.result_capture_write_count + + ownerApprovedResultCaptureReadback.rollups.score_write_count + + ownerApprovedResultCaptureReadback.rollups.learning_write_count + + ownerApprovedResultCaptureReadback.rollups.playbook_trust_write_count + + ownerApprovedResultCaptureReadback.rollups.gateway_queue_write_count + + ownerApprovedResultCaptureReadback.rollups.telegram_send_count + + ownerApprovedResultCaptureReadback.rollups.production_write_count + ) const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count @@ -4323,6 +4411,192 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('ownerApprovedResultCaptureReadback.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('ownerApprovedResultCaptureReadback.priorTitle')} + + {ownerApprovedResultCaptureReadback.prior_dry_run_readback.readback_note} + +
+ + + + +
+
+ +
+ {t('ownerApprovedResultCaptureReadback.truthTitle')} + + {ownerApprovedResultCaptureReadback.readback_truth.truth_note} + +
+ + + + + + + + + + + +
+
+
+ +
+ {visibleOwnerResultReadbackDigests.map(digest => ( +
+
+ + {digest.display_name} + + +
+
+ + + +
+ + {digest.promotion_blocker} + +
+ + + + + +
+
+ ))} +
+ +
+ {visibleOwnerResultReadbackPromotionReviews.map(review => ( +
+
+ + {review.display_name} + + +
+ + {review.failure_if_missing} + +
+ + + + + +
+
+ ))} +
+ +
+ {visibleOwnerResultReadbackFailureLanes.map(lane => ( +
+
+ + {lane.display_name} + + +
+ + {lane.trigger_condition} + + + {lane.required_response} + +
+ + + +
+
+ ))} +
+ +
+ {visibleOwnerResultReadbackQueuePreview.map(item => ( +
+
+ + {item.display_name} + + +
+
+ + + + + + +
+
+ ))} +
+ +
+ {ownerApprovedResultCaptureReadback.operator_actions.map(action => ( +
+
+ + {action.display_name} + + +
+ + {action.operator_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 7a89fb42e..e9e9638f2 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -430,6 +430,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentOwnerApprovedResultCaptureReadback() { + const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-result-capture-readback`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -3521,6 +3526,169 @@ export interface AiAgentOwnerApprovedResultCaptureDryRunSnapshot { } } +export interface AiAgentOwnerApprovedResultCaptureReadbackSnapshot { + schema_version: 'ai_agent_owner_approved_result_capture_readback_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-107' + next_task_id: 'P2-108' + read_only_mode: true + runtime_authority: 'owner_approved_result_capture_readback_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_dry_run_readback: { + source_schema_version: 'ai_agent_owner_approved_result_capture_dry_run_v1' + readback_at: string + result_capture_template_count: number + score_fixture_count: number + dry_run_gate_count: number + operator_action_count: number + approval_required_gate_count: number + blocked_gate_count: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + owner_approval_received_count: number + dry_run_preview_generated_count: number + result_capture_write_count: number + score_write_count: number + learning_write_count: number + playbook_trust_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + production_write_count: number + readback_note: string + } + readback_truth: { + p2_106_dry_run_loaded: true + fixture_readback_allowed: true + readback_digest_ready: true + promotion_readiness_review_ready: true + owner_review_required_before_promotion: true + canonical_runtime_readback_enabled: false + runtime_result_capture_write_enabled: false + runtime_score_write_enabled: false + runtime_learning_write_enabled: false + playbook_trust_write_enabled: false + reviewer_queue_write_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + production_write_enabled: false + secret_read_enabled: false + destructive_operation_enabled: false + owner_approval_received_count: number + readback_digest_generated_count: number + promotion_approved_count: number + reviewer_queue_write_count: number + result_capture_write_count_24h: number + score_write_count_24h: number + learning_write_count_24h: number + playbook_trust_write_count_24h: number + gateway_queue_write_count_24h: number + telegram_send_count_24h: number + production_write_count_24h: number + secret_read_count_24h: number + destructive_operation_count_24h: number + truth_note: string + } + result_capture_readback_digests: Array<{ + digest_id: string + display_name: string + source_template_id: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + candidate_count_24h: number + readback_fields: string[] + verifier_checks: string[] + promotion_blocker: string + fixture_only: true + runtime_read_enabled: false + write_enabled: false + evidence_hash: string + }> + promotion_readiness_reviews: Array<{ + review_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + source_digest_id: string + readiness_state: 'ready_for_owner_review' | 'needs_owner_review' | 'blocked_by_policy' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + required_before_promotion: string[] + failure_if_missing: string + promotion_allowed: false + creates_runtime_write: false + }> + failure_lanes: Array<{ + lane_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_review' | 'blocked_by_policy' + trigger_condition: string + required_response: string + aborts_promotion: true + creates_runtime_write: false + }> + reviewer_queue_preview: Array<{ + queue_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + reviewer_role: 'arbiter' | 'reporter' | 'verifier' | 'safety_reviewer' + status: 'queued_for_owner_review' | 'blocked_by_policy' + required_inputs: string[] + queue_write_enabled: false + telegram_send_enabled: false + evidence_hash: string + }> + operator_actions: Array<{ + action_id: string + action_type: 'review_readback' | 'compare_digest' | 'review_failure_lane' | 'reject_or_rework' | 'promote_to_next_gate' + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + operator_instruction: string + runtime_write_allowed: false + }> + display_redaction_contract: { + redaction_required: true + frontend_display_policy: string + raw_prompt_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + raw_telegram_payload_display_allowed: false + work_window_transcript_display_allowed: false + } + rollups: { + readback_digest_count: number + promotion_review_count: number + failure_lane_count: number + reviewer_queue_preview_count: number + operator_action_count: number + approval_required_digest_count: number + blocked_digest_count: number + ready_review_count: number + blocked_review_count: number + blocked_failure_lane_count: number + queued_reviewer_preview_count: number + blocked_reviewer_preview_count: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + owner_approval_received_count: number + readback_digest_generated_count: number + promotion_approved_count: number + reviewer_queue_write_count: number + result_capture_write_count: number + score_write_count: number + learning_write_count: number + playbook_trust_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + production_write_count: number + secret_read_count: number + destructive_operation_count: number + } +} + export interface AiAgentOwnerApprovedFixtureDryRunSnapshot { schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index dab48c0e6..688af7bd5 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,25 @@ +## 2026-06-13|P2-107 Owner-approved result capture readback / promotion readiness 本地完成 + +**背景**:P2-106 已把統帥批准後的 result capture 行為固定成 no-write dry-run template、score fixture 與 operator action;但仍缺「批准後 Agent 看過 dry-run / 報告後,如何只讀回查、互判、排隊審核與判斷能否升級」的公開證據。 + +**完成內容**: +- 新增 `ai_agent_owner_approved_result_capture_readback_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-owner-approved-result-capture-readback`。 +- P2-107 snapshot 固定 5 個 readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action。 +- Governance automation inventory 頁新增 P2-107 區塊,顯示 P2-107 進度 `100%`、readback digest `5`、promotion review `5`、failure lane `4`、reviewer queue preview `4`、操作選項 `5`、阻擋總數 `7`、owner approval received `0`、readback digest generated `0`、promotion approved `0`、reviewer queue write `0` 與 live writes `0`。 +- 更新 MASTER §3.2 / §5、`docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md` 與 `docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md`,將下一步改為 `P2-108`。 + +**驗證**: +- JSON parse:P2-107 schema / snapshot、`zh-TW.json` 通過。 +- API/service pytest:P2-107、P2-106、P2-105、public redaction 目標測試 `36 passed`。 +- Web typecheck:`pnpm --filter @awoooi/web typecheck` 通過。 + +**安全邊界**: +- P2-107 仍是只讀契約;不讀 canonical runtime target、不寫 reviewer queue、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action。 +- 前端只顯示公開摘要、hash、count、gate 與狀態,不顯示內部協作逐字內容。 + +**下一步**: +- `P2-108`:runtime readback approval package,先固定下一關批准包、canonical readback plan、rollback drill 與 Telegram failure receipt gate;仍不得直接啟動 live writer。 + ## 2026-06-13|P2-106 Owner-approved result capture dry-run 本地完成與正式驗證 **背景**:P2-105 已把 approved 後缺口轉成 critic / reviewer scorecard、result capture contract、promotion gate 與 candidate route;但統帥批准後仍不能直接寫 score、result capture、learning、PlayBook trust 或 Telegram。本段把「批准後可以先產生什麼 no-write preview」固定成只讀契約。 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 b8982953e..873d08a34 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -12,7 +12,7 @@ | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | | 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | -| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review;P2-403H 已完成 post-write verifier implementation package;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相、告警有效性、日週月報、Agent 工作量、圖表化報告、AI 建議與風險自動化政策審查;P2-403K / L / M / N 已把 SRE 戰情室路由、報表派送啟動前閘門、no-write dry-run 與 fixture/readback/verifier dry-run 固定;P2-404 已完成 runtime worker shadow / no-write evidence;P2-101 已完成操作類別權限模型;P2-102 已完成 13 類候選操作 dry-run 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約;P2-106 已完成 owner-approved result capture dry-run,固定 5 個 no-write template、5 個 score fixture、7 個 dry-run gate 與 5 個 operator action。runtime worker、DB migration、production Redis consumer group、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_operation_permission_model_v1`、`ai_agent_candidate_operation_dry_run_evidence_v1`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`ai_agent_owner_approved_result_capture_dry_run_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | +| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review;P2-403H 已完成 post-write verifier implementation package;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相、告警有效性、日週月報、Agent 工作量、圖表化報告、AI 建議與風險自動化政策審查;P2-403K / L / M / N 已把 SRE 戰情室路由、報表派送啟動前閘門、no-write dry-run 與 fixture/readback/verifier dry-run 固定;P2-404 已完成 runtime worker shadow / no-write evidence;P2-101 已完成操作類別權限模型;P2-102 已完成 13 類候選操作 dry-run 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約;P2-106 已完成 owner-approved result capture dry-run;P2-107 已完成 owner-approved result capture readback / promotion readiness,固定 5 個 readback digest、5 個 promotion review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_operation_permission_model_v1`、`ai_agent_candidate_operation_dry_run_evidence_v1`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`ai_agent_owner_approved_result_capture_dry_run_v1`、`ai_agent_owner_approved_result_capture_readback_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`、`GET /api/v1/agents/agent-owner-approved-result-capture-readback`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | @@ -20,9 +20,9 @@ AI Agent 自動化工作包目前完成度:**93%**。本工作清單文件本 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K/L/M/N 報表與 SRE 戰情室 dry-run 鏈、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、P2-102 候選操作 dry-run 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查、P2-105 critic / reviewer 評分與 result capture 契約,以及 P2-106 owner-approved result capture dry-run;目前 live AgentSession、Agent message、handoff、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-106 已固定 5 個 no-write result capture template、5 個 score fixture、7 個 dry-run gate 與 5 個 operator action;真正下一步是 `P2-107`。 +三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K/L/M/N 報表與 SRE 戰情室 dry-run 鏈、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、P2-102 候選操作 dry-run 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查、P2-105 critic / reviewer 評分與 result capture 契約、P2-106 owner-approved result capture dry-run,以及 P2-107 owner-approved result capture readback / promotion readiness;目前 live AgentSession、Agent message、handoff、canonical runtime readback、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-107 已固定 5 個 readback digest、5 個 promotion review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action;真正下一步是 `P2-108`。 -AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-106` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture 與 owner-approved result capture dry-run。下一步是 `P2-107`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 +AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-107` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture、owner-approved result capture dry-run 與 owner-approved result capture readback / promotion readiness。下一步是 `P2-108`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: @@ -979,6 +979,7 @@ UI: | P2-104 | 完成 | 100 | OpenClaw + Hermes + NemoTron | 修復 `matched_playbook_id` 學習缺口 | `ai_agent_matched_playbook_learning_gap_v1` / snapshot / 只讀 API / governance UI;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`、approved without execution meta `63`、pending matched `2`、execution failed matched `1`、PlayBook updated_24h `0`;5 條 gap lane、5 個 learning gate、4 個 writeback candidate | 已由 P2-105 承接;不寫 learning、不更新 PlayBook trust、不送 Telegram、不寫 Gateway queue、不讀 secret | | P2-105 | 完成 | 100 | OpenClaw + Hermes + NemoTron | 批准前加入 critic / reviewer 評分 | `ai_agent_critic_reviewer_result_capture_v1` / snapshot / 只讀 API / governance UI;5 張 Agent scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;approved gap `63`、failed candidate `1`;runtime score / result capture write / learning / trust / queue / Telegram 全為 `0` | 已由 P2-106 承接;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram | | P2-106 | 完成 | 100 | OpenClaw + Hermes + NemoTron | owner-approved result capture dry-run | `ai_agent_owner_approved_result_capture_dry_run_v1` / schema / snapshot / 只讀 API / governance UI;5 個 no-write result capture template、5 個 score fixture、7 個 dry-run gate、5 個 operator action;owner approval received、preview generated、score / result capture / learning / trust / queue / Telegram 全部 `0` | 已由 P2-107 承接;不啟動 live write、不送 Telegram、不更新 PlayBook trust | +| P2-107 | 完成 | 100 | OpenClaw + Hermes + NemoTron | owner-approved result capture readback / promotion readiness | `ai_agent_owner_approved_result_capture_readback_v1` / schema / snapshot / 只讀 API / governance UI;5 個 readback digest、5 個 promotion review、4 條 failure lane、4 個 reviewer queue preview、5 個 operator action;owner approval received、readback generated、promotion approved、reviewer queue write、score / result capture / learning / trust / queue / Telegram 全部 `0` | 下一步 P2-108;不讀 canonical runtime target、不寫 reviewer queue、不啟動 live writer | ### P3 - 候選 Agent 擴展 diff --git a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md index cd474853b..ed483a921 100644 --- a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md +++ b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md @@ -169,18 +169,22 @@ | `GET /api/v1/agents/agent-matched-playbook-learning-gap` | 只讀 API;不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 Gateway queue、不送 Telegram | | `docs/schemas/ai_agent_critic_reviewer_result_capture_v1.schema.json` | P2-105 critic / reviewer result capture schema;強制 runtime score、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 維持未授權 | | `docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json` | P2-105 committed snapshot,完成度 `100%`,5 張 scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;approved gap `63`、failed candidate `1` | +| `docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json` | P2-106 owner-approved result capture dry-run schema;強制 owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 維持未授權 | +| `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` | P2-106 committed snapshot,完成度 `100%`,5 個 no-write result capture template、5 個 score fixture、7 個 dry-run gate、5 個 operator action;所有 live write / send counts 全為 `0` | +| `docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json` | P2-107 owner-approved result capture readback schema;強制 canonical runtime readback、promotion approved、reviewer queue write、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 維持未授權 | +| `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` | P2-107 committed snapshot,完成度 `100%`,5 個 readback digest、5 個 promotion review、4 條 failure lane、4 個 reviewer queue preview、5 個 operator action;canonical runtime readback、promotion approved、reviewer queue write 與所有 live write / send counts 全為 `0` | | `GET /api/v1/agents/agent-critic-reviewer-result-capture` | 只讀 API;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader 與安全驗證 | | `apps/api/src/services/ai_agent_live_read_model_gate.py` | P2-403B 只讀 loader;拒絕 live DB query、Redis consumer、unsafe fields、Telegram 與 writeback | | `GET /api/v1/agents/agent-interaction-learning-proof` | 只讀 API,不啟動 worker、不碰 Redis / DB runtime、不發 Telegram | | `GET /api/v1/agents/agent-live-read-model-gate` | 只讀 API,不連 DB、不讀寫 Redis、不發 Telegram | -| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、P2-102 candidate dry-run evidence、P2-103 task result audit trail、P2-104 matched PlayBook learning gap、P2-105 critic / reviewer result capture、Agent lane、可觀測訊號、runtime gates、前端 redaction | +| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、P2-102 candidate dry-run evidence、P2-103 task result audit trail、P2-104 matched PlayBook learning gap、P2-105 critic / reviewer result capture、P2-106 owner-approved result capture dry-run、P2-107 owner-approved result capture readback / promotion readiness、Agent lane、可觀測訊號、runtime gates、前端 redaction | ## 5. 後續優先順序 | 優先 | ID | 工作 | gate | |---:|---|---|---| -| 1 | P2-106 | owner-approved result capture dry-run | 以 P2-105 契約產生 no-write dry-run payload、owner review packet 與 verifier gate | +| 1 | P2-108 | runtime readback approval package | 以 P2-107 readback / promotion readiness 產生下一關批准包,仍需維持 no live write | ## 6. 紅線 diff --git a/docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json b/docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json new file mode 100644 index 000000000..888a740c3 --- /dev/null +++ b/docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json @@ -0,0 +1,480 @@ +{ + "schema_version": "ai_agent_owner_approved_result_capture_readback_v1", + "generated_at": "2026-06-13T14:18:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-107", + "next_task_id": "P2-108", + "read_only_mode": true, + "runtime_authority": "owner_approved_result_capture_readback_only_no_live_write", + "status_note": "P2-107 承接 P2-106 no-write dry-run,固定 result capture readback digest、promotion readiness review、failure lane 與 reviewer queue preview;本快照只讀,不寫 runtime 結果。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json", + "docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json", + "apps/api/src/services/ai_agent_owner_approved_result_capture_dry_run.py", + "apps/api/src/services/ai_agent_critic_reviewer_result_capture.py", + "apps/api/src/services/post_execution_verifier.py", + "apps/api/src/services/approval_execution.py", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md" + ], + "prior_dry_run_readback": { + "source_schema_version": "ai_agent_owner_approved_result_capture_dry_run_v1", + "readback_at": "2026-06-13T12:49:00+08:00", + "result_capture_template_count": 5, + "score_fixture_count": 5, + "dry_run_gate_count": 7, + "operator_action_count": 5, + "approval_required_gate_count": 2, + "blocked_gate_count": 1, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "dry_run_preview_generated_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + "readback_note": "P2-106 已建立 owner-approved dry-run template 與 score fixture;P2-107 只做 fixture readback 與 promotion readiness,不讀 canonical runtime target。" + }, + "readback_truth": { + "p2_106_dry_run_loaded": true, + "fixture_readback_allowed": true, + "readback_digest_ready": true, + "promotion_readiness_review_ready": true, + "owner_review_required_before_promotion": true, + "canonical_runtime_readback_enabled": false, + "runtime_result_capture_write_enabled": false, + "runtime_score_write_enabled": false, + "runtime_learning_write_enabled": false, + "playbook_trust_write_enabled": false, + "reviewer_queue_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "destructive_operation_enabled": false, + "owner_approval_received_count": 0, + "readback_digest_generated_count": 0, + "promotion_approved_count": 0, + "reviewer_queue_write_count": 0, + "result_capture_write_count_24h": 0, + "score_write_count_24h": 0, + "learning_write_count_24h": 0, + "playbook_trust_write_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_send_count_24h": 0, + "production_write_count_24h": 0, + "secret_read_count_24h": 0, + "destructive_operation_count_24h": 0, + "truth_note": "目前只允許 committed fixture readback 與 promotion readiness 檢查;owner approval、readback runtime generation、reviewer queue write、score/result/learning/trust/Gateway/Telegram/production write 仍全部為 0。" + }, + "result_capture_readback_digests": [ + { + "digest_id": "readback_digest_approved_execution_result", + "display_name": "APPROVED result capture readback digest", + "source_template_id": "dry_run_capture_approved_execution_result", + "owner_agent": "openclaw", + "status": "approval_required", + "candidate_count_24h": 63, + "readback_fields": [ + "owner_approval_id", + "approval_id", + "incident_id", + "matched_playbook_id", + "operator_outcome", + "verifier_plan_ref" + ], + "verifier_checks": [ + "owner_scope_match", + "result_state_match", + "rollback_plan_present", + "public_redaction_pass" + ], + "promotion_blocker": "owner approval 與 canonical readback 尚未啟用,禁止 promotion 到 live writer。", + "fixture_only": true, + "runtime_read_enabled": false, + "write_enabled": false, + "evidence_hash": "sha256:1313131313131313131313131313131313131313131313131313131313131313" + }, + { + "digest_id": "readback_digest_execution_failed_candidate", + "display_name": "EXECUTION_FAILED candidate readback digest", + "source_template_id": "dry_run_capture_execution_failed_candidate", + "owner_agent": "nemotron", + "status": "approval_required", + "candidate_count_24h": 1, + "readback_fields": [ + "owner_approval_id", + "approval_id", + "matched_playbook_id", + "failure_reason_ref", + "negative_learning_weight_preview", + "verifier_plan_ref" + ], + "verifier_checks": [ + "failure_reason_redacted", + "negative_learning_preview_only", + "trust_update_hold", + "replay_hash_present" + ], + "promotion_blocker": "NemoTron 只能驗證 failure candidate,不得寫負向 learning 或 trust。", + "fixture_only": true, + "runtime_read_enabled": false, + "write_enabled": false, + "evidence_hash": "sha256:2424242424242424242424242424242424242424242424242424242424242424" + }, + { + "digest_id": "readback_digest_pending_human_gate", + "display_name": "PENDING human gate readback digest", + "source_template_id": "dry_run_capture_pending_human_gate", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "candidate_count_24h": 2, + "readback_fields": [ + "approval_id", + "matched_playbook_id", + "pending_reason", + "human_decision_required", + "expires_at" + ], + "verifier_checks": [ + "pending_reason_present", + "human_gate_required", + "learning_write_absent", + "operator_followup_ready" + ], + "promotion_blocker": "pending human gate 不可被自動轉成 learning 或 result capture write。", + "fixture_only": true, + "runtime_read_enabled": false, + "write_enabled": false, + "evidence_hash": "sha256:3535353535353535353535353535353535353535353535353535353535353535" + }, + { + "digest_id": "readback_digest_manual_or_noop", + "display_name": "manual / no-op readback digest", + "source_template_id": "dry_run_capture_manual_or_noop", + "owner_agent": "hermes", + "status": "ready_for_owner_review", + "candidate_count_24h": 0, + "readback_fields": [ + "incident_id", + "approval_id", + "manual_resolution_note", + "noop_reason", + "operator_verified" + ], + "verifier_checks": [ + "manual_note_redacted", + "noop_reason_present", + "learning_exclusion_present", + "operator_verified_present" + ], + "promotion_blocker": "沒有候選資料與人工核對前,只能保留為 readback digest。", + "fixture_only": true, + "runtime_read_enabled": false, + "write_enabled": false, + "evidence_hash": "sha256:4646464646464646464646464646464646464646464646464646464646464646" + }, + { + "digest_id": "readback_digest_post_write_verifier_receipt", + "display_name": "post-write verifier receipt readback digest", + "source_template_id": "dry_run_capture_post_write_verifier_receipt", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "candidate_count_24h": 0, + "readback_fields": [ + "verifier_plan_ref", + "expected_result_hash", + "rollback_plan_ref", + "operator_ack_required" + ], + "verifier_checks": [ + "expected_hash_present", + "rollback_lane_present", + "operator_ack_required", + "canonical_read_disabled" + ], + "promotion_blocker": "post-write verifier receipt 尚未有 live write,所以不得讀 canonical target 或回寫 receipt。", + "fixture_only": true, + "runtime_read_enabled": false, + "write_enabled": false, + "evidence_hash": "sha256:5757575757575757575757575757575757575757575757575757575757575757" + } + ], + "promotion_readiness_reviews": [ + { + "review_id": "promotion_review_owner_scope", + "display_name": "Owner scope readiness", + "owner_agent": "openclaw", + "source_digest_id": "readback_digest_approved_execution_result", + "readiness_state": "needs_owner_review", + "risk_tier": "high", + "required_before_promotion": [ + "owner approval id", + "approved contract ids", + "rollback owner", + "public evidence refs" + ], + "failure_if_missing": "缺 owner scope 時,禁止推進到 P2-108。", + "promotion_allowed": false, + "creates_runtime_write": false + }, + { + "review_id": "promotion_review_score_fixture_parity", + "display_name": "Score fixture parity readiness", + "owner_agent": "openclaw", + "source_digest_id": "readback_digest_approved_execution_result", + "readiness_state": "ready_for_owner_review", + "risk_tier": "medium", + "required_before_promotion": [ + "critic score fixture", + "reviewer score fixture", + "disagreement gate", + "minimum score policy" + ], + "failure_if_missing": "缺 critic / reviewer fixture 時,不得建立 reviewer queue item。", + "promotion_allowed": false, + "creates_runtime_write": false + }, + { + "review_id": "promotion_review_redaction_digest", + "display_name": "Public redaction digest readiness", + "owner_agent": "hermes", + "source_digest_id": "readback_digest_manual_or_noop", + "readiness_state": "ready_for_owner_review", + "risk_tier": "medium", + "required_before_promotion": [ + "public summary", + "blocked fields list", + "allowed fields list", + "evidence hash" + ], + "failure_if_missing": "缺公開遮罩摘要時,治理頁與 Telegram digest 都不得顯示。", + "promotion_allowed": false, + "creates_runtime_write": false + }, + { + "review_id": "promotion_review_failure_candidate", + "display_name": "Failure candidate verifier readiness", + "owner_agent": "nemotron", + "source_digest_id": "readback_digest_execution_failed_candidate", + "readiness_state": "needs_owner_review", + "risk_tier": "high", + "required_before_promotion": [ + "failure reason ref", + "no-write replay hash", + "negative learning hold", + "post-write verifier plan" + ], + "failure_if_missing": "缺 no-write replay hash 時,NemoTron 不得提交 failure candidate。", + "promotion_allowed": false, + "creates_runtime_write": false + }, + { + "review_id": "promotion_review_live_writer_gate", + "display_name": "Live writer gate readiness", + "owner_agent": "openclaw", + "source_digest_id": "readback_digest_post_write_verifier_receipt", + "readiness_state": "blocked_by_policy", + "risk_tier": "critical", + "required_before_promotion": [ + "P2-108 approval package", + "canonical readback plan", + "rollback drill", + "Telegram failure receipt gate" + ], + "failure_if_missing": "沒有下一關批准包前,任何 live writer、Telegram send 或 queue write 都維持 0。", + "promotion_allowed": false, + "creates_runtime_write": false + } + ], + "failure_lanes": [ + { + "lane_id": "failure_lane_missing_owner_scope", + "display_name": "missing owner scope", + "owner_agent": "openclaw", + "status": "blocked_by_policy", + "trigger_condition": "owner approval id、scope 或 contract ids 不完整", + "required_response": "退回 owner packet 補件,不產生 reviewer queue write。", + "aborts_promotion": true, + "creates_runtime_write": false + }, + { + "lane_id": "failure_lane_redaction_mismatch", + "display_name": "redaction mismatch", + "owner_agent": "hermes", + "status": "blocked_by_policy", + "trigger_condition": "public summary、blocked field list 或 evidence hash 不一致", + "required_response": "重建公開遮罩 digest;前端與 Telegram digest 均不顯示原始內容。", + "aborts_promotion": true, + "creates_runtime_write": false + }, + { + "lane_id": "failure_lane_verifier_fixture_missing", + "display_name": "verifier fixture missing", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "trigger_condition": "post-write verifier plan、rollback lane 或 no-write replay hash 缺漏", + "required_response": "保留在 fixture-only readback,不讀 canonical target。", + "aborts_promotion": true, + "creates_runtime_write": false + }, + { + "lane_id": "failure_lane_agent_disagreement", + "display_name": "agent disagreement", + "owner_agent": "openclaw", + "status": "ready_for_review", + "trigger_condition": "Critic、Reviewer 或 verifier verdict 不一致", + "required_response": "提升到統帥審核;Hermes 只整理摘要,NemoTron 只補 replay hash。", + "aborts_promotion": true, + "creates_runtime_write": false + } + ], + "reviewer_queue_preview": [ + { + "queue_id": "review_queue_owner_scope", + "display_name": "owner scope review queue preview", + "owner_agent": "openclaw", + "reviewer_role": "arbiter", + "status": "queued_for_owner_review", + "required_inputs": [ + "owner approval id", + "approved contract ids", + "rollback owner" + ], + "queue_write_enabled": false, + "telegram_send_enabled": false, + "evidence_hash": "sha256:6868686868686868686868686868686868686868686868686868686868686868" + }, + { + "queue_id": "review_queue_public_redaction", + "display_name": "public redaction review queue preview", + "owner_agent": "hermes", + "reviewer_role": "reporter", + "status": "queued_for_owner_review", + "required_inputs": [ + "public summary", + "blocked display fields", + "allowed display fields" + ], + "queue_write_enabled": false, + "telegram_send_enabled": false, + "evidence_hash": "sha256:7979797979797979797979797979797979797979797979797979797979797979" + }, + { + "queue_id": "review_queue_failure_candidate", + "display_name": "failure candidate review queue preview", + "owner_agent": "nemotron", + "reviewer_role": "verifier", + "status": "queued_for_owner_review", + "required_inputs": [ + "failure reason ref", + "no-write replay hash", + "verifier plan ref" + ], + "queue_write_enabled": false, + "telegram_send_enabled": false, + "evidence_hash": "sha256:8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a" + }, + { + "queue_id": "review_queue_live_writer_blocker", + "display_name": "live writer blocker review queue preview", + "owner_agent": "openclaw", + "reviewer_role": "safety_reviewer", + "status": "blocked_by_policy", + "required_inputs": [ + "P2-108 approval package", + "canonical readback plan", + "rollback drill evidence" + ], + "queue_write_enabled": false, + "telegram_send_enabled": false, + "evidence_hash": "sha256:9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b" + } + ], + "operator_actions": [ + { + "action_id": "op_review_readback_digest", + "action_type": "review_readback", + "display_name": "審查 readback digest", + "owner_agent": "openclaw", + "operator_instruction": "確認 digest 只引用公開遮罩欄位與 hash,沒有 runtime result capture write。", + "runtime_write_allowed": false + }, + { + "action_id": "op_compare_dry_run_hash", + "action_type": "compare_digest", + "display_name": "比對 dry-run hash", + "owner_agent": "hermes", + "operator_instruction": "比對 P2-106 template hash、P2-107 digest hash 與 blocked field list。", + "runtime_write_allowed": false + }, + { + "action_id": "op_review_failure_candidate", + "action_type": "review_failure_lane", + "display_name": "審查 failure candidate", + "owner_agent": "nemotron", + "operator_instruction": "只檢查 failure reason ref 與 no-write replay hash,不寫負向 learning。", + "runtime_write_allowed": false + }, + { + "action_id": "op_reject_or_rework_readback", + "action_type": "reject_or_rework", + "display_name": "退回 readback 補件", + "owner_agent": "openclaw", + "operator_instruction": "缺 owner scope、redaction、verifier fixture 或 rollback plan 時退回補件。", + "runtime_write_allowed": false + }, + { + "action_id": "op_promote_to_p2_108_package", + "action_type": "promote_to_next_gate", + "display_name": "推進 P2-108 批准包", + "owner_agent": "openclaw", + "operator_instruction": "只在全部 readback 與 promotion readiness 通過後,建立下一關批准包;仍不啟動 live writer。", + "runtime_write_allowed": false + } + ], + "display_redaction_contract": { + "redaction_required": true, + "frontend_display_policy": "治理頁只顯示公開遮罩 summary、hash、readback digest、promotion readiness、failure lane 與 queue preview;不得顯示內部協作逐字稿、未脫敏提示內容、私有推理內容、未脫敏工具輸出或機密明文。", + "raw_prompt_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_telegram_payload_display_allowed": false, + "work_window_transcript_display_allowed": false + }, + "rollups": { + "readback_digest_count": 5, + "promotion_review_count": 5, + "failure_lane_count": 4, + "reviewer_queue_preview_count": 4, + "operator_action_count": 5, + "approval_required_digest_count": 2, + "blocked_digest_count": 2, + "ready_review_count": 2, + "blocked_review_count": 1, + "blocked_failure_lane_count": 3, + "queued_reviewer_preview_count": 3, + "blocked_reviewer_preview_count": 1, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "readback_digest_generated_count": 0, + "promotion_approved_count": 0, + "reviewer_queue_write_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json b/docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json new file mode 100644 index 000000000..fe226d5e4 --- /dev/null +++ b/docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json @@ -0,0 +1,172 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json", + "title": "AI Agent Owner Approved Result Capture Readback v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_dry_run_readback", + "readback_truth", + "result_capture_readback_digests", + "promotion_readiness_reviews", + "failure_lanes", + "reviewer_queue_preview", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { "const": "ai_agent_owner_approved_result_capture_readback_v1" }, + "generated_at": { "type": "string" }, + "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": { "enum": ["P0", "P1", "P2", "P3"] }, + "current_task_id": { "const": "P2-107" }, + "next_task_id": { "const": "P2-108" }, + "read_only_mode": { "const": true }, + "runtime_authority": { "const": "owner_approved_result_capture_readback_only_no_live_write" }, + "status_note": { "type": "string" } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "prior_dry_run_readback": { + "type": "object", + "required": [ + "source_schema_version", + "readback_at", + "result_capture_template_count", + "score_fixture_count", + "dry_run_gate_count", + "operator_action_count", + "approval_required_gate_count", + "blocked_gate_count", + "approved_without_execution_meta_24h", + "execution_failed_with_matched_24h", + "owner_approval_received_count", + "dry_run_preview_generated_count", + "result_capture_write_count", + "score_write_count", + "learning_write_count", + "playbook_trust_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "production_write_count", + "readback_note" + ], + "properties": { + "source_schema_version": { "const": "ai_agent_owner_approved_result_capture_dry_run_v1" }, + "readback_at": { "type": "string" }, + "result_capture_template_count": { "const": 5 }, + "score_fixture_count": { "const": 5 }, + "dry_run_gate_count": { "const": 7 }, + "operator_action_count": { "const": 5 }, + "approval_required_gate_count": { "const": 2 }, + "blocked_gate_count": { "const": 1 }, + "approved_without_execution_meta_24h": { "const": 63 }, + "execution_failed_with_matched_24h": { "const": 1 }, + "owner_approval_received_count": { "const": 0 }, + "dry_run_preview_generated_count": { "const": 0 }, + "result_capture_write_count": { "const": 0 }, + "score_write_count": { "const": 0 }, + "learning_write_count": { "const": 0 }, + "playbook_trust_write_count": { "const": 0 }, + "gateway_queue_write_count": { "const": 0 }, + "telegram_send_count": { "const": 0 }, + "production_write_count": { "const": 0 }, + "readback_note": { "type": "string" } + }, + "additionalProperties": false + }, + "readback_truth": { + "type": "object", + "required": [ + "p2_106_dry_run_loaded", + "fixture_readback_allowed", + "readback_digest_ready", + "promotion_readiness_review_ready", + "owner_review_required_before_promotion", + "canonical_runtime_readback_enabled", + "runtime_result_capture_write_enabled", + "runtime_score_write_enabled", + "runtime_learning_write_enabled", + "playbook_trust_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled", + "owner_approval_received_count", + "readback_digest_generated_count", + "promotion_approved_count", + "reviewer_queue_write_count", + "result_capture_write_count_24h", + "score_write_count_24h", + "learning_write_count_24h", + "playbook_trust_write_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "production_write_count_24h", + "secret_read_count_24h", + "destructive_operation_count_24h", + "truth_note" + ], + "properties": { + "p2_106_dry_run_loaded": { "const": true }, + "fixture_readback_allowed": { "const": true }, + "readback_digest_ready": { "const": true }, + "promotion_readiness_review_ready": { "const": true }, + "owner_review_required_before_promotion": { "const": true }, + "canonical_runtime_readback_enabled": { "const": false }, + "runtime_result_capture_write_enabled": { "const": false }, + "runtime_score_write_enabled": { "const": false }, + "runtime_learning_write_enabled": { "const": false }, + "playbook_trust_write_enabled": { "const": false }, + "reviewer_queue_write_enabled": { "const": false }, + "gateway_queue_write_enabled": { "const": false }, + "telegram_send_enabled": { "const": false }, + "production_write_enabled": { "const": false }, + "secret_read_enabled": { "const": false }, + "destructive_operation_enabled": { "const": false }, + "owner_approval_received_count": { "const": 0 }, + "readback_digest_generated_count": { "const": 0 }, + "promotion_approved_count": { "const": 0 }, + "reviewer_queue_write_count": { "const": 0 }, + "result_capture_write_count_24h": { "const": 0 }, + "score_write_count_24h": { "const": 0 }, + "learning_write_count_24h": { "const": 0 }, + "playbook_trust_write_count_24h": { "const": 0 }, + "gateway_queue_write_count_24h": { "const": 0 }, + "telegram_send_count_24h": { "const": 0 }, + "production_write_count_24h": { "const": 0 }, + "secret_read_count_24h": { "const": 0 }, + "destructive_operation_count_24h": { "const": 0 }, + "truth_note": { "type": "string" } + }, + "additionalProperties": false + }, + "result_capture_readback_digests": { "type": "array", "minItems": 5 }, + "promotion_readiness_reviews": { "type": "array", "minItems": 5 }, + "failure_lanes": { "type": "array", "minItems": 4 }, + "reviewer_queue_preview": { "type": "array", "minItems": 4 }, + "operator_actions": { "type": "array", "minItems": 5 }, + "display_redaction_contract": { "type": "object" }, + "rollups": { "type": "object" } + }, + "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 10ef8f108..b70b86e1a 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 @@ -643,7 +643,8 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_task_result_audit_trail_2026-06-13.json` + `GET /api/v1/agents/agent-task-result-audit-trail` | P2-103 任務結果稽核軌跡;8 條 result route、6 個 writeback contract、7 個 audit checkpoint、5 個 operator handoff;把 diagnostic-only、repair candidate、execution failed、provider unmatched、report zero-signal 等結果固定到 KM 草稿、LOGBOOK 證據、audit trail、timeline 與人工下一步;KM / LOGBOOK / audit DB / timeline / PlayBook trust / Gateway queue / Telegram 寫入全為 `0 / false`,已由 P2-104 承接 | | `docs/evaluations/ai_agent_matched_playbook_learning_gap_2026-06-13.json` + `GET /api/v1/agents/agent-matched-playbook-learning-gap` | P2-104 matched PlayBook 學習缺口;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`,active gap 是 APPROVED `63` 筆缺 execution learning、PlayBook updated_24h `0`;5 條 gap lane、5 個 learning gate、4 個 writeback candidate;learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,已由 P2-105 承接 | | `docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json` + `GET /api/v1/agents/agent-critic-reviewer-result-capture` | P2-105 critic / reviewer 評分與 result capture 契約;承接 P2-104 approved gap `63` 與 failed candidate `1`,建立 5 張 scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;runtime Critic score、Reviewer score、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-106 owner-approved result capture dry-run | -| `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 owner-approved result capture dry-run;承接 P2-105 scorecard / result capture contract / promotion gate,建立 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action;owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-107 | +| `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 owner-approved result capture dry-run;承接 P2-105 scorecard / result capture contract / promotion gate,建立 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action;owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,已由 P2-107 承接 | +| `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-readback` | P2-107 owner-approved result capture readback / promotion readiness;承接 P2-106 no-write dry-run,建立 5 個 readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action;canonical runtime readback、owner approval received、readback digest generated、promotion approved、reviewer queue write、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-108 | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -738,7 +739,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 24. 把任務結果接回 KM / LOGBOOK / 稽核軌跡。✅ P2-103 已完成;result route `8`、writeback contract `6`、audit checkpoint `7`、operator handoff `5`,KM write、LOGBOOK runtime append、audit DB write、timeline write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-104 承接。 25. 修復 matched PlayBook 學習缺口。✅ P2-104 已完成;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`,真正缺口是 APPROVED `63` 筆缺 execution learning、PlayBook updated_24h `0`;learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-105 承接。 26. 批准前加入 critic / reviewer 評分與 result capture。✅ P2-105 已完成;scorecard `5`、result capture contract `5`、promotion gate `6`、candidate route `4`,approved gap `63`、failed candidate `1`;runtime score、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-106 承接。 -27. 建立 owner-approved result capture dry-run。✅ P2-106 已完成;result capture dry-run template `5`、score fixture `5`、dry-run gate `7`、operator action `5`,owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。下一步 P2-107。 +27. 建立 owner-approved result capture dry-run。✅ P2-106 已完成;result capture dry-run template `5`、score fixture `5`、dry-run gate `7`、operator action `5`,owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-107 承接。 +28. 建立 owner-approved result capture readback / promotion readiness。✅ P2-107 已完成;readback digest `5`、promotion review `5`、failure lane `4`、reviewer queue preview `4`、operator action `5`,canonical runtime readback、owner approval received、readback digest generated、promotion approved、reviewer queue write、score / result capture / learning / trust / Gateway / Telegram / production write 仍為 `0 / false`。下一步 P2-108。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -775,6 +777,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence | `docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json` + `GET /api/v1/agents/agent-critic-reviewer-result-capture` | P2-105 critic / reviewer 評分與 result capture;5 張 scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 Gateway queue、不送 Telegram | | `docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json` | P2-106 owner-approved result capture dry-run schema;強制 owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部維持 `0 / false` | | `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 committed snapshot;5 個 no-write result capture template、5 個 critic / reviewer score fixture、7 個 dry-run gate、5 個 operator action;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不讀 secret、不執行 destructive action | +| `docs/schemas/ai_agent_owner_approved_result_capture_readback_v1.schema.json` | P2-107 owner-approved result capture readback schema;強制 canonical runtime readback、owner approval received、readback digest generated、promotion approved、reviewer queue write、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部維持 `0 / false` | +| `docs/evaluations/ai_agent_owner_approved_result_capture_readback_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-readback` | P2-107 committed snapshot;5 個 fixture-only readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview、5 個 operator action;不讀 canonical runtime target、不寫 reviewer queue、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不讀 secret、不執行 destructive action | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader;強制 live flags / DB / Redis / Telegram / transcript / 私有推理全部關閉 | | `GET /api/v1/agents/agent-interaction-learning-proof` | 治理 API;只回傳證據面,不啟動 worker、不碰 live DB/Redis、不發 Telegram | | `docs/schemas/ai_agent_live_read_model_gate_v1.schema.json` | P2-403B live read model gate schema;強制 DB / Redis / worker / Telegram / learning writeback 仍需批准 | @@ -1969,7 +1973,14 @@ Phase 6 完成後 - `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`,治理頁顯示 P2-106 進度 `100%`、捕捉模板 `5`、評分 fixture `5`、dry-run gate `7`、操作選項 `5`、批准缺口 `63`、失敗候選 `1`、owner approval received `0`、dry-run preview generated `0` 與 live writes `0`。 - 同步強化 P2-403J 每個 Agent 工作狀態報告:OpenClaw / Hermes / NemoTron 卡片顯示角色、工作單位、完成比例、待審核數、負責報告章節、分析建議數與 24h runtime 作業數;正式 runtime delivery 與 auto optimization 仍不得因 UI 可見而視為啟用。 - 政策裁決:P2-106 只允許在統帥批准後產生 no-write preview 與 verifier fixture;不得把批准包、score fixture 或 dry-run template 解讀成 runtime score / result capture / learning 已寫入。 -- 本波仍不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;下一步 P2-107。 +- 本波仍不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;已由 P2-107 承接。 + +### 2026-06-13 14:18 (台北) — §3.2 / §5 — 完成 P2-107 owner-approved result capture readback / promotion readiness — 把批准後 readback 與升級準備固定成只讀閘門 + +- 新增 `ai_agent_owner_approved_result_capture_readback_v1` schema / committed snapshot / loader / API / 測試,承接 P2-106 的 no-write template、score fixture 與 dry-run gate,定義 5 個 readback digest、5 個 promotion readiness review、4 條 failure lane、4 個 reviewer queue preview 與 5 個 operator action。 +- `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-owner-approved-result-capture-readback`,治理頁顯示 P2-107 進度 `100%`、readback digest `5`、promotion review `5`、failure lane `4`、reviewer queue preview `4`、操作選項 `5`、阻擋總數 `7`、owner approval received `0`、readback digest generated `0`、promotion approved `0`、reviewer queue write `0` 與 live writes `0`。 +- 政策裁決:P2-107 只允許 committed fixture readback 與 promotion readiness review;不得把 readback digest、reviewer queue preview 或 promotion review 解讀成 canonical runtime readback、reviewer queue write、Telegram 實發或 live writer 已啟用。 +- 本波仍不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;下一步 P2-108。 ### 2026-06-12 11:55 (台北) — §3.2 / §5 — 完成 P2-403M 報表 runtime no-write dry-run 證據包 — 把 queue / verifier 草案固定成可審核證據