diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 3b47bf5ee..553d2a718 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -142,6 +142,9 @@ from src.services.ai_agent_result_capture_release_decision_owner_response_prefli from src.services.ai_agent_result_capture_release_decision_owner_response_readback import ( load_latest_ai_agent_result_capture_release_decision_owner_response_readback, ) +from src.services.ai_agent_result_capture_release_decision_owner_response_acceptance_gate import ( + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate, +) from src.services.ai_agent_result_capture_owner_promotion_review import ( load_latest_ai_agent_result_capture_owner_promotion_review, ) @@ -2490,6 +2493,38 @@ async def get_agent_result_capture_release_decision_owner_response_readback() -> ) from exc +@router.get( + "/agent-result-capture-release-decision-owner-response-acceptance-gate", + response_model=dict[str, Any], + summary="取得 AI Agent result capture release decision owner response acceptance gate", + description=( + "讀取最新已提交的 P2-145 release decision owner response acceptance gate;" + "此端點只建立 P2-144 readback 後的驗收門檻," + "不把未收到或未遮罩回覆視為正式收件、接受、拒絕或 release authorization," + "不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust," + "不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。" + ), +) +async def get_agent_result_capture_release_decision_owner_response_acceptance_gate() -> dict[str, Any]: + """Return the latest read-only release decision owner-response acceptance gate.""" + try: + payload = await asyncio.to_thread( + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate + ) + 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_result_capture_release_decision_owner_response_acceptance_gate_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent result capture release decision owner response acceptance gate 無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py b/apps/api/src/services/ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py new file mode 100644 index 000000000..6cfe3a889 --- /dev/null +++ b/apps/api/src/services/ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py @@ -0,0 +1,383 @@ +""" +AI Agent result capture release decision owner-response acceptance gate snapshot. + +Loads the latest committed P2-145 owner-response acceptance gate package. This +module only validates the gate definition; it never treats a missing response as +receipt, approval, reviewer queue write, Gateway queue write, Telegram send, +Bot API call, result capture, learning, PlayBook trust, secret read, +production write, or destructive operation. +""" + +from __future__ import annotations + +import json +import re +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_result_capture_release_decision_owner_response_acceptance_gate_*.json" +_SCHEMA_VERSION = "ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1" +_RUNTIME_AUTHORITY = "result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write" + + +def load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed release decision owner-response acceptance gate.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent result capture release decision owner response acceptance gate snapshots found in {directory}" + ) + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + + label = str(latest) + _require_schema(payload, label) + _require_prior(payload, label) + _require_truth(payload, label) + _require_acceptance_gate_lanes(payload, label) + _require_validation_checks(payload, label) + _require_rejection_guards(payload, label) + _require_actions(payload, label) + _require_display_redaction(payload, label) + _require_no_forbidden_display_terms(payload, label) + _require_rollup_consistency(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + expected = { + "current_priority": "P2", + "current_task_id": "P2-145", + "next_task_id": "P2-146", + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + "overall_completion_percent": 100, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_prior(payload: dict[str, Any], label: str) -> None: + prior = payload.get("prior_owner_response_readback") or {} + expected = { + "schema_version": "ai_agent_result_capture_release_decision_owner_response_readback_v1", + "current_task_id": "P2-144", + "next_task_id": "P2-145", + "response_readback_lane_count": 5, + "required_owner_field_count": 18, + "readback_validation_check_count": 6, + "readback_rejection_guard_count": 6, + "operator_action_count": 5, + "waiting_external_response_count": 5, + "no_external_response_received_count": 5, + "approval_required_subtotal": 12, + "blocked_and_critical_subtotal": 12, + } + expected.update(_owner_response_zero_counts()) + mismatches = _mismatches(prior, expected) + if mismatches: + raise ValueError(f"{label}: prior_owner_response_readback mismatch: {mismatches}") + if not prior.get("readiness_note"): + raise ValueError(f"{label}: prior_owner_response_readback.readiness_note is required") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("owner_response_acceptance_truth") or {} + required_true = { + "p2_144_readback_loaded", + "p2_143_preflight_baseline_preserved", + "owner_response_acceptance_gate_ready", + "acceptance_gate_only_mode", + "no_acceptable_external_response", + "requires_redacted_payload", + "requires_complete_owner_fields", + "rejection_policy_preserved", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: owner response acceptance flags must remain true: {missing}") + + required_false = { + "owner_response_received", + "owner_response_accepted", + "owner_response_rejected", + "redacted_payload_ingested", + "owner_release_authorized", + "owner_release_approved", + "owner_review_approved", + "owner_decision_approved", + "verifier_decision_approved", + "maintenance_window_approved", + "rollback_owner_confirmed", + "post_release_verifier_ready", + "final_release_candidate_approved", + "final_release_candidate_passed", + "release_decision_passed", + "release_authorization_granted", + "release_authorization_passed", + "rollback_release_passed", + "live_apply_release_passed", + "writer_apply_enabled", + "execution_apply_enabled", + "receipt_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "report_receipt_write_enabled", + "result_capture_write_enabled", + "learning_write_enabled", + "playbook_trust_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled", + } + unsafe = sorted(field for field in required_false if truth.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: owner response live/send/write flags must remain false: {unsafe}") + + non_zero = sorted(field for field in _owner_response_zero_counts() if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: owner response live counters must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: owner_response_acceptance_truth.truth_note is required") + + +def _require_acceptance_gate_lanes(payload: dict[str, Any], label: str) -> None: + items = payload.get("acceptance_gate_lanes") or [] + _require_ids( + items, + "gate_id", + { + "owner_release_acceptance_gate", + "verifier_acceptance_gate", + "rollback_owner_acceptance_gate", + "maintenance_window_acceptance_gate", + "live_apply_acceptance_gate", + }, + label, + "acceptance gate lanes", + ) + for item in items: + item_id = item.get("gate_id") + expected = { + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": False, + "acceptance_passed": False, + "acceptance_rejected": False, + "redacted_payload_ingested": False, + "runtime_write_allowed": False, + "telegram_send_allowed": False, + } + mismatches = _mismatches(item, expected) + if mismatches: + raise ValueError(f"{label}: acceptance gate lane {item_id} mismatch: {mismatches}") + required_fields = item.get("required_fields") or [] + if not isinstance(required_fields, list) or not required_fields: + raise ValueError(f"{label}: acceptance gate lane {item_id}.required_fields is required") + if not item.get("source_readback_id"): + raise ValueError(f"{label}: acceptance gate lane {item_id}.source_readback_id is required") + if not item.get("acceptance_summary"): + raise ValueError(f"{label}: acceptance gate lane {item_id}.acceptance_summary is required") + if not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: acceptance gate lane {item_id}.evidence_hash must be redacted sha256") + + +def _require_validation_checks(payload: dict[str, Any], label: str) -> None: + items = payload.get("acceptance_validation_checks") or [] + _require_count(items, 6, label, "acceptance validation checks") + allowed = {"blocked_no_external_response", "not_satisfied", "blocked_by_policy"} + for item in items: + item_id = item.get("check_id") + if item.get("status") not in allowed: + raise ValueError(f"{label}: acceptance validation check {item_id} status is invalid") + if item.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: acceptance validation check {item_id} must keep runtime_write_allowed=false") + if not item.get("requirement"): + raise ValueError(f"{label}: acceptance validation check {item_id}.requirement is required") + + +def _require_rejection_guards(payload: dict[str, Any], label: str) -> None: + items = payload.get("acceptance_rejection_guards") or [] + _require_count(items, 6, label, "acceptance rejection guards") + for item in items: + item_id = item.get("guard_id") + if item.get("status") != "active": + raise ValueError(f"{label}: acceptance rejection guard {item_id} status must remain active") + if item.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: acceptance rejection guard {item_id} must keep runtime_write_allowed=false") + if not item.get("reason"): + raise ValueError(f"{label}: acceptance rejection guard {item_id}.reason is required") + + +def _require_actions(payload: dict[str, Any], label: str) -> None: + items = payload.get("operator_actions") or [] + _require_count(items, 5, label, "operator actions") + for item in items: + item_id = str(item.get("action_id")) + stale_ids = {"p2_139", "p2_140", "p2_141", "p2_142", "p2_143"} + if any(stale_id in item_id for stale_id in stale_ids): + raise ValueError(f"{label}: operator action {item_id} must not point back to stale release gates") + if item.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: operator action {item_id} must keep runtime_write_allowed=false") + if item.get("status") not in {"ready_for_operator_review", "approval_required"}: + raise ValueError(f"{label}: operator action {item_id} status is invalid") + if not item.get("operator_instruction"): + raise ValueError(f"{label}: operator action {item_id}.operator_instruction is required") + + +def _require_display_redaction(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + expected = { + "redaction_required": True, + "raw_prompt_display_allowed": False, + "private_reasoning_display_allowed": False, + "secret_value_display_allowed": False, + "raw_runtime_payload_display_allowed": False, + "internal_collaboration_content_display_allowed": False, + } + mismatches = _mismatches(contract, expected) + if mismatches: + raise ValueError(f"{label}: display_redaction_contract mismatch: {mismatches}") + if not contract.get("frontend_display_policy"): + raise ValueError(f"{label}: display_redaction_contract.frontend_display_policy is required") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + lanes = payload.get("acceptance_gate_lanes") or [] + required_owner_field_count = sum(len(item.get("required_fields") or []) for item in lanes) + expected = { + "acceptance_gate_lane_count": len(lanes), + "required_owner_field_count": required_owner_field_count, + "acceptance_validation_check_count": len(payload.get("acceptance_validation_checks") or []), + "acceptance_rejection_guard_count": len(payload.get("acceptance_rejection_guards") or []), + "operator_action_count": len(payload.get("operator_actions") or []), + "blocked_no_external_response_count": len(lanes), + "acceptance_ready_count": 0, + "no_acceptable_external_response_count": len(lanes), + "approval_required_subtotal": 12, + "blocked_and_critical_subtotal": 12, + } + expected.update(_owner_response_zero_counts()) + mismatches = _mismatches(rollups, expected) + if mismatches: + raise ValueError(f"{label}: rollups mismatch: {mismatches}") + + +def _require_no_forbidden_display_terms(payload: Any, label: str) -> None: + forbidden = { + "work_window_transcript", + "internal_collaboration_transcript", + "raw_prompt", + "private_reasoning", + "chain_of_thought", + "authorization_header", + "secret_value", + "工作視窗", + "內部協作", + "原始提示詞", + "私有推理", + "raw runtime payload", + "authorization header", + } + found = sorted(term for term in forbidden if _contains_term(payload, term)) + if found: + raise ValueError(f"{label}: forbidden display terms leaked: {found}") + + +def _contains_term(value: Any, term: str) -> bool: + if isinstance(value, str): + return term.lower() in value.lower() + if isinstance(value, dict): + return any(_contains_term(child, term) for child in value.values()) + if isinstance(value, list): + return any(_contains_term(child, term) for child in value) + return False + + +def _owner_response_zero_counts() -> dict[str, int]: + return { + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "redacted_payload_ingested_count": 0, + **_zero_count_expectations(), + } + + +def _zero_count_expectations() -> dict[str, int]: + return { + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0, + } + + +def _require_ids(items: list[dict[str, Any]], field: str, expected_ids: set[str], label: str, group_name: str) -> None: + actual_ids = {str(item.get(field)) for item in items} + if actual_ids != expected_ids: + raise ValueError(f"{label}: {group_name} ids mismatch: expected={sorted(expected_ids)} actual={sorted(actual_ids)}") + + +def _require_count(items: list[Any], expected: int, label: str, group_name: str) -> None: + if len(items) != expected: + raise ValueError(f"{label}: expected {expected} {group_name}, got {len(items)}") + + +def _is_redacted_sha256(value: Any) -> bool: + return isinstance(value, str) and re.fullmatch(r"sha256:[0-9a-f]{64}", value) is not None + + +def _mismatches(payload: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + key: {"expected": expected_value, "actual": payload.get(key)} + for key, expected_value in expected.items() + if payload.get(key) != expected_value + } diff --git a/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py b/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py new file mode 100644 index 000000000..59b46fd04 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_result_capture_release_decision_owner_response_acceptance_gate import ( + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate, +) + + +def test_load_latest_release_decision_owner_response_acceptance_gate_snapshot() -> None: + snapshot = load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate() + + assert snapshot["schema_version"] == "ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1" + assert snapshot["program_status"]["current_task_id"] == "P2-145" + assert snapshot["program_status"]["next_task_id"] == "P2-146" + assert snapshot["program_status"]["overall_completion_percent"] == 100 + assert snapshot["program_status"]["runtime_authority"] == ( + "result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write" + ) + + rollups = snapshot["rollups"] + assert rollups["acceptance_gate_lane_count"] == 5 + assert rollups["required_owner_field_count"] == 18 + assert rollups["acceptance_validation_check_count"] == 6 + assert rollups["acceptance_rejection_guard_count"] == 6 + assert rollups["operator_action_count"] == 5 + assert rollups["blocked_no_external_response_count"] == 5 + assert rollups["acceptance_ready_count"] == 0 + assert rollups["no_acceptable_external_response_count"] == 5 + assert rollups["owner_response_received_count"] == 0 + assert rollups["owner_response_accepted_count"] == 0 + assert rollups["owner_response_rejected_count"] == 0 + assert rollups["redacted_payload_ingested_count"] == 0 + assert rollups["reviewer_queue_write_count"] == 0 + assert rollups["gateway_queue_write_count"] == 0 + assert rollups["telegram_send_count"] == 0 + assert rollups["bot_api_call_count"] == 0 + assert rollups["production_write_count"] == 0 + + +def test_owner_response_acceptance_gate_keeps_all_runtime_gates_closed() -> None: + snapshot = load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate() + truth = snapshot["owner_response_acceptance_truth"] + + assert truth["p2_144_readback_loaded"] is True + assert truth["p2_143_preflight_baseline_preserved"] is True + assert truth["owner_response_acceptance_gate_ready"] is True + assert truth["acceptance_gate_only_mode"] is True + assert truth["no_acceptable_external_response"] is True + assert truth["requires_redacted_payload"] is True + assert truth["owner_response_received"] is False + assert truth["owner_response_accepted"] is False + assert truth["redacted_payload_ingested"] is False + assert truth["release_authorization_granted"] is False + assert truth["reviewer_queue_write_enabled"] is False + assert truth["gateway_queue_write_enabled"] is False + assert truth["telegram_send_enabled"] is False + assert truth["production_write_enabled"] is False + assert truth["secret_read_enabled"] is False + assert truth["destructive_operation_enabled"] is False + + +def test_rejects_missing_acceptance_gate_lane(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate()) + snapshot["acceptance_gate_lanes"] = snapshot["acceptance_gate_lanes"][:-1] + snapshot["rollups"]["acceptance_gate_lane_count"] = 4 + snapshot["rollups"]["blocked_no_external_response_count"] = 4 + snapshot["rollups"]["no_acceptable_external_response_count"] = 4 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="acceptance gate lanes ids mismatch"): + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate(tmp_path) + + +def test_rejects_owner_response_accepted_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate()) + snapshot["owner_response_acceptance_truth"]["owner_response_accepted"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="flags must remain false"): + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate(tmp_path) + + +def test_rejects_gateway_write_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate()) + snapshot["rollups"]["gateway_queue_write_count"] = 1 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollups mismatch"): + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate()) + snapshot["operator_actions"][0]["operator_instruction"] = "不要顯示 raw_prompt" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden display terms leaked"): + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate(tmp_path) + + +def test_rejects_chinese_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate()) + snapshot["operator_actions"][0]["operator_instruction"] = "不要顯示工作視窗內容" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden display terms leaked"): + load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate(tmp_path) + + +def _write_snapshot(directory: Path, payload: dict) -> None: + path = directory / "ai_agent_result_capture_release_decision_owner_response_acceptance_gate_2099-01-01.json" + path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8") diff --git a/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate_api.py b/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate_api.py new file mode 100644 index 000000000..dbd3e1ea4 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_decision_owner_response_acceptance_gate_api.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from src.main import app + + +def test_release_decision_owner_response_acceptance_gate_endpoint() -> None: + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-result-capture-release-decision-owner-response-acceptance-gate") + + assert response.status_code == 200 + payload = response.json() + assert payload["schema_version"] == "ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1" + assert payload["program_status"]["current_task_id"] == "P2-145" + assert payload["program_status"]["next_task_id"] == "P2-146" + assert payload["program_status"]["overall_completion_percent"] == 100 + assert payload["program_status"]["runtime_authority"] == ( + "result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write" + ) + assert payload["prior_owner_response_readback"]["schema_version"] == ( + "ai_agent_result_capture_release_decision_owner_response_readback_v1" + ) + assert payload["prior_owner_response_readback"]["next_task_id"] == "P2-145" + assert payload["rollups"]["acceptance_gate_lane_count"] == 5 + assert payload["rollups"]["required_owner_field_count"] == 18 + assert payload["rollups"]["acceptance_validation_check_count"] == 6 + assert payload["rollups"]["acceptance_rejection_guard_count"] == 6 + assert payload["rollups"]["operator_action_count"] == 5 + assert payload["rollups"]["blocked_no_external_response_count"] == 5 + assert payload["rollups"]["acceptance_ready_count"] == 0 + assert payload["rollups"]["no_acceptable_external_response_count"] == 5 + assert payload["rollups"]["owner_response_received_count"] == 0 + assert payload["rollups"]["owner_response_accepted_count"] == 0 + assert payload["rollups"]["redacted_payload_ingested_count"] == 0 + assert payload["rollups"]["reviewer_queue_write_count"] == 0 + assert payload["rollups"]["gateway_queue_write_count"] == 0 + assert payload["rollups"]["telegram_send_count"] == 0 + assert payload["rollups"]["bot_api_call_count"] == 0 + assert payload["rollups"]["production_write_count"] == 0 + + owner_gate = payload["acceptance_gate_lanes"][0] + assert owner_gate["gate_id"] == "owner_release_acceptance_gate" + assert owner_gate["source_readback_id"] == "owner_release_response_readback" + assert owner_gate["acceptance_status"] == "blocked_no_external_response" + assert owner_gate["required_fields"] == [ + "owner_role_team", + "owner_decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "followup_owner", + ] diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index ec3c60d79..0c76dbb7e 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -6369,6 +6369,46 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseDecisionOwnerResponseAcceptanceGate": { + "title": "P2-145 Owner response 驗收門檻", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "P2-144 回讀基線", + "truthTitle": "Owner response 驗收真相", + "laneTitle": "驗收 gate lane", + "guardTitle": "驗收拒收規則", + "actionTitle": "操作員驗收事項", + "metrics": { + "overall": "完成度", + "lanes": "驗收 gate", + "requiredFields": "必填欄位", + "validationChecks": "驗收檢查", + "rejectionGuards": "拒收規則", + "actions": "操作選項", + "blockedNoResponse": "未收件阻擋", + "acceptanceReady": "可驗收", + "noAcceptable": "無合格回覆", + "accepted": "已收 / 已驗收", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-144={value}", + "acceptanceGateOnly": "只開驗收門檻={value}", + "noAcceptableResponse": "尚無合格回覆={value}" + }, + "labels": { + "priorCurrent": "上一關目前={value}", + "priorNext": "上一關下一步={value}", + "ownerReceived": "owner 回覆收件={value}", + "redactedPayload": "遮罩 payload 納入={value}", + "gatewayWrites": "Gateway 寫入={value}", + "telegramSends": "Telegram 送出={value}", + "productionWrites": "正式寫入={value}", + "requiredFields": "必填欄位={value}", + "responseReceived": "已收件={value}", + "acceptancePassed": "驗收通過={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "warRoom": { "title": "12-Agent War Room 作戰室", "source": "產生 {generated};目前 {current};下一步 {next}", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index ec3c60d79..0c76dbb7e 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -6369,6 +6369,46 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseDecisionOwnerResponseAcceptanceGate": { + "title": "P2-145 Owner response 驗收門檻", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "P2-144 回讀基線", + "truthTitle": "Owner response 驗收真相", + "laneTitle": "驗收 gate lane", + "guardTitle": "驗收拒收規則", + "actionTitle": "操作員驗收事項", + "metrics": { + "overall": "完成度", + "lanes": "驗收 gate", + "requiredFields": "必填欄位", + "validationChecks": "驗收檢查", + "rejectionGuards": "拒收規則", + "actions": "操作選項", + "blockedNoResponse": "未收件阻擋", + "acceptanceReady": "可驗收", + "noAcceptable": "無合格回覆", + "accepted": "已收 / 已驗收", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-144={value}", + "acceptanceGateOnly": "只開驗收門檻={value}", + "noAcceptableResponse": "尚無合格回覆={value}" + }, + "labels": { + "priorCurrent": "上一關目前={value}", + "priorNext": "上一關下一步={value}", + "ownerReceived": "owner 回覆收件={value}", + "redactedPayload": "遮罩 payload 納入={value}", + "gatewayWrites": "Gateway 寫入={value}", + "telegramSends": "Telegram 送出={value}", + "productionWrites": "正式寫入={value}", + "requiredFields": "必填欄位={value}", + "responseReceived": "已收件={value}", + "acceptancePassed": "驗收通過={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "warRoom": { "title": "12-Agent War Room 作戰室", "source": "產生 {generated};目前 {current};下一步 {next}", 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 b36b22dda..ca55a354c 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 @@ -93,6 +93,7 @@ import { type AiAgentResultCaptureReleaseDecisionInputPrepSnapshot, type AiAgentResultCaptureReleaseDecisionOwnerResponsePreflightSnapshot, type AiAgentResultCaptureReleaseDecisionOwnerResponseReadbackSnapshot, + type AiAgentResultCaptureReleaseDecisionOwnerResponseAcceptanceGateSnapshot, type AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot, type AiAgentResultCaptureReleaseVerifierOwnerReviewPacketSnapshot, type AiAgentResultCaptureWriterImplementationReviewSnapshot, @@ -519,6 +520,7 @@ export function AutomationInventoryTab() { const [resultCaptureReleaseDecisionInputPrep, setResultCaptureReleaseDecisionInputPrep] = useState(null) const [resultCaptureReleaseDecisionOwnerResponsePreflight, setResultCaptureReleaseDecisionOwnerResponsePreflight] = useState(null) const [resultCaptureReleaseDecisionOwnerResponseReadback, setResultCaptureReleaseDecisionOwnerResponseReadback] = useState(null) + const [resultCaptureReleaseDecisionOwnerResponseAcceptanceGate, setResultCaptureReleaseDecisionOwnerResponseAcceptanceGate] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -600,6 +602,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentResultCaptureReleaseDecisionInputPrep(), apiClient.getAiAgentResultCaptureReleaseDecisionOwnerResponsePreflight(), apiClient.getAiAgentResultCaptureReleaseDecisionOwnerResponseReadback(), + apiClient.getAiAgentResultCaptureReleaseDecisionOwnerResponseAcceptanceGate(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -680,6 +683,7 @@ export function AutomationInventoryTab() { resultCaptureReleaseDecisionInputPrepResult, resultCaptureReleaseDecisionOwnerResponsePreflightResult, resultCaptureReleaseDecisionOwnerResponseReadbackResult, + resultCaptureReleaseDecisionOwnerResponseAcceptanceGateResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -757,6 +761,7 @@ export function AutomationInventoryTab() { setResultCaptureReleaseDecisionInputPrep(resultCaptureReleaseDecisionInputPrepResult.status === 'fulfilled' ? resultCaptureReleaseDecisionInputPrepResult.value : null) setResultCaptureReleaseDecisionOwnerResponsePreflight(resultCaptureReleaseDecisionOwnerResponsePreflightResult.status === 'fulfilled' ? resultCaptureReleaseDecisionOwnerResponsePreflightResult.value : null) setResultCaptureReleaseDecisionOwnerResponseReadback(resultCaptureReleaseDecisionOwnerResponseReadbackResult.status === 'fulfilled' ? resultCaptureReleaseDecisionOwnerResponseReadbackResult.value : null) + setResultCaptureReleaseDecisionOwnerResponseAcceptanceGate(resultCaptureReleaseDecisionOwnerResponseAcceptanceGateResult.status === 'fulfilled' ? resultCaptureReleaseDecisionOwnerResponseAcceptanceGateResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -832,6 +837,7 @@ export function AutomationInventoryTab() { resultCaptureReleaseDecisionInputPrepResult, resultCaptureReleaseDecisionOwnerResponsePreflightResult, resultCaptureReleaseDecisionOwnerResponseReadbackResult, + resultCaptureReleaseDecisionOwnerResponseAcceptanceGateResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -2087,7 +2093,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3547,6 +3553,34 @@ export function AutomationInventoryTab() { + resultCaptureReleaseDecisionOwnerResponseReadback.rollups.playbook_trust_write_count + resultCaptureReleaseDecisionOwnerResponseReadback.rollups.production_write_count ) + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateOverall = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.program_status.overall_completion_percent + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateLanes = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.acceptance_gate_lane_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateFields = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.required_owner_field_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateChecks = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.acceptance_validation_check_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateGuards = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.acceptance_rejection_guard_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateActions = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.operator_action_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateBlocked = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.blocked_no_external_response_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateNoAcceptable = resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.no_acceptable_external_response_count + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateAccepted = ( + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.owner_response_received_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.owner_response_accepted_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.redacted_payload_ingested_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.owner_response_rejected_count + ) + const resultCaptureReleaseDecisionOwnerResponseAcceptanceGateLiveWrites = ( + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.writer_apply_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.execution_apply_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.receipt_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.reviewer_queue_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.gateway_queue_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.telegram_send_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.bot_api_call_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.report_receipt_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.result_capture_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.learning_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.rollups.playbook_trust_write_count + + resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.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 @@ -8252,6 +8286,132 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.priorTitle')} +

+ {resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.prior_owner_response_readback.readiness_note} +

+
+ + + + +
+
+ +
+ {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.truthTitle')} +

+ {resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.owner_response_acceptance_truth.truth_note} +

+
+ + + + + + + + +
+
+
+ + {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.laneTitle')} +
+ {resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.acceptance_gate_lanes.slice(0, 5).map(gate => ( +
+
+ + {gate.display_name} + + +
+ + {gate.acceptance_summary} + +
+ + + + + + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.acceptance_rejection_guards.slice(0, 6).map(guard => ( +
+ {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.guardTitle')} + + {guard.guard_id} + + + {guard.reason} + + +
+ ))} +
+ + {t('resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.actionTitle')} +
+ {resultCaptureReleaseDecisionOwnerResponseAcceptanceGate.operator_actions.slice(0, 5).map(action => ( +
+
+ + {action.action_id} + + +
+ + {action.operator_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 9dff42d58..4c6780224 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -629,6 +629,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentResultCaptureReleaseDecisionOwnerResponseAcceptanceGate() { + const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-release-decision-owner-response-acceptance-gate`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -10169,6 +10174,153 @@ export interface AiAgentResultCaptureReleaseDecisionOwnerResponseReadbackSnapsho } } +interface AiAgentReleaseDecisionOwnerResponseAcceptanceGateLane { + gate_id: string + display_name: string + source_readback_id: string + owner_agent: string + current_task_id: string + target_next_task_id: string + acceptance_status: string + acceptance_decision: string + required_fields: string[] + acceptance_summary: string + response_received: boolean + acceptance_passed: boolean + acceptance_rejected: boolean + redacted_payload_ingested: boolean + runtime_write_allowed: boolean + telegram_send_allowed: boolean + evidence_hash?: string +} + +export interface AiAgentResultCaptureReleaseDecisionOwnerResponseAcceptanceGateSnapshot { + schema_version: 'ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-145' + next_task_id: 'P2-146' + read_only_mode: true + runtime_authority: 'result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_owner_response_readback: AiAgentReleaseDecisionNextHandoffZeroCounts & { + schema_version: string + current_task_id: string + next_task_id: string + response_readback_lane_count: number + required_owner_field_count: number + readback_validation_check_count: number + readback_rejection_guard_count: number + operator_action_count: number + waiting_external_response_count: number + no_external_response_received_count: number + approval_required_subtotal: number + blocked_and_critical_subtotal: number + readiness_note: string + owner_response_received_count: number + owner_response_accepted_count: number + owner_response_rejected_count: number + redacted_payload_ingested_count: number + } + owner_response_acceptance_truth: AiAgentReleaseDecisionNextHandoffZeroCounts & { + p2_144_readback_loaded: boolean + p2_143_preflight_baseline_preserved: boolean + owner_response_acceptance_gate_ready: boolean + acceptance_gate_only_mode: boolean + no_acceptable_external_response: boolean + requires_redacted_payload: boolean + requires_complete_owner_fields: boolean + rejection_policy_preserved: boolean + truth_note: string + owner_response_received: boolean + owner_response_accepted: boolean + owner_response_rejected: boolean + redacted_payload_ingested: boolean + owner_release_authorized: boolean + owner_release_approved: boolean + owner_review_approved: boolean + owner_decision_approved: boolean + verifier_decision_approved: boolean + maintenance_window_approved: boolean + rollback_owner_confirmed: boolean + post_release_verifier_ready: boolean + final_release_candidate_approved: boolean + final_release_candidate_passed: boolean + release_decision_passed: boolean + release_authorization_granted: boolean + release_authorization_passed: boolean + rollback_release_passed: boolean + live_apply_release_passed: boolean + writer_apply_enabled: boolean + execution_apply_enabled: boolean + receipt_write_enabled: boolean + reviewer_queue_write_enabled: boolean + gateway_queue_write_enabled: boolean + telegram_send_enabled: boolean + bot_api_call_enabled: boolean + report_receipt_write_enabled: boolean + result_capture_write_enabled: boolean + learning_write_enabled: boolean + playbook_trust_write_enabled: boolean + production_write_enabled: boolean + secret_read_enabled: boolean + destructive_operation_enabled: boolean + owner_response_received_count: number + owner_response_accepted_count: number + owner_response_rejected_count: number + redacted_payload_ingested_count: number + } + acceptance_gate_lanes: AiAgentReleaseDecisionOwnerResponseAcceptanceGateLane[] + acceptance_validation_checks: { + check_id: string + status: string + requirement: string + runtime_write_allowed: boolean + }[] + acceptance_rejection_guards: { + guard_id: string + status: string + reason: string + runtime_write_allowed: boolean + }[] + operator_actions: { + action_id: string + owner_agent: string + status: string + operator_instruction: string + runtime_write_allowed: boolean + }[] + display_redaction_contract: { + redaction_required: boolean + raw_prompt_display_allowed: boolean + private_reasoning_display_allowed: boolean + secret_value_display_allowed: boolean + raw_runtime_payload_display_allowed: boolean + internal_collaboration_content_display_allowed: boolean + frontend_display_policy: string + } + rollups: AiAgentReleaseDecisionNextHandoffZeroCounts & { + acceptance_gate_lane_count: number + required_owner_field_count: number + acceptance_validation_check_count: number + acceptance_rejection_guard_count: number + operator_action_count: number + blocked_no_external_response_count: number + acceptance_ready_count: number + no_acceptable_external_response_count: number + approval_required_subtotal: number + blocked_and_critical_subtotal: number + owner_response_received_count: number + owner_response_accepted_count: number + owner_response_rejected_count: number + redacted_payload_ingested_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 f1b578d06..2e9dd2366 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,34 @@ +## 2026-06-14|P2-145 Owner response 驗收門檻本地完成 + +**背景**:P2-144 owner response readback 已正式驗證完成;本輪承接 `P2-144 -> P2-145`,建立 owner response acceptance gate。此階段只定義驗收門檻與拒收規則,尚未收到合格外部回覆,不代表正式收件、驗收通過、release authorization、maintenance window approval、Gateway / Telegram / reviewer queue 寫入或 runtime writer。 + +**完成項目**: +- 新增 `ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1` committed snapshot、service guard、API endpoint `GET /api/v1/agents/agent-result-capture-release-decision-owner-response-acceptance-gate`、API/service tests、API client 型別與 governance automation inventory P2-145 區塊。 +- P2-145 snapshot 固定 acceptance gate lane `5`、required owner field `18`、acceptance validation check `6`、acceptance rejection guard `6`、operator action `5`、blocked no external response `5`、no acceptable external response `5`。 +- owner response received / accepted / rejected、redacted payload ingested、owner release authorized / approved、owner review approved、owner decision approved、verifier decision approved、maintenance window approved、rollback owner confirmed、release decision passed、release authorization granted、rollback release passed、live apply release passed、reviewer queue write、Gateway queue write、Telegram send、Bot API call、result capture write、learning write、PlayBook trust write、production write、secret read、destructive operation 全部維持 `0 / false`。 +- Governance automation inventory 頁新增 P2-145 卡片,顯示 P2-144 回讀基線、驗收真相、五條驗收 gate lane、拒收規則與操作員驗收事項;前端只顯示產品化資安狀態與已遮罩 metadata,不放非公開工作溝通內容。 + +**本地驗證**: +- JSON parse:P2-145 snapshot、`zh-TW.json`、`en.json` 通過。 +- Python 編譯:P2-145 loader 與 `agents.py` 通過。 +- API/service pytest:P2-144 + P2-145 owner response chain regression `16 passed`。 +- Web typecheck:`pnpm --filter @awoooi/web typecheck` 通過。 +- Guard:`SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`、`SECURITY_MIRROR_PROGRESS_GUARD_OK`、`DOC_SECRET_SANITY_OK scanned_files=808`、`git diff --check` 通過。 +- P2-145 snapshot / messages 顯示值工作視窗污染掃描:`PUBLIC_DISPLAY_VALUE_WORK_WINDOW_SCAN_OK`;`zh-TW` / `en` i18n key parity:`I18N_KEY_PARITY_OK`。 + +**安全邊界**: +- P2-145 仍只是 owner response acceptance gate;不得把 gate 可見、UI 顯示、AwoooP approval、CD success、API readback 或 smoke pass 解讀成 owner response received / accepted、owner decision approved、verifier decision approved、maintenance window approved、release decision passed、release authorization granted、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、result capture write、learning write、PlayBook trust write、production write、secret read 或 destructive operation。 +- 本輪沒有 SSH 修改主機、Nginx reload、Docker restart、firewall 變更、K8s live write、active scan、secret 明文收集、force push 或 destructive git。 + +**完成度**: +- P2-145 owner response acceptance gate local slice:`100%`。 +- P2-145 production verification:`0%`。 +- active runtime gate、Telegram 實發、reviewer / Gateway queue write、production write 仍維持 `0`。 + +**下一步**: +- 推送 feature commit;等待 Gitea code-review / CD,deploy marker 生效後執行 production API readback、desktop / mobile smoke、in-app browser sanity 與水平溢位檢查。 +- 正式驗證通過後,`P2-146` 才能承接 acceptance receipt preview;收到合格、遮罩、欄位完整、可驗證來源的外部正式回覆前,所有 received / accepted / rejected count 不得假性拉高。 + ## 2026-06-14|P2-144 owner response 回讀後 recovery readback 無回歸 **背景**:平行協作流程已完成 P2-144 Owner response 回讀狀態正式驗證;本輪提交前 `gitea/main` 已前進至 `180a6543 chore(cd): deploy fef94df [skip ci]`。最新 live image 來自 feature commit `fef94df8 feat(platform): 擴充 Tenants 全域產品資產台帳`;P2-144 endpoint 仍由 `8795f100 feat(governance): 新增 P2-144 owner response 回讀` 提供 readback。本輪只從 reboot / cold-start / backup recovery 角度做只讀 readback,不重複修改 P2-144 正式驗證、MASTER 或 AI worklist。 diff --git a/docs/evaluations/ai_agent_result_capture_release_decision_owner_response_acceptance_gate_2026-06-14.json b/docs/evaluations/ai_agent_result_capture_release_decision_owner_response_acceptance_gate_2026-06-14.json new file mode 100644 index 000000000..68d1a35b4 --- /dev/null +++ b/docs/evaluations/ai_agent_result_capture_release_decision_owner_response_acceptance_gate_2026-06-14.json @@ -0,0 +1,415 @@ +{ + "schema_version": "ai_agent_result_capture_release_decision_owner_response_acceptance_gate_v1", + "generated_at": "2026-06-14T15:55:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-145", + "next_task_id": "P2-146", + "read_only_mode": true, + "runtime_authority": "result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write", + "status_note": "P2-145 只建立 owner response acceptance gate;目前沒有合格外部回覆可驗收,所有收件、接受、拒絕、寫入、通知與正式套用仍維持 0。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_result_capture_release_decision_owner_response_readback_2026-06-14.json", + "docs/evaluations/ai_agent_result_capture_release_decision_owner_response_preflight_2026-06-14.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#65-建立-owner-response-readback", + "docs/HARD_RULES.md#iwooos-security-governance" + ], + "prior_owner_response_readback": { + "schema_version": "ai_agent_result_capture_release_decision_owner_response_readback_v1", + "current_task_id": "P2-144", + "next_task_id": "P2-145", + "response_readback_lane_count": 5, + "required_owner_field_count": 18, + "readback_validation_check_count": 6, + "readback_rejection_guard_count": 6, + "operator_action_count": 5, + "waiting_external_response_count": 5, + "no_external_response_received_count": 5, + "approval_required_subtotal": 12, + "blocked_and_critical_subtotal": 12, + "readiness_note": "P2-144 已讀回五條 owner response lane 仍未收到外部正式回覆;P2-145 只能建立驗收門檻,不得把空回覆、口頭批准、AwoooP approval、UI 可見或部署成功視為通過。", + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "redacted_payload_ingested_count": 0, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + }, + "owner_response_acceptance_truth": { + "p2_144_readback_loaded": true, + "p2_143_preflight_baseline_preserved": true, + "owner_response_acceptance_gate_ready": true, + "acceptance_gate_only_mode": true, + "no_acceptable_external_response": true, + "requires_redacted_payload": true, + "requires_complete_owner_fields": true, + "rejection_policy_preserved": true, + "truth_note": "P2-145 只把下一關 acceptance gate 固定成可驗收門檻;目前沒有合格外部正式回覆,因此不得標記收件、接受、拒絕、授權或任何 runtime 寫入。", + "owner_response_received": false, + "owner_response_accepted": false, + "owner_response_rejected": false, + "redacted_payload_ingested": false, + "owner_release_authorized": false, + "owner_release_approved": false, + "owner_review_approved": false, + "owner_decision_approved": false, + "verifier_decision_approved": false, + "maintenance_window_approved": false, + "rollback_owner_confirmed": false, + "post_release_verifier_ready": false, + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "release_decision_passed": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "rollback_release_passed": false, + "live_apply_release_passed": false, + "writer_apply_enabled": false, + "execution_apply_enabled": false, + "receipt_write_enabled": false, + "reviewer_queue_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "report_receipt_write_enabled": false, + "result_capture_write_enabled": false, + "learning_write_enabled": false, + "playbook_trust_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "destructive_operation_enabled": false, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "redacted_payload_ingested_count": 0, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + }, + "acceptance_gate_lanes": [ + { + "gate_id": "owner_release_acceptance_gate", + "display_name": "Owner release 驗收門檻", + "source_readback_id": "owner_release_response_readback", + "owner_agent": "openclaw", + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "required_fields": [ + "owner_role_team", + "owner_decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "followup_owner" + ], + "acceptance_summary": "必須收到已遮罩且欄位完整的 owner release 正式回覆後才可驗收;目前沒有可驗收資料。", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "redacted_payload_ingested": false, + "runtime_write_allowed": false, + "telegram_send_allowed": false, + "evidence_hash": "sha256:06d60cf44c6e852f4b9ed15fc9c0c4d3b5ab168cbe3253aa0f08ec2a391a9e64" + }, + { + "gate_id": "verifier_acceptance_gate", + "display_name": "Verifier 驗收門檻", + "source_readback_id": "verifier_response_readback", + "owner_agent": "openclaw", + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "required_fields": ["verifier_scope", "verification_criteria", "failure_escalation"], + "acceptance_summary": "必須收到 verifier scope、criteria 與 escalation 正式回覆後才可驗收;不得用頁面可見或 smoke pass 取代。", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "redacted_payload_ingested": false, + "runtime_write_allowed": false, + "telegram_send_allowed": false, + "evidence_hash": "sha256:9a6a1b0e30865c9a70d08a928934f2d8e2d1ef963f61ae6e069ff21cfb31a6e5" + }, + { + "gate_id": "rollback_owner_acceptance_gate", + "display_name": "Rollback owner 驗收門檻", + "source_readback_id": "rollback_owner_response_readback", + "owner_agent": "hermes", + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "required_fields": ["rollback_owner", "rollback_condition", "rollback_validation_plan"], + "acceptance_summary": "必須收到 rollback owner、回滾條件與驗證計畫後才可驗收;目前 rollback owner 仍未確認。", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "redacted_payload_ingested": false, + "runtime_write_allowed": false, + "telegram_send_allowed": false, + "evidence_hash": "sha256:651ca2f5f52aa3f2c59d1842df417171c263120243783635efdf5d3aaad94f3a" + }, + { + "gate_id": "maintenance_window_acceptance_gate", + "display_name": "維護窗口驗收門檻", + "source_readback_id": "maintenance_window_response_readback", + "owner_agent": "openclaw", + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "required_fields": ["maintenance_window", "impact_scope", "notification_plan"], + "acceptance_summary": "必須收到維護時間、影響範圍與通知計畫後才可驗收;目前不得批准或通知。", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "redacted_payload_ingested": false, + "runtime_write_allowed": false, + "telegram_send_allowed": false, + "evidence_hash": "sha256:85a3f2784b5dc7a0ea5ca2fb49f5a60b9e8e4c2d41718c8df5dd4d8708af38db" + }, + { + "gate_id": "live_apply_acceptance_gate", + "display_name": "正式套用驗收門檻", + "source_readback_id": "live_apply_response_readback", + "owner_agent": "openclaw", + "current_task_id": "P2-145", + "target_next_task_id": "P2-146", + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "required_fields": ["live_apply_strategy", "stop_condition", "acceptance_evidence"], + "acceptance_summary": "必須收到正式套用策略、停止條件與驗收證據後才可驗收;目前不得釋放 writer 或 production write。", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "redacted_payload_ingested": false, + "runtime_write_allowed": false, + "telegram_send_allowed": false, + "evidence_hash": "sha256:8ef7d2c54bc3f14d1f0a7f40109b1d414da932c7d18cdff5b7c1228e88f58871" + } + ], + "acceptance_validation_checks": [ + { + "check_id": "check_external_owner_response_exists", + "status": "blocked_no_external_response", + "requirement": "必須有外部正式 owner response 才能進入驗收;目前所有 lane 都沒有合格回覆。", + "runtime_write_allowed": false + }, + { + "check_id": "check_required_owner_fields_complete", + "status": "not_satisfied", + "requirement": "owner role / team、decision、decision reason、affected scope、redacted evidence refs、followup owner 必須完整。", + "runtime_write_allowed": false + }, + { + "check_id": "check_redacted_evidence_refs_verifiable", + "status": "not_satisfied", + "requirement": "evidence refs 必須遮罩且可追溯,不得包含 secret value 或可反推敏感資料的內容。", + "runtime_write_allowed": false + }, + { + "check_id": "check_verifier_and_rollback_ready", + "status": "not_satisfied", + "requirement": "verifier criteria、failure escalation、rollback owner、rollback condition 與 validation plan 必須齊備。", + "runtime_write_allowed": false + }, + { + "check_id": "check_maintenance_window_ready", + "status": "not_satisfied", + "requirement": "維護時間、影響範圍與通知計畫必須可驗收;否則不能進入 release acceptance。", + "runtime_write_allowed": false + }, + { + "check_id": "check_no_runtime_execution_request", + "status": "blocked_by_policy", + "requirement": "若回覆要求直接 restart、reload、active scan、firewall / Nginx 變更或 production apply,必須拒收。", + "runtime_write_allowed": false + } + ], + "acceptance_rejection_guards": [ + { + "guard_id": "reject_missing_external_owner_response", + "status": "active", + "reason": "沒有外部正式 owner response 時不得建立 acceptance receipt。", + "runtime_write_allowed": false + }, + { + "guard_id": "reject_unredacted_payload", + "status": "active", + "reason": "未遮罩 payload、secret、token、cookie、private key 或驗證標頭必須拒收。", + "runtime_write_allowed": false + }, + { + "guard_id": "reject_incomplete_required_owner_fields", + "status": "active", + "reason": "必填 owner / verifier / rollback / maintenance / live-apply 欄位不完整時不得驗收。", + "runtime_write_allowed": false + }, + { + "guard_id": "reject_unverified_evidence_refs", + "status": "active", + "reason": "evidence refs 不可驗證或可反推出敏感資料時必須拒收。", + "runtime_write_allowed": false + }, + { + "guard_id": "reject_runtime_execution_request", + "status": "active", + "reason": "任何要求 runtime execution、host change、Nginx/firewall 改動、active scan 或 production apply 的回覆都不得驗收。", + "runtime_write_allowed": false + }, + { + "guard_id": "reject_security_approval_confusion", + "status": "active", + "reason": "AwoooP approval、CD success、UI 可見、smoke pass 或一般任務批准不得替代 IwoooS security owner response。", + "runtime_write_allowed": false + } + ], + "operator_actions": [ + { + "action_id": "prepare_p2_146_acceptance_receipt_preview", + "owner_agent": "openclaw", + "status": "approval_required", + "operator_instruction": "若未來收到完整且遮罩的 owner response,P2-146 才能建立 acceptance receipt preview;目前保持 0 寫入。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_acceptance_payload_redaction", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "先檢查 evidence refs 與 payload 是否已遮罩;不得讀取或保存 secret value。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_rollback_and_maintenance_scope", + "owner_agent": "openclaw", + "status": "approval_required", + "operator_instruction": "驗收前必須確認 rollback owner、maintenance window、impact scope 與 notification plan 都存在。", + "runtime_write_allowed": false + }, + { + "action_id": "keep_gateway_telegram_writes_zero", + "owner_agent": "nemotron", + "status": "ready_for_operator_review", + "operator_instruction": "P2-145 只顯示 gate,不寫 reviewer queue、Gateway queue、不發 Telegram、不呼叫 Bot API。", + "runtime_write_allowed": false + }, + { + "action_id": "preserve_acceptance_gate_zero_truth", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "驗收前所有 received / accepted / rejected / production write counter 必須維持 0。", + "runtime_write_allowed": false + } + ], + "display_redaction_contract": { + "redaction_required": true, + "raw_prompt_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_runtime_payload_display_allowed": false, + "internal_collaboration_content_display_allowed": false, + "frontend_display_policy": "治理頁只能顯示 acceptance gate 狀態、遮罩 evidence hash 與 0 寫入邊界;不得顯示非公開工作內容、敏感值、未遮罩 runtime payload 或可反推 secret 的資料。" + }, + "rollups": { + "acceptance_gate_lane_count": 5, + "required_owner_field_count": 18, + "acceptance_validation_check_count": 6, + "acceptance_rejection_guard_count": 6, + "operator_action_count": 5, + "blocked_no_external_response_count": 5, + "acceptance_ready_count": 0, + "no_acceptable_external_response_count": 5, + "approval_required_subtotal": 12, + "blocked_and_critical_subtotal": 12, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "redacted_payload_ingested_count": 0, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md index 90deaef24..792f3ce81 100644 --- a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md +++ b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md @@ -2,11 +2,11 @@ | 項目 | 內容 | |------|------| -| 日期 | 2026-06-04 | -| 狀態 | IwoooS 64% 只讀治理推進中;S4.9 owner response gate 是第一優先 | +| 日期 | 2026-06-14 | +| 狀態 | IwoooS 64% 只讀治理推進中;P2-145 owner response acceptance gate 本地完成,正式驗證待部署;S4.9 owner response gate 仍是第一優先 | | 本階段完成 | 資安供應鏈 contract manifest + Source Control Approval Board + Draft Reconcile Plan + Ref Detail Diff + Ref Truth Classification + Source Control Ref Truth Owner Response 收件包 + GitHub Primary Readiness Gate + GitHub Primary Rollback ADR + GitHub Target Owner Decision Response 收件包 + Gitea 認證清冊匯出請求 + Gitea 認證清冊匯入驗收契約 + Gitea 清冊覆蓋 Owner Attestation + Gitea Owner Attestation Approval Lane 對齊 + Gitea Owner Attestation Response 收件包 + Workflow / Secret Name Inventory + Workflow / Secret Name Local Evidence + Workflow / Secret Name Redacted Export Request + Workflow / Secret Name Owner Response 收件包 + Source Control Owner Response Validation Rollup + Kali 112 live integration status + Security Finding contract + Kali scan scope approval package + Security Approval Queue + S3 人工批准 Gate + S3 人工決策紀錄 + S3 人工審查封包 + S3 人工決策狀態轉移 + S3 後續 runtime gate 準備契約 + 鏡像 readiness index + 鏡像接收計畫 + 鏡像事件信封 + 鏡像路由矩陣 + 鏡像驗收契約 + 鏡像隔離契約 + 鏡像 dry-run 報告契約 + 鏡像狀態彙整契約 + IwoooS 前端態勢入口 + IwoooS posture projection contract + IwoooS 既有前端資安頁面整合 + IwoooS 覆蓋與邊界矩陣 + IwoooS 只讀資安處理旅程 + IwoooS owner evidence readiness board + IwoooS host coverage view + IwoooS host action gate matrix + IwoooS host evidence readiness board + IwoooS host evidence collection order + IwoooS host evidence intake preflight + IwoooS host evidence review outcome lanes + IwoooS host evidence review handoff packets + IwoooS host evidence reviewer checklist + IwoooS host evidence reviewer outcome lanes + IwoooS host owner decision candidate packets + IwoooS host owner decision review checklist + IwoooS host owner decision review outcome lanes + IwoooS host owner decision record draft packets + IwoooS host owner decision record draft review checklist + IwoooS host owner decision record draft review outcome lanes + IwoooS host owner decision record write-up packets + IwoooS host owner decision record write-up review checklist + IwoooS host owner decision record write-up review outcome lanes + IwoooS host owner decision record formal candidate packets + IwoooS host owner decision record formal candidate review checklist + IwoooS host owner decision record formal candidate review outcome lanes + IwoooS host owner decision record formal record queue packets + IwoooS host owner decision record formal record queue review checklist + IwoooS host owner decision record formal record queue review outcome lanes + IwoooS host owner decision record human handoff readiness packets + IwoooS host owner decision record human handoff readiness review checklist + IwoooS host owner decision record human handoff readiness review outcome lanes + IwoooS host owner decision record human record owner review candidate packets + IwoooS host owner decision record human record owner review candidate checklist + IwoooS host owner decision record human record owner review candidate outcome lanes + IwoooS host owner decision record human record owner review preparation packets + IwoooS host owner decision record human record owner review preparation checklist + IwoooS progress acceleration lanes + IwoooS owner response next-action focus + IwoooS S4.9 owner response preflight + IwoooS S4.9 owner response request templates + IwoooS progress hold movement gates + IwoooS AwoooP read-only landing readiness + IwoooS AwoooP cross-session handoff packets + AwoooP 首頁 IwoooS 資安鏡像候選 + AwoooP 工作鏈路 IwoooS 資安鏡像候選 + AwoooP 審批佇列 IwoooS owner response 只讀焦點 | | 本階段追加 | AwoooP 合約儀表板 IwoooS 資安契約只讀候選 + AwoooP 租戶管理 IwoooS 資安租戶範圍只讀候選 + AwoooP 執行監控 IwoooS 執行狀態只讀候選 + 既有安全 / 合規頁面 IwoooS 只讀反向橋接 + 告警 / 錯誤 / 授權 / 治理頁面 IwoooS 只讀反向橋接 + 稽核 / 工程審查頁面 IwoooS 深色只讀反向橋接 + IwoooS 前端資安頁面連接狀態板 + IwoooS GitHub 主要來源就緒度只讀狀態板 + AwoooP 工作鏈路 GitHub 主要來源就緒度只讀工作項 + AwoooP 合約儀表板 GitHub 主要來源就緒度合約只讀候選 + AwoooP 審批佇列 GitHub 主要來源就緒度審批邊界 + AwoooP 首頁 GitHub 主要來源就緒度只讀摘要 + AwoooP 租戶管理 GitHub 主要來源就緒度租戶範圍 + AwoooP 執行監控 GitHub 主要來源就緒度執行邊界 + IwoooS / AwoooP 資安可視區塊繁體中文呈現防護檢查 + AwoooP 執行詳情 / 審批詳情繁體中文呈現防護檢查 + AwoooP 首頁負責人回覆驗收總覽 + AwoooP 工作鏈路負責人回覆驗收只讀工作項 + AwoooP 合約儀表板負責人回覆驗收契約只讀候選 + AwoooP 審批佇列負責人回覆驗收只讀審查邊界 + AwoooP 租戶管理負責人回覆驗收租戶範圍 + AwoooP 執行監控負責人回覆驗收執行邊界 + AwoooP 執行詳情負責人回覆驗收詳情邊界 + AwoooP 審批決策負責人回覆驗收審批邊界 + IwoooS AwoooP 資安入口覆蓋狀態板 + IwoooS 階段式資安收斂節奏圖 + IwoooS 下一步人工收件作戰板 + IwoooS 人工回覆安全驗收閘道 + IwoooS 人工回覆審查結果分流 + IwoooS 人工決策準備佇列 + IwoooS 人工決策紀錄草稿防誤用 + IwoooS 人工決策正式紀錄負責人指派確認準備包 + IwoooS 人工決策正式紀錄負責人指派確認清單 + IwoooS 人工決策正式紀錄負責人指派確認結果分流 + IwoooS 人工決策正式紀錄負責人指派決策準備包 + IwoooS 人工決策正式紀錄負責人指派決策檢查清單 + IwoooS S4.9 負責人回覆封套欄位 + IwoooS S4.9 負責人回覆封套送件前檢查 + IwoooS S4.9 負責人回覆封套送件前結果分流 + IwoooS S4.9 負責人回覆送件請求草稿 + IwoooS S4.9 負責人回覆送件鏈路摘要 + IwoooS 低摩擦分階段收斂主控 + IwoooS 低摩擦下一步行動邊界 + IwoooS 64% 進度移動訊號驗收條 + IwoooS 第一個進度解鎖路徑 + IwoooS 第一解鎖證據包 + IwoooS 第一解鎖證據包預檢分流 + IwoooS 第一解鎖證據包補件路徑 + IwoooS 第一解鎖證據包補件送審前檢查 + IwoooS 第一解鎖證據包補件送審結果分流 + IwoooS 第一解鎖證據包 reviewer 指派準備包 + IwoooS 第一解鎖證據包 reviewer 指派前檢查 + IwoooS 第一解鎖證據包 reviewer 指派前檢查結果分流 + IwoooS 正式只讀 landing 與 Kali 112 只讀證據進度重估 | -| 本階段追加補充 | IwoooS 目前具體工作地圖 + IwoooS 目前具體交付清單 + IwoooS 目前阻塞與解除條件 + IwoooS 三軸進度與全產品套用範圍 + IwoooS 全產品分階段套用台帳 + IwoooS 全產品 rollout 波次驗收門檻 + IwoooS 全產品 rollout 驗收結果分流 + IwoooS 全產品證據接線地圖 + IwoooS 全產品證據接線預檢 + IwoooS 全產品證據接線預檢結果分流 + IwoooS 全產品預檢補件回收台帳 + IwoooS 全產品補件重試門檻 + IwoooS 全產品重試結果分流 + IwoooS 全產品人工審查候選準備 + IwoooS 全產品人工審查候選預檢 + IwoooS 全產品人工審查候選預檢結果分流 + IwoooS 全產品人工審查候選預檢補件回收台帳 + IwoooS 全產品人工審查候選預檢補件重試門檻 + IwoooS 全產品只讀套用快照 | +| 本階段追加補充 | IwoooS 目前具體工作地圖 + IwoooS 目前具體交付清單 + IwoooS 目前阻塞與解除條件 + IwoooS 三軸進度與全產品套用範圍 + IwoooS 全產品分階段套用台帳 + IwoooS 全產品 rollout 波次驗收門檻 + IwoooS 全產品 rollout 驗收結果分流 + IwoooS 全產品證據接線地圖 + IwoooS 全產品證據接線預檢 + IwoooS 全產品證據接線預檢結果分流 + IwoooS 全產品預檢補件回收台帳 + IwoooS 全產品補件重試門檻 + IwoooS 全產品重試結果分流 + IwoooS 全產品人工審查候選準備 + IwoooS 全產品人工審查候選預檢 + IwoooS 全產品人工審查候選預檢結果分流 + IwoooS 全產品人工審查候選預檢補件回收台帳 + IwoooS 全產品人工審查候選預檢補件重試門檻 + IwoooS 全產品只讀套用快照 + P2-145 owner response acceptance gate 本地完成 | | 原則 | 低摩擦分階段;文件、schema、read-only evidence 優先;不做 runtime enforcement、不切 primary | | P0 主控板 | `docs/workplans/2026-06-04-iwooos-security-governance-p0.md` | @@ -382,6 +382,7 @@ headline 要再往上,需要 S4.9 / S4.10 / S4.11 / S4.12 任一 owner respons | S2.139 IwoooS 全產品人工審查候選預檢補件回收台帳 | 完成草案 | `/iwooos` 新增全產品人工審查候選預檢補件回收台帳,列出識別、來源、範圍、脫敏、版本、主機、工具摘要與 runtime 聲明八個 recovery queues | 使用者能直接理解退回、隔離與暫停後要補什麼、由誰補、怎麼回到預檢;目前 submitted / accepted / ready for preflight retry / reviewer candidate / audit emitted / ready for runtime 仍為 0,所以 headline 仍不提高 | | S2.140 IwoooS 全產品人工審查候選預檢補件重試門檻 | 完成草案 | `/iwooos` 新增全產品人工審查候選預檢補件重試門檻,列出識別、來源、範圍、脫敏、版本、主機窗口、工具摘要與 runtime 分離八個 retry gates | 使用者能直接理解補件回收後何時可以重新回到 S2.137 預檢;目前 retry candidate / submitted / passed / ready for preflight retry / reviewer candidate / audit emitted / ready for runtime 仍為 0,所以 headline 仍不提高 | | S2.141 IwoooS 全產品只讀套用快照 | 完成草案 | `/iwooos` 預設展開區改放全產品 compact snapshot,六類產品狀態一屏可讀,完整三軸與 rollout 明細留在進階收合區 | 使用者能直接理解「所有專案產品都有套用,但目前只讀、低摩擦、runtime ready 仍 0」;這是 UI/UX 與可理解性推進,headline 仍不提高 | +| P2-145 AI Agent Owner Response 驗收門檻 | 本地完成,正式驗證待部署 | `/governance?tab=automation-inventory` 新增 P2-145 owner response acceptance gate 卡片,顯示 P2-144 回讀基線、5 條驗收 gate lane、6 個驗收檢查、6 個拒收規則、5 個操作事項與 received / accepted / rejected / Gateway / Telegram / production write 全部 0 | 使用者與另一個 AwoooP Session 能確認下一關只建立驗收門檻;未收到合格、遮罩、欄位完整、可驗證來源的外部正式回覆前,不得建立 acceptance receipt、不得進 reviewer / Gateway queue、不得發 Telegram、不得開 runtime gate,headline 仍不提高 | | S3 approval gate | 進行中 | `security_approval_gate_v1` 已建立 8 個人工 gate items:7 pending、1 block candidate、0 approved | 不得繞過人工批准;批准後仍需 follow-up runtime gate | | S3.0 人工批准 Gate 契約 | 完成草案 | 定義批准範圍、決策選項、required reviewers、still forbidden 與 follow-up runtime gate | AwoooP 可記錄決策,不可執行 gate item | | S3.1 人工決策紀錄契約 | 完成草案 | `security_approval_decision_record_v1` 已建立;目前 0 筆 decision records、0 個 runtime action 授權 | AwoooP 可稽核決策,不可把決策當執行 | 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 388727db8..26500983c 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 @@ -682,6 +682,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_12_agent_war_room_2026-06-14.json` + `GET /api/v1/agents/agent-12-agent-war-room` | P2-142 12-Agent War Room readback;建立 12 個邏輯工位、12 份 read-only review、總工作量 `82`、evidence `84`、需批准 `61`、阻擋項 `54`、市場 refresh candidate `5`、日週月報 cadence `3`;runtime authority 固定 `12_agent_war_room_read_only_no_live_write`;live write、Telegram send、Bot API、production write、paid API、SDK install、secret read 與 destructive operation 全部 `0 / false`;feature commit `5de4b3f3` 與 deploy marker `1a2c9e36` 已進 `gitea/main`,本地 API/service pytest、JSON parse、Python compile、Web typecheck、Web production build、guard、doc secret sanity 與公開 payload 目標掃描通過;production API readback、desktop / mobile smoke、水平溢位 `0`、危險控制 `0`、工作溝通片語命中 `0` 已完成,已由 P2-143 承接 | | `docs/evaluations/ai_agent_result_capture_release_decision_owner_response_preflight_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-decision-owner-response-preflight` | P2-143 owner response preflight;承接 P2-141 decision input prep 與 P2-142 War Room 基線,建立 5 條 response intake lane、18 個 required owner field、6 個 intake validation check、6 個 rejection guard、5 個 operator action 與 5 個 waiting external response;runtime authority 固定 `result_capture_release_decision_owner_response_preflight_only_no_live_write`;owner response received / accepted / rejected、redacted payload ingested、owner decision approved、verifier decision approved、maintenance window approved、release decision passed、release authorization granted、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、本地 regression `37 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、禁用外露值掃描、production API readback、desktop / mobile smoke、水平溢位 `0`、危險控制 `0`、工作溝通片語命中 `0` 已完成,下一步 P2-144 owner response readback | | `docs/evaluations/ai_agent_result_capture_release_decision_owner_response_readback_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback` | P2-144 owner response readback;承接 P2-143 preflight,建立 5 條 response readback lane、18 個 required owner field、6 個 readback validation check、6 個 readback rejection guard、5 個 operator action、5 個 waiting external response 與 5 個 no external response received lane;runtime authority 固定 `result_capture_release_decision_owner_response_readback_only_no_live_write`;owner response received / accepted / rejected、redacted payload ingested、owner decision approved、verifier decision approved、maintenance window approved、release decision passed、release authorization granted、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、本地 P2-139 至 P2-144 regression `45 passed`,rebase 後含 tenants regression 的推送前回歸 `47 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、value-only 禁用外露值掃描、i18n key parity 與 diff check 通過;production API readback、desktop / mobile smoke、水平溢位 `0`、危險控制 `0`、工作溝通片語命中 `0` 已完成,下一步 P2-145 owner response acceptance gate | +| `docs/evaluations/ai_agent_result_capture_release_decision_owner_response_acceptance_gate_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-decision-owner-response-acceptance-gate` | P2-145 owner response acceptance gate;承接 P2-144 readback,建立 5 條 acceptance gate lane、18 個 required owner field、6 個 acceptance validation check、6 個 acceptance rejection guard、5 個 operator action、5 個 blocked no external response 與 5 個 no acceptable external response;runtime authority 固定 `result_capture_release_decision_owner_response_acceptance_gate_only_no_live_write`;owner response received / accepted / rejected、redacted payload ingested、owner release authorized / approved、owner review approved、owner decision approved、verifier decision approved、maintenance window approved、rollback owner confirmed、release decision passed、release authorization granted、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;本地 P2-144 + P2-145 owner response chain regression `16 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、顯示值工作視窗污染掃描、i18n key parity 與 diff check 通過;production verification 尚未執行,下一步 P2-146 acceptance receipt preview | | `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 主動營運委派與版本生命週期契約 @@ -815,6 +816,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 63. 建立 12-Agent War Room readback。✅ P2-142 已完成正式驗證;12 個邏輯工位、12 份 read-only review、總工作量 `82`、evidence `84`、需批准 `61`、阻擋項 `54`、市場 refresh candidate `5`、日週月報 cadence `3`;runtime writer、Telegram send、Bot API、production write、paid API、SDK install、secret read、destructive operation 仍為 `0 / false`;feature commit `5de4b3f3` 與 deploy marker `1a2c9e36` 已進 `gitea/main`,本地 API/service pytest、JSON parse、Python compile、Web typecheck、Web production build、guard、doc secret sanity、公開 payload 掃描、production API readback、desktop / mobile smoke 通過。已由 P2-143 承接。 64. 建立 owner response preflight 與拒收邊界。✅ P2-143 已完成正式驗證;承接 P2-141 decision input prep 與 P2-142 War Room 基線,固定 response intake lane `5`、required owner field `18`、intake validation check `6`、rejection guard `6`、operator action `5`、waiting external response `5`;owner response received / accepted / rejected、redacted payload ingested、reviewer queue write、Gateway queue write、Telegram send、Bot API、result capture、learning、PlayBook trust、production write、secret read、destructive operation 仍為 `0 / false`;feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、本地 P2-142 War Room + P2-139 至 P2-143 regression `37 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、禁用外露值掃描、production API readback 與 desktop / mobile smoke 通過。下一步 P2-144 owner response readback。 65. 建立 owner response readback。✅ P2-144 已完成正式驗證;承接 P2-143 preflight,固定 response readback lane `5`、required owner field `18`、readback validation check `6`、readback rejection guard `6`、operator action `5`、waiting external response `5`、no external response received lane `5`;owner response received / accepted / rejected、redacted payload ingested、reviewer queue write、Gateway queue write、Telegram send、Bot API、result capture、learning、PlayBook trust、production write、secret read、destructive operation 仍為 `0 / false`;feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success;本地 P2-139 至 P2-144 regression `45 passed`,rebase 後含 tenants regression 的推送前回歸 `47 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、value-only 禁用外露值掃描、i18n key parity 與 diff check 通過;production API readback、desktop / mobile smoke、水平溢位 `0`、危險控制 `0`、工作溝通片語命中 `0` 已完成。下一步 P2-145 owner response acceptance gate。 +66. 建立 owner response acceptance gate。✅ P2-145 本地完成,正式驗證待部署;承接 P2-144 readback,固定 acceptance gate lane `5`、required owner field `18`、acceptance validation check `6`、acceptance rejection guard `6`、operator action `5`、blocked no external response `5`、no acceptable external response `5`;owner response received / accepted / rejected、redacted payload ingested、reviewer queue write、Gateway queue write、Telegram send、Bot API、result capture、learning、PlayBook trust、production write、secret read、destructive operation 仍為 `0 / false`;P2-144 + P2-145 regression `16 passed`、JSON parse、Python compile、Web typecheck、guard、doc secret sanity、顯示值工作視窗污染掃描、i18n key parity 與 diff check 通過。下一步 P2-146 acceptance receipt preview,且必須等合格、遮罩、欄位完整、可驗證來源的外部正式回覆後才能建立 receipt preview。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面