diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 680eba439..50ad05f6b 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -70,6 +70,9 @@ from src.services.ai_agent_learning_writeback_approval_package import ( from src.services.ai_agent_live_read_model_gate import ( load_latest_ai_agent_live_read_model_gate, ) +from src.services.ai_agent_owner_approved_fixture_dry_run import ( + load_latest_ai_agent_owner_approved_fixture_dry_run, +) from src.services.ai_agent_owner_approved_learning_dry_run import ( load_latest_ai_agent_owner_approved_learning_dry_run, ) @@ -754,6 +757,34 @@ async def get_agent_owner_approved_learning_dry_run() -> dict[str, Any]: ) from exc +@router.get( + "/agent-owner-approved-fixture-dry-run", + response_model=dict[str, Any], + summary="取得 AI Agent owner-approved fixture dry-run 批准包", + description=( + "讀取最新已提交的 owner-approved fixture dry-run 批准包;此端點只回傳 fixture-only dry-run 證據," + "不寫 KM、不更新 PlayBook trust、不寫 timeline / replay score、不寫 Gateway queue、不呼叫 Telegram Bot API、" + "不啟動 runtime worker、不開 Redis consumer group、不執行 DB migration、不觸發 workflow、" + "不執行主機或 cluster 指令、不使用 secrets 或付費 API、不回傳未核准內部細節。" + ), +) +async def get_agent_owner_approved_fixture_dry_run() -> dict[str, Any]: + """Return the latest read-only AI Agent owner-approved fixture dry-run package.""" + try: + return await asyncio.to_thread(load_latest_ai_agent_owner_approved_fixture_dry_run) + 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_fixture_dry_run_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent owner-approved fixture dry-run 批准包無效", + ) from exc + + @router.get( "/agent-proactive-operations-contract", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_owner_approved_fixture_dry_run.py b/apps/api/src/services/ai_agent_owner_approved_fixture_dry_run.py new file mode 100644 index 000000000..26870de33 --- /dev/null +++ b/apps/api/src/services/ai_agent_owner_approved_fixture_dry_run.py @@ -0,0 +1,213 @@ +""" +AI Agent owner-approved fixture dry-run snapshot. + +Loads the latest committed P2-403F fixture-only dry-run package. This module +never writes KM, updates PlayBook trust, writes timeline or replay scores, +writes Gateway queues, sends Telegram messages, opens Redis consumer groups, +starts workers, runs workflows, invokes secrets or paid APIs, or executes host +and cluster commands. +""" + +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_fixture_dry_run_*.json" +_SCHEMA_VERSION = "ai_agent_owner_approved_fixture_dry_run_v1" +_RUNTIME_AUTHORITY = "owner_approved_fixture_dry_run_only_no_live_write" + + +def load_latest_ai_agent_owner_approved_fixture_dry_run( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed AI Agent owner-approved fixture dry-run package.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent owner-approved fixture dry-run 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_read_only_boundaries(payload, str(latest)) + _require_package_safety(payload, str(latest)) + _require_rollup_consistency(payload, str(latest)) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + actual = payload.get("schema_version") + if actual != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}, got {actual!r}") + + 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 stay {_RUNTIME_AUTHORITY}") + if status.get("current_task_id") != "P2-403F": + raise ValueError(f"{label}: current_task_id must stay P2-403F") + if status.get("next_task_id") != "P2-403G": + raise ValueError(f"{label}: next_task_id must stay P2-403G") + + +def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = payload.get("approval_boundaries") or {} + enabled = sorted(key for key, value in boundaries.items() if value is not False) + if enabled: + raise ValueError(f"{label}: approval boundaries must remain false: {enabled}") + + truth = payload.get("dry_run_truth") or {} + if truth.get("owner_fixture_scope_approved") is not True: + raise ValueError(f"{label}: owner fixture scope must be approved for fixture-only dry-run") + if truth.get("fixture_dry_run_allowed") is not True: + raise ValueError(f"{label}: fixture dry-run must remain explicitly allowed") + + false_flags = { + "production_write_approved", + "km_write_allowed", + "playbook_trust_write_allowed", + "timeline_learning_write_allowed", + "agent_replay_score_write_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "redis_consumer_group_allowed", + "db_migration_allowed", + "workflow_trigger_allowed", + "runtime_worker_allowed", + "host_or_cluster_command_allowed", + "secret_or_paid_api_allowed", + } + unsafe = sorted(flag for flag in false_flags if truth.get(flag) is not False) + if unsafe: + raise ValueError(f"{label}: dry-run truth flags must remain false: {unsafe}") + + zero_counts = { + "live_learning_write_count", + "live_playbook_trust_update_count", + "live_km_update_count", + "live_timeline_write_count", + "live_replay_score_write_count", + "live_gateway_queue_write_count", + "live_telegram_send_count", + } + non_zero = sorted(key for key in zero_counts if truth.get(key) != 0) + if non_zero: + raise ValueError(f"{label}: live dry-run counts must remain zero: {non_zero}") + + +def _require_package_safety(payload: dict[str, Any], label: str) -> None: + package = payload.get("fixture_package") or {} + required_fields = set(package.get("required_fields") or []) + required_minimum = { + "fixture_event_id", + "source_contract_ref", + "scenario_type", + "owner_scope_ref", + "agent_owner", + "target_surface", + "proposed_delta_summary", + "redacted_evidence_ref", + "dry_run_expected_output", + "no_write_proof_ref", + "rollback_plan_ref", + } + missing = sorted(required_minimum - required_fields) + if missing: + raise ValueError(f"{label}: fixture package missing required fields: {missing}") + if package.get("owner_review_required") is not True: + raise ValueError(f"{label}: owner review must be required") + if package.get("rollback_required") is not True: + raise ValueError(f"{label}: rollback must be required") + if package.get("no_write_proof_required") is not True: + raise ValueError(f"{label}: no-write proof must be required") + + redaction = payload.get("display_redaction_contract") or {} + if redaction.get("redaction_required") is not True: + raise ValueError(f"{label}: frontend redaction must be required") + for flag in ( + "raw_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "action_button_allowed", + ): + if redaction.get(flag) is not False: + raise ValueError(f"{label}: {flag} must remain false") + + rollback = payload.get("rollback_contract") or {} + if rollback.get("rollback_required") is not True: + raise ValueError(f"{label}: rollback_contract.rollback_required must be true") + if not rollback.get("rollback_steps"): + raise ValueError(f"{label}: rollback steps must not be empty") + if not payload.get("fixture_sets"): + raise ValueError(f"{label}: fixture sets must not be empty") + if not payload.get("simulation_steps"): + raise ValueError(f"{label}: simulation steps must not be empty") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + fixture_sets = payload.get("fixture_sets") or [] + gates = payload.get("dry_run_gates") or [] + steps = payload.get("simulation_steps") or [] + package = payload.get("fixture_package") or {} + truth = payload.get("dry_run_truth") or {} + expected_counts = { + "fixture_set_count": len(fixture_sets), + "dry_run_gate_count": len(gates), + "simulation_step_count": len(steps), + "approved_fixture_only_count": sum( + 1 for fixture in fixture_sets if fixture.get("status") == "approved_for_fixture_only" + ), + "blocked_runtime_action_count": len( + { + action + for action in [gate.get("blocked_runtime_action") for gate in gates] + if action + } + ), + "required_field_count": len(package.get("required_fields") or []), + "forbidden_field_count": len(package.get("forbidden_fields") or []), + } + mismatched = { + key: {"expected": expected, "actual": rollups.get(key)} + for key, expected in expected_counts.items() + if rollups.get(key) != expected + } + if mismatched: + raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}") + + approval_required = sorted( + gate.get("gate_id") for gate in gates if gate.get("status") == "approval_required" + ) + if sorted(rollups.get("approval_required_gate_ids") or []) != approval_required: + raise ValueError(f"{label}: rollups.approval_required_gate_ids mismatch") + + live_write_total = sum( + int(truth.get(key) or 0) + for key in ( + "live_learning_write_count", + "live_playbook_trust_update_count", + "live_km_update_count", + "live_timeline_write_count", + "live_replay_score_write_count", + "live_gateway_queue_write_count", + ) + ) + if rollups.get("live_write_count_total") != live_write_total: + raise ValueError(f"{label}: rollups.live_write_count_total mismatch") + if rollups.get("live_send_count_total") != int(truth.get("live_telegram_send_count") or 0): + raise ValueError(f"{label}: rollups.live_send_count_total mismatch") + if rollups.get("live_receipt_count_total") != 0: + raise ValueError(f"{label}: rollups.live_receipt_count_total must remain zero") diff --git a/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run.py b/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run.py new file mode 100644 index 000000000..c517d1e00 --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run.py @@ -0,0 +1,56 @@ +import copy + +import pytest + +from src.services.ai_agent_owner_approved_fixture_dry_run import ( + load_latest_ai_agent_owner_approved_fixture_dry_run, +) + + +def test_load_latest_ai_agent_owner_approved_fixture_dry_run(): + data = load_latest_ai_agent_owner_approved_fixture_dry_run() + + assert data["schema_version"] == "ai_agent_owner_approved_fixture_dry_run_v1" + assert data["program_status"]["current_task_id"] == "P2-403F" + assert data["program_status"]["next_task_id"] == "P2-403G" + assert data["program_status"]["overall_completion_percent"] == 88 + assert data["dry_run_truth"]["owner_fixture_scope_approved"] is True + assert data["dry_run_truth"]["fixture_dry_run_allowed"] is True + assert data["dry_run_truth"]["production_write_approved"] is False + assert data["dry_run_truth"]["telegram_send_allowed"] is False + assert data["rollups"]["fixture_set_count"] == len(data["fixture_sets"]) + assert data["rollups"]["live_write_count_total"] == 0 + assert data["rollups"]["live_send_count_total"] == 0 + + +def test_rejects_production_write_approval(tmp_path): + data = load_latest_ai_agent_owner_approved_fixture_dry_run() + bad = copy.deepcopy(data) + bad["dry_run_truth"]["production_write_approved"] = True + path = tmp_path / "ai_agent_owner_approved_fixture_dry_run_2026-06-11.json" + path.write_text(__import__("json").dumps(bad), encoding="utf-8") + + with pytest.raises(ValueError, match="dry-run truth flags"): + load_latest_ai_agent_owner_approved_fixture_dry_run(tmp_path) + + +def test_rejects_action_button_display(tmp_path): + data = load_latest_ai_agent_owner_approved_fixture_dry_run() + bad = copy.deepcopy(data) + bad["display_redaction_contract"]["action_button_allowed"] = True + path = tmp_path / "ai_agent_owner_approved_fixture_dry_run_2026-06-11.json" + path.write_text(__import__("json").dumps(bad), encoding="utf-8") + + with pytest.raises(ValueError, match="action_button_allowed"): + load_latest_ai_agent_owner_approved_fixture_dry_run(tmp_path) + + +def test_rejects_rollup_mismatch(tmp_path): + data = load_latest_ai_agent_owner_approved_fixture_dry_run() + bad = copy.deepcopy(data) + bad["rollups"]["fixture_set_count"] = 999 + path = tmp_path / "ai_agent_owner_approved_fixture_dry_run_2026-06-11.json" + path.write_text(__import__("json").dumps(bad), encoding="utf-8") + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_owner_approved_fixture_dry_run(tmp_path) diff --git a/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run_api.py b/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run_api.py new file mode 100644 index 000000000..195b1e131 --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_fixture_dry_run_api.py @@ -0,0 +1,19 @@ +from fastapi.testclient import TestClient + +from src.main import app + + +def test_get_ai_agent_owner_approved_fixture_dry_run_api(): + client = TestClient(app) + response = client.get("/api/v1/agents/agent-owner-approved-fixture-dry-run") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_owner_approved_fixture_dry_run_v1" + assert data["program_status"]["current_task_id"] == "P2-403F" + assert data["program_status"]["next_task_id"] == "P2-403G" + assert data["dry_run_truth"]["fixture_dry_run_allowed"] is True + assert data["dry_run_truth"]["production_write_approved"] is False + assert data["dry_run_truth"]["telegram_send_allowed"] is False + assert data["rollups"]["live_write_count_total"] == 0 + assert data["rollups"]["live_send_count_total"] == 0 diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 2c8a608ad..8a0f6906c 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -3824,6 +3824,46 @@ "forbiddenInputs": "禁止輸入 {count}", "previewAllowed": "preview contract: {value}" } + }, + "ownerDryRunPackage": { + "title": "P2-403F Owner-approved Fixture Dry-run", + "source": "{generated} · {current} → {next}", + "packageTitle": "Fixture dry-run approval package", + "truthTitle": "Current dry-run truth", + "redactionTitle": "Frontend display boundaries", + "metrics": { + "overall": "P2-403F progress", + "fixtures": "Fixture sets", + "gates": "Dry-run gates", + "approval": "Approval gates", + "blocked": "Blocked actions", + "live": "Live write/send total" + }, + "flags": { + "fixture": "Fixture dry-run: {value}", + "write": "Production write: {value}", + "send": "Telegram send: {value}", + "actionButton": "Action button: {value}", + "secret": "Secret display: {value}" + }, + "labels": { + "requiredFields": "Required fields {count}", + "forbiddenFields": "Forbidden fields {count}", + "noWrite": "no-write proof: {value}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron" + }, + "statuses": { + "approved_for_fixture_only": "fixture only", + "approval_required": "owner approval required", + "fixture_only": "fixture-only", + "ready": "ready", + "blocked": "blocked", + "blocked_by_scope": "scope blocked" + } } } }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 2c8a608ad..221da0bd9 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -3824,6 +3824,46 @@ "forbiddenInputs": "禁止輸入 {count}", "previewAllowed": "preview contract: {value}" } + }, + "ownerDryRunPackage": { + "title": "P2-403F Owner-approved Fixture Dry-run", + "source": "{generated} · {current} → {next}", + "packageTitle": "Fixture dry-run 批准包", + "truthTitle": "目前乾跑真相", + "redactionTitle": "前端顯示紅線", + "metrics": { + "overall": "P2-403F 進度", + "fixtures": "Fixture sets", + "gates": "Dry-run gates", + "approval": "需批准 gates", + "blocked": "阻擋動作", + "live": "Live 寫送合計" + }, + "flags": { + "fixture": "Fixture dry-run: {value}", + "write": "Production write: {value}", + "send": "Telegram send: {value}", + "actionButton": "Action button: {value}", + "secret": "Secret display: {value}" + }, + "labels": { + "requiredFields": "必填欄位 {count}", + "forbiddenFields": "禁止欄位 {count}", + "noWrite": "no-write proof: {value}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron" + }, + "statuses": { + "approved_for_fixture_only": "僅批准 fixture", + "approval_required": "需 owner 批准", + "fixture_only": "fixture-only", + "ready": "已就緒", + "blocked": "已阻擋", + "blocked_by_scope": "範圍阻擋" + } } } }, 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 5d0400642..2d74dc704 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 @@ -40,6 +40,7 @@ import { type AiAgentInteractionLearningProofSnapshot, type AiAgentLearningWritebackApprovalPackageSnapshot, type AiAgentLiveReadModelGateSnapshot, + type AiAgentOwnerApprovedFixtureDryRunSnapshot, type AiAgentOwnerApprovedLearningDryRunSnapshot, type AiAgentProactiveOperationsContractSnapshot, type AiAgentRedisDryRunGateSnapshot, @@ -328,6 +329,7 @@ export function AutomationInventoryTab() { const [learningWritebackPackage, setLearningWritebackPackage] = useState(null) const [telegramReceiptPackage, setTelegramReceiptPackage] = useState(null) const [ownerApprovedLearningDryRun, setOwnerApprovedLearningDryRun] = useState(null) + const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) const [serviceHealthGapMatrix, setServiceHealthGapMatrix] = useState(null) const [serviceHealthNotificationPolicy, setServiceHealthNotificationPolicy] = useState(null) @@ -355,6 +357,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentLearningWritebackApprovalPackage(), apiClient.getAiAgentTelegramReceiptApprovalPackage(), apiClient.getAiAgentOwnerApprovedLearningDryRun(), + apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), apiClient.getServiceHealthGapMatrix(), apiClient.getServiceHealthFailureNotificationPolicy(), @@ -381,6 +384,7 @@ export function AutomationInventoryTab() { learningWritebackPackageResult, telegramReceiptPackageResult, ownerApprovedLearningDryRunResult, + ownerDryRunPackageResult, hostStatefulInventoryResult, serviceHealthGapMatrixResult, serviceHealthNotificationPolicyResult, @@ -404,6 +408,7 @@ export function AutomationInventoryTab() { setLearningWritebackPackage(learningWritebackPackageResult.status === 'fulfilled' ? learningWritebackPackageResult.value : null) setTelegramReceiptPackage(telegramReceiptPackageResult.status === 'fulfilled' ? telegramReceiptPackageResult.value : null) setOwnerApprovedLearningDryRun(ownerApprovedLearningDryRunResult.status === 'fulfilled' ? ownerApprovedLearningDryRunResult.value : null) + setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) setServiceHealthGapMatrix(serviceHealthGapMatrixResult.status === 'fulfilled' ? serviceHealthGapMatrixResult.value : null) setServiceHealthNotificationPolicy(serviceHealthNotificationPolicyResult.status === 'fulfilled' ? serviceHealthNotificationPolicyResult.value : null) @@ -425,6 +430,7 @@ export function AutomationInventoryTab() { learningWritebackPackageResult, telegramReceiptPackageResult, ownerApprovedLearningDryRunResult, + ownerDryRunPackageResult, hostStatefulInventoryResult, serviceHealthGapMatrixResult, serviceHealthNotificationPolicyResult, @@ -714,6 +720,42 @@ export function AutomationInventoryTab() { }) }, [ownerApprovedLearningDryRun]) + const visibleOwnerDryRunGates = useMemo(() => { + if (!ownerDryRunPackage) return [] + const priority = { approval_required: 0, approved_for_fixture_only: 1, fixture_only: 2, ready: 3 } as Record + return [...ownerDryRunPackage.dry_run_gates] + .sort((a, b) => { + const left = priority[a.status] ?? 4 + const right = priority[b.status] ?? 4 + if (left !== right) return left - right + return a.gate_id.localeCompare(b.gate_id) + }) + }, [ownerDryRunPackage]) + + const visibleOwnerDryRunFixtures = useMemo(() => { + if (!ownerDryRunPackage) return [] + const priority = { approved_for_fixture_only: 0, approval_required: 1, blocked_by_scope: 2 } as Record + return [...ownerDryRunPackage.fixture_sets] + .sort((a, b) => { + const left = priority[a.status] ?? 3 + const right = priority[b.status] ?? 3 + if (left !== right) return left - right + return a.fixture_id.localeCompare(b.fixture_id) + }) + }, [ownerDryRunPackage]) + + const visibleOwnerDryRunSteps = useMemo(() => { + if (!ownerDryRunPackage) return [] + const priority = { fixture_only: 0, ready: 1, blocked: 2 } as Record + return [...ownerDryRunPackage.simulation_steps] + .sort((a, b) => { + const left = priority[a.status] ?? 3 + const right = priority[b.status] ?? 3 + if (left !== right) return left - right + return a.step_id.localeCompare(b.step_id) + }) + }, [ownerDryRunPackage]) + const visibleReadinessRows = useMemo(() => { if (!backupReadiness) return [] const priority = { blocked: 0, action_required: 1, deferred: 2, ready: 3 } as Record @@ -857,7 +899,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -976,6 +1018,16 @@ export function AutomationInventoryTab() { const ownerApprovedLearningBlockedWrites = ownerApprovedLearningDryRun.rollups.blocked_write_action_count const ownerApprovedLearningPreviewOutputs = ownerApprovedLearningDryRun.rollups.preview_output_count const ownerApprovedLearningGenerated = ownerApprovedLearningDryRun.rollups.dry_run_preview_generated_count + const ownerDryRunOverall = ownerDryRunPackage.program_status.overall_completion_percent + const ownerDryRunFixtures = ownerDryRunPackage.rollups.fixture_set_count + const ownerDryRunGates = ownerDryRunPackage.rollups.dry_run_gate_count + const ownerDryRunApprovals = ownerDryRunPackage.rollups.approval_required_gate_ids.length + const ownerDryRunBlockedActions = ownerDryRunPackage.rollups.blocked_runtime_action_count + const ownerDryRunLiveTotal = ( + ownerDryRunPackage.rollups.live_write_count_total + + ownerDryRunPackage.rollups.live_send_count_total + + ownerDryRunPackage.rollups.live_receipt_count_total + ) const hostReadonlyDeniedCount = ( hostStatefulInventory.rollups.ssh_login_allowed_count + hostStatefulInventory.rollups.kubectl_command_execution_allowed_count @@ -1185,6 +1237,14 @@ export function AutomationInventoryTab() { } } + const ownerDryRunValueLabel = (group: string, value: string) => { + try { + return t(`ownerDryRunPackage.${group}.${value}` as never) + } catch { + return value + } + } + const hostStatefulValueLabel = (group: string, value: string) => { try { return t(`hostStateful.${group}.${value}` as never) @@ -2078,6 +2138,135 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('ownerDryRunPackage.title')} + +
+ +
+ +
+ } /> + } /> + } /> + 0 ? 'danger' : 'ok'} icon={} /> + 0 ? 'danger' : 'ok'} icon={} /> + } /> +
+ +
+
+
+ {t('ownerDryRunPackage.packageTitle')} + + {ownerDryRunPackage.fixture_package.operator_meaning} + +
+ + + +
+
+ +
+ {t('ownerDryRunPackage.truthTitle')} + + {ownerDryRunPackage.dry_run_truth.truth_note} + +
+ + + +
+
+ +
+ {t('ownerDryRunPackage.redactionTitle')} + + {ownerDryRunPackage.display_redaction_contract.frontend_display_policy} + +
+ + +
+
+
+ +
+ {visibleOwnerDryRunGates.map(gate => ( +
+
+ + {gate.gate_id} + + +
+ + {gate.display_name} + + + {gate.required_evidence} + +
+ + +
+
+ ))} +
+
+ +
+ {visibleOwnerDryRunFixtures.map(fixture => ( +
+
+ + {fixture.display_name} + + +
+ + {ownerDryRunValueLabel('agents', fixture.owner_agent)} · {fixture.scenario_type} · {fixture.target_surface} + + + {fixture.operator_visible_result} + + +
+ ))} +
+ +
+ {visibleOwnerDryRunSteps.map(step => ( +
+
+ + {step.step_id} + + +
+ + {step.display_name} + + + {step.expected_artifact} + +
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index bc7dc1e69..7c92554cc 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -302,6 +302,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentOwnerApprovedFixtureDryRun() { + const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) + return handleResponse(res) + }, + async getAiAgentHostStatefulVersionInventory() { const res = await fetch(`${API_BASE_URL}/agents/agent-host-stateful-version-inventory`) return handleResponse(res) @@ -1591,6 +1596,106 @@ export interface AiAgentOwnerApprovedLearningDryRunSnapshot { } } +export interface AiAgentOwnerApprovedFixtureDryRunSnapshot { + schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: string + next_task_id: string + read_only_mode: true + runtime_authority: 'owner_approved_fixture_dry_run_only_no_live_write' + status_note: string + } + source_refs: string[] + dry_run_truth: { + owner_fixture_scope_approved: true + production_write_approved: false + fixture_dry_run_allowed: true + km_write_allowed: false + playbook_trust_write_allowed: false + timeline_learning_write_allowed: false + agent_replay_score_write_allowed: false + gateway_queue_write_allowed: false + telegram_send_allowed: false + redis_consumer_group_allowed: false + db_migration_allowed: false + workflow_trigger_allowed: false + runtime_worker_allowed: false + host_or_cluster_command_allowed: false + secret_or_paid_api_allowed: false + live_learning_write_count: number + live_playbook_trust_update_count: number + live_km_update_count: number + live_timeline_write_count: number + live_replay_score_write_count: number + live_gateway_queue_write_count: number + live_telegram_send_count: number + truth_note: string + } + fixture_package: { + required_fields: string[] + forbidden_fields: string[] + owner_review_required: true + rollback_required: true + no_write_proof_required: true + operator_meaning: string + } + fixture_sets: Array<{ + fixture_id: string + display_name: string + scenario_type: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: string + target_surface: string + operator_visible_result: string + blocked_runtime_action: string + }> + dry_run_gates: Array<{ + gate_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: string + required_evidence: string + blocked_runtime_action: string + }> + simulation_steps: Array<{ + step_id: string + display_name: string + status: string + expected_artifact: string + }> + rollback_contract: { + rollback_required: true + rollback_steps: string[] + } + approval_boundaries: Record + display_redaction_contract: { + redaction_required: true + raw_payload_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + action_button_allowed: false + allowed_frontend_content: string[] + forbidden_frontend_content: string[] + frontend_display_policy: string + } + rollups: { + fixture_set_count: number + dry_run_gate_count: number + simulation_step_count: number + approved_fixture_only_count: number + approval_required_gate_ids: string[] + blocked_runtime_action_count: number + required_field_count: number + forbidden_field_count: number + live_write_count_total: number + live_send_count_total: number + live_receipt_count_total: number + } +} + export interface AiAgentHostStatefulVersionInventorySnapshot { schema_version: 'ai_agent_host_stateful_version_inventory_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 3b6ac1242..aba01aa20 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -62,6 +62,8 @@ - 新增 `ai_agent_owner_approved_learning_dry_run_v1` schema、committed snapshot、只讀 loader、API route 與測試。 - 新增 `GET /api/v1/agents/agent-owner-approved-learning-dry-run`,只回傳 dry-run preview 契約與人工操作選項;不產生 live preview、不寫 KM、不更新 PlayBook trust、不寫 timeline learning、不寫 replay score、不發 Telegram、不啟動 runtime worker。 +- 新增 `ai_agent_owner_approved_fixture_dry_run_v1` schema、committed snapshot、只讀 loader、API route 與測試,把 learning writeback、Telegram receipt、handoff replay、operator feedback 收斂成 fixture-only dry-run 證據;不寫 Gateway queue、不送 Telegram、不開 Redis consumer、不觸發 workflow。 +- 新增 `GET /api/v1/agents/agent-owner-approved-fixture-dry-run` 與治理頁 P2-403F fixture 區塊,顯示 fixture sets、dry-run gates、simulation steps、no-write proof、redaction 與 live write / send / receipt total `0`。 - Snapshot 固定 `8` 個 dry-run preview 必填輸入、`6` 個 forbidden inputs、`6` 個 preview outputs、`4` 個 operator actions、`4` 個 dry-run gates、verification / rollback contract 與 live write total `0`。 - Governance automation inventory 頁新增 P2-403F 區塊,顯示「審查 learning delta preview、補齊修復證據、批准產生 dry-run preview、退回或標記 no-action」四種人工下一步,避免批准後只顯示空泛結論。 - `agent-interaction-learning-proof` 與 `agent-proactive-operations-contract` 已同步 current / next:`P2-403F -> P2-403G`;三 Agent 互動學習證據完成度 `80% -> 88%`。 @@ -71,7 +73,7 @@ - JSON parse:P2-403F schema / snapshot、P2-403 interaction snapshot、P2-403 proactive snapshot、`zh-TW.json`、`en.json` 通過。 - `python3 -m py_compile apps/api/src/services/ai_agent_owner_approved_learning_dry_run.py apps/api/src/api/v1/agents.py`:通過。 -- API/service pytest:P2-403F owner-approved learning dry-run、P2-403E Telegram receipt approval package、P2-403 interaction proof、P2-403 proactive contract 目標測試 `22 passed`。 +- API/service pytest:P2-403F owner-approved learning dry-run、P2-403F owner-approved fixture dry-run、P2-403E Telegram receipt approval package、P2-403 interaction proof、P2-403 proactive contract 目標測試通過。 - `pnpm --filter @awoooi/web typecheck`:通過。 - `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING=1 pnpm --filter @awoooi/web build`:通過;`/zh-TW/governance` First Load JS `395 kB`。 - `source-control-owner-response-guard.py`、`security-mirror-progress-guard.py`、`doc-secrets-sanity-check.py docs .gitea`、`git diff --check` 通過。 @@ -94,13 +96,13 @@ **完成度同步**: -- P2-403F Owner-approved learning dry-run preview:本地 `100%`,正式站 `100%`。 +- P2-403F Owner-approved learning dry-run preview + fixture dry-run:本地 `100%`;其中 learning dry-run 正式站 `100%`,fixture dry-run 待本次推版後補正式驗證。 - 三 Agent 主動溝通、學習與成長證據:`88%`。 - P2-403G runtime write gate review:`0%`,尚未開始。 - owner approval received、dry-run preview generated、KM write、PlayBook trust update、timeline learning write、replay score write、Telegram send、runtime worker:全部仍為 `0 / false`。 - IwoooS 整體仍維持 `64%`;active runtime gate 仍 `0`。 -**邊界**:本段只做只讀 dry-run 契約、snapshot、schema、API、測試、前端可視化與文件;未產生 live preview、未寫 KM、未更新 PlayBook trust、未寫 timeline learning、未寫 replay score、未發 Telegram、未 SSH、未 active scan、未收 secret value、未開 runtime gate、未建立任何 P2-403F 內容區執行按鈕。 +**邊界**:本段只做只讀 dry-run 契約、fixture 證據、snapshot、schema、API、測試、前端可視化與文件;未產生 live preview、未寫 KM、未更新 PlayBook trust、未寫 timeline learning、未寫 replay score、未寫 Gateway queue、未發 Telegram、未開 Redis consumer、未 SSH、未 active scan、未收 secret value、未開 runtime gate、未建立任何 P2-403F 內容區執行按鈕。 ## 2026-06-11|P2-403E Telegram Receipt Approval Package 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 513247e85..1eca03b16 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 主動溝通、學習與成長證據 | 88% | 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 與人工操作選項。runtime worker、DB migration、production Redis consumer group、Telegram 實發、KM / PlayBook trust / timeline / replay score 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_live_read_model_gate_v1`、`ai_agent_redis_dry_run_gate_v1`、`ai_agent_learning_writeback_approval_package_v1`、`ai_agent_telegram_receipt_approval_package_v1`、`GET /api/v1/agents/agent-communication-learning-contract`、`GET /api/v1/agents/agent-interaction-learning-proof`、`GET /api/v1/agents/agent-live-read-model-gate`、`GET /api/v1/agents/agent-redis-dry-run-gate`、`GET /api/v1/agents/agent-learning-writeback-approval-package`、`GET /api/v1/agents/agent-telegram-receipt-approval-package`、`GET /api/v1/agents/agent-owner-approved-learning-dry-run`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | +| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 88% | 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 總包。runtime worker、DB migration、production Redis consumer group、Telegram 實發、KM / PlayBook trust / timeline / replay score 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_live_read_model_gate_v1`、`ai_agent_redis_dry_run_gate_v1`、`ai_agent_learning_writeback_approval_package_v1`、`ai_agent_telegram_receipt_approval_package_v1`、`ai_agent_owner_approved_learning_dry_run_v1`、`ai_agent_owner_approved_fixture_dry_run_v1`、`GET /api/v1/agents/agent-communication-learning-contract`、`GET /api/v1/agents/agent-interaction-learning-proof`、`GET /api/v1/agents/agent-live-read-model-gate`、`GET /api/v1/agents/agent-redis-dry-run-gate`、`GET /api/v1/agents/agent-learning-writeback-approval-package`、`GET /api/v1/agents/agent-telegram-receipt-approval-package`、`GET /api/v1/agents/agent-owner-approved-learning-dry-run`、`GET /api/v1/agents/agent-owner-approved-fixture-dry-run`、`/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,7 +20,7 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**88%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、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 與人工操作選項、API、治理頁顯示、測試與 MASTER 同步;目前 live AgentSession、Agent message、handoff、learning write、Telegram receipt 仍全部為 `0`,下一步依優先順序推 `P2-403G` runtime write gate review,但在批准前仍不得啟動 runtime loop。 +三 Agent 主動溝通、學習與成長證據目前完成度:**88%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、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、人工操作選項與 fixture-only dry-run 總包、API、治理頁顯示、測試與 MASTER 同步;目前 live AgentSession、Agent message、handoff、learning write、Telegram receipt、Gateway queue write 與 Telegram send 仍全部為 `0`,下一步依優先順序推 `P2-403G` runtime write gate review,但在批准前仍不得啟動 runtime loop。 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-403B`、`P2-403C`、`P2-403D`、`P2-403E` 與 `P2-403F` 已先補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package 與 owner-approved learning dry-run preview。下一步是 `P2-403G` runtime write gate review,外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 diff --git a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md index fa423a29d..18ad3eb81 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 @@ -1,12 +1,12 @@ # AI Agent 互動、溝通、學習與成長證據報告 > 日期:2026-06-11(台北時間) -> 文件定位:P2-403A 證據面 + P2-403B AgentSession / Redis Streams 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、API、治理頁 UI 與後續 runtime gate 分析。 +> 文件定位:P2-403A 證據面 + P2-403B AgentSession / Redis Streams 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 / fixture dry-run、API、治理頁 UI 與後續 runtime gate 分析。 > 事實邊界:本波只建立可見證據面與 read model gate,不啟動 runtime worker、不建立 DB migration、不開 Redis consumer group、不發 Telegram、不顯示工作視窗對話內容。 ## 1. 結論 -已完成 P2-403A、P2-403B、P2-403C、P2-403D 與 P2-403E 與 P2-403F:讓統帥能在治理頁看到 OpenClaw / Hermes / NemoTron 的互動、接手、學習與成長是否真的有證據,並看到 live read model、Redis dry-run、handoff envelope、ack / dead-letter / replay、learning writeback approval、Telegram receipt approval 下一步要通過哪些 gate。 +已完成 P2-403A、P2-403B、P2-403C、P2-403D、P2-403E 與 P2-403F:讓統帥能在治理頁看到 OpenClaw / Hermes / NemoTron 的互動、接手、學習與成長是否真的有證據,並看到 live read model、Redis dry-run、handoff envelope、ack / dead-letter / replay、learning writeback approval、Telegram receipt approval 與 fixture dry-run 下一步要通過哪些 gate。 目前真相: @@ -61,7 +61,7 @@ | `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、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、Agent lane、可觀測訊號、runtime gates、前端 redaction | ## 5. 後續優先順序 diff --git a/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md b/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md index d8039cb03..84665e097 100644 --- a/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md +++ b/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md @@ -1,7 +1,7 @@ # AI Agent 主動營運委派與版本生命週期分析報告 > 日期:2026-06-11(台北時間) -> 文件定位:P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G / P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F 只讀契約與治理 UI 摘要。權威細節以 MASTER §3.2.1c / §3.2.1d、`ai_agent_proactive_operations_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_live_read_model_gate_v1`、`ai_agent_redis_dry_run_gate_v1`、`ai_agent_learning_writeback_approval_package_v1`、`ai_agent_telegram_receipt_approval_package_v1`、`ai_agent_owner_approved_learning_dry_run_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` 為準。 +> 文件定位:P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G / P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F 只讀契約與治理 UI 摘要。權威細節以 MASTER §3.2.1c / §3.2.1d、`ai_agent_proactive_operations_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_live_read_model_gate_v1`、`ai_agent_redis_dry_run_gate_v1`、`ai_agent_learning_writeback_approval_package_v1`、`ai_agent_telegram_receipt_approval_package_v1`、`ai_agent_owner_approved_learning_dry_run_v1`、`ai_agent_owner_approved_fixture_dry_run_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` 為準。 ## 1. 本波完成度 @@ -144,7 +144,7 @@ P2-402G 的重點是把前面六個資料契約接回治理頁,讓統帥可以 | P2-403C | 6 | Redis Streams consumer group dry-run、handoff envelope、ack / dead letter / replay | 已完成,只讀;production Redis consumer / worker / ACK / replay 未啟用 | | P2-403D | 7 | Learning writeback approval package、資料保留、錯誤補償、人工回饋回滾 | 已完成,只讀;KM / PlayBook trust / timeline learning / replay score 寫入未啟用 | | P2-403E | 8 | Telegram receipt approval package、queue / delivery / ack / failure / retry | 已完成,只讀;Gateway queue / Bot API / delivery receipt / retry worker 未啟用 | -| P2-403F | 9 | Owner-approved learning dry-run preview、人工操作選項、驗證與 rollback | 已完成,只讀;owner approval / dry-run preview generation / KM / PlayBook trust / timeline / replay score write 未啟用 | +| P2-403F | 9 | Owner-approved learning dry-run preview、fixture dry-run、人工操作選項、驗證與 rollback | 已完成,只讀;owner approval / dry-run preview generation / KM / PlayBook trust / timeline / replay score write / Gateway queue / Telegram send 未啟用 | ## 11. 仍維持 false 的安全邊界 diff --git a/docs/evaluations/ai_agent_owner_approved_fixture_dry_run_2026-06-11.json b/docs/evaluations/ai_agent_owner_approved_fixture_dry_run_2026-06-11.json new file mode 100644 index 000000000..0b1c573df --- /dev/null +++ b/docs/evaluations/ai_agent_owner_approved_fixture_dry_run_2026-06-11.json @@ -0,0 +1,270 @@ +{ + "schema_version": "ai_agent_owner_approved_fixture_dry_run_v1", + "generated_at": "2026-06-11T23:59:59+08:00", + "program_status": { + "overall_completion_percent": 88, + "current_priority": "P2", + "current_task_id": "P2-403F", + "next_task_id": "P2-403G", + "read_only_mode": true, + "runtime_authority": "owner_approved_fixture_dry_run_only_no_live_write", + "status_note": "P2-403F 固定 owner-approved fixture dry-run 批准包;只允許用已提交 fixture 計算學習回寫、Telegram receipt、handoff replay 與 operator feedback 的乾跑證據,仍不寫 KM、不更新 PlayBook trust、不寫 timeline / replay score、不寫 Gateway queue、不送 Telegram。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_learning_writeback_approval_package_2026-06-11.json", + "docs/evaluations/ai_agent_telegram_receipt_approval_package_2026-06-11.json", + "docs/evaluations/ai_agent_interaction_learning_proof_2026-06-11.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md" + ], + "dry_run_truth": { + "owner_fixture_scope_approved": true, + "production_write_approved": false, + "fixture_dry_run_allowed": true, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "timeline_learning_write_allowed": false, + "agent_replay_score_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "redis_consumer_group_allowed": false, + "db_migration_allowed": false, + "workflow_trigger_allowed": false, + "runtime_worker_allowed": false, + "host_or_cluster_command_allowed": false, + "secret_or_paid_api_allowed": false, + "live_learning_write_count": 0, + "live_playbook_trust_update_count": 0, + "live_km_update_count": 0, + "live_timeline_write_count": 0, + "live_replay_score_write_count": 0, + "live_gateway_queue_write_count": 0, + "live_telegram_send_count": 0, + "truth_note": "本階段只代表 owner 已批准把 committed fixture 轉成 dry-run 證據與前台只讀卡片;任何 production write、Telegram send、queue write、worker、Redis consumer、DB migration、host / cluster command、secret 或付費 API 仍全部是 false / 0。" + }, + "fixture_package": { + "required_fields": [ + "fixture_event_id", + "source_contract_ref", + "scenario_type", + "owner_scope_ref", + "agent_owner", + "target_surface", + "proposed_delta_summary", + "redacted_evidence_ref", + "dry_run_expected_output", + "no_write_proof_ref", + "rollback_plan_ref", + "created_at" + ], + "forbidden_fields": [ + "secret_value", + "credential_value", + "telegram_bot_token", + "telegram_chat_id_raw", + "authorization_header", + "production_database_dsn", + "kubeconfig_value", + "raw_prompt", + "conversation_transcript", + "private_reasoning", + "raw_tool_output" + ], + "owner_review_required": true, + "rollback_required": true, + "no_write_proof_required": true, + "operator_meaning": "每個 fixture dry-run 候選都必須說清楚來源契約、場景、負責 Agent、目標 surface、預期輸出、脫敏證據、無寫入證明與 rollback;批准前只能產生乾跑證據,不得進 production write 或 send path。" + }, + "fixture_sets": [ + { + "fixture_id": "learning_writeback_fixture", + "display_name": "Learning writeback fixture", + "scenario_type": "learning_writeback", + "owner_agent": "hermes", + "status": "approved_for_fixture_only", + "target_surface": "knowledge_entries + playbook_trust_history + agent_replay_results", + "operator_visible_result": "顯示候選知識條目、trust delta、replay score delta 與 rollback 摘要;live write count 必須維持 0。", + "blocked_runtime_action": "km_playbook_timeline_replay_live_write" + }, + { + "fixture_id": "telegram_receipt_fixture", + "display_name": "Telegram receipt fixture", + "scenario_type": "telegram_receipt", + "owner_agent": "openclaw", + "status": "approved_for_fixture_only", + "target_surface": "telegram_gateway_queue + governance receipts", + "operator_visible_result": "顯示 queued / delivered / acknowledged / failed / retry 的脫敏收據結構;Gateway queue write 與 Telegram send 必須維持 0。", + "blocked_runtime_action": "telegram_gateway_queue_or_send" + }, + { + "fixture_id": "handoff_replay_fixture", + "display_name": "Handoff replay fixture", + "scenario_type": "handoff_replay", + "owner_agent": "nemotron", + "status": "approved_for_fixture_only", + "target_surface": "agent_session_replay + evaluation scorecard", + "operator_visible_result": "顯示 OpenClaw / Hermes / NemoTron 接手、拒收、補證與 replay scoring 摘要;Redis consumer group 與 runtime worker 必須維持關閉。", + "blocked_runtime_action": "redis_consumer_or_runtime_worker" + }, + { + "fixture_id": "operator_feedback_fixture", + "display_name": "Operator feedback fixture", + "scenario_type": "operator_feedback", + "owner_agent": "openclaw", + "status": "approved_for_fixture_only", + "target_surface": "approval_records + learning_service feedback", + "operator_visible_result": "顯示人工接受、拒絕、要求補證後會如何形成策略修正草案;不寫 canonical memory、不送 Telegram、不啟動 workflow。", + "blocked_runtime_action": "feedback_to_live_memory_write" + } + ], + "dry_run_gates": [ + { + "gate_id": "owner_fixture_scope_gate", + "display_name": "Owner fixture scope gate", + "owner_agent": "openclaw", + "status": "approved_for_fixture_only", + "required_evidence": "明確限制在 committed fixture、只讀 API、前端脫敏卡片、無寫入證明與 rollback plan。", + "blocked_runtime_action": "production_write_or_send" + }, + { + "gate_id": "learning_writeback_no_write_gate", + "display_name": "Learning writeback no-write gate", + "owner_agent": "hermes", + "status": "approval_required", + "required_evidence": "KM / PlayBook trust / timeline / replay score 寫入需獨立 owner approval、pre-write snapshot、idempotency key、rollback record。", + "blocked_runtime_action": "learning_writeback_live_write" + }, + { + "gate_id": "telegram_gateway_no_send_gate", + "display_name": "Telegram gateway no-send gate", + "owner_agent": "openclaw", + "status": "approval_required", + "required_evidence": "Gateway queue dry-run、channel alias、message template、delivery correlation、failure fallback 與 E2E receipt proof。", + "blocked_runtime_action": "telegram_queue_write_or_bot_send" + }, + { + "gate_id": "redis_consumer_no_connect_gate", + "display_name": "Redis consumer no-connect gate", + "owner_agent": "nemotron", + "status": "approval_required", + "required_evidence": "consumer group 建立、ack / dead-letter / replay、idempotency 與 production Redis read/write approval。", + "blocked_runtime_action": "redis_consumer_group_connect" + }, + { + "gate_id": "secret_paid_service_gate", + "display_name": "Secret and paid service gate", + "owner_agent": "openclaw", + "status": "approval_required", + "required_evidence": "任何 secret、token、paid API、host / cluster command、workflow trigger 都需獨立授權與稽核記錄。", + "blocked_runtime_action": "secret_paid_api_or_host_command" + } + ], + "simulation_steps": [ + { + "step_id": "load_committed_fixtures", + "display_name": "載入 committed fixtures", + "status": "ready", + "expected_artifact": "fixture manifest + source_refs" + }, + { + "step_id": "validate_redaction_contract", + "display_name": "驗證前端脫敏契約", + "status": "ready", + "expected_artifact": "allowed / forbidden frontend fields" + }, + { + "step_id": "calculate_learning_delta", + "display_name": "計算學習回寫 delta", + "status": "fixture_only", + "expected_artifact": "candidate KM / trust / replay delta without write" + }, + { + "step_id": "calculate_telegram_receipt", + "display_name": "計算 Telegram receipt envelope", + "status": "fixture_only", + "expected_artifact": "queued / delivered / ack / failed / retry receipt envelope without send" + }, + { + "step_id": "score_handoff_replay", + "display_name": "評分 Agent handoff replay", + "status": "fixture_only", + "expected_artifact": "handoff accepted / rejected / challenge scorecard without worker" + }, + { + "step_id": "emit_governance_readonly_snapshot", + "display_name": "產出治理頁只讀快照", + "status": "ready", + "expected_artifact": "API response + UI cards with live counters at 0" + } + ], + "rollback_contract": { + "rollback_required": true, + "rollback_steps": [ + "P2-403F 只新增 committed fixture 與只讀 API,rollback 可直接移除端點與前端卡片。", + "若 fixture 被判定誤導,先把 fixture_set status 改回 blocked_by_scope,再更新治理頁。", + "任何後續 live write / send gate 必須保留 pre-write snapshot 與 owner approval record。", + "發現前端誤顯未核准內容時,先移除欄位、保留 schema version,再重新部署。", + "rollback 後 LOGBOOK 必須記錄 live counters 仍為 0。" + ] + }, + "approval_boundaries": { + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "timeline_learning_write_allowed": false, + "agent_replay_score_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "redis_consumer_group_allowed": false, + "db_migration_allowed": false, + "workflow_trigger_allowed": false, + "runtime_worker_allowed": false, + "host_or_cluster_command_allowed": false, + "secret_or_paid_api_allowed": false, + "paid_external_service_allowed": false, + "production_route_change_allowed": false + }, + "display_redaction_contract": { + "redaction_required": true, + "raw_payload_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "action_button_allowed": false, + "allowed_frontend_content": [ + "fixture scenario", + "owner agent", + "target surface", + "operator visible result", + "blocked runtime action", + "dry-run gate status", + "live counters" + ], + "forbidden_frontend_content": [ + "Telegram token", + "raw chat id", + "authorization header", + "production DSN", + "kubeconfig", + "提示內容", + "未核准細節", + "raw tool output" + ], + "frontend_display_policy": "治理頁只顯示 fixture 場景、owner agent、target surface、阻擋動作、乾跑 gate 與 live counters;token、raw chat id、authorization header、production DSN、kubeconfig、提示內容、未核准細節與 raw tool output 不進前端,也不提供批准或執行按鈕。" + }, + "rollups": { + "fixture_set_count": 4, + "dry_run_gate_count": 5, + "simulation_step_count": 6, + "approved_fixture_only_count": 4, + "approval_required_gate_ids": [ + "learning_writeback_no_write_gate", + "redis_consumer_no_connect_gate", + "secret_paid_service_gate", + "telegram_gateway_no_send_gate" + ], + "blocked_runtime_action_count": 5, + "required_field_count": 12, + "forbidden_field_count": 11, + "live_write_count_total": 0, + "live_send_count_total": 0, + "live_receipt_count_total": 0 + } +} diff --git a/docs/schemas/ai_agent_owner_approved_fixture_dry_run_v1.schema.json b/docs/schemas/ai_agent_owner_approved_fixture_dry_run_v1.schema.json new file mode 100644 index 000000000..c3694c390 --- /dev/null +++ b/docs/schemas/ai_agent_owner_approved_fixture_dry_run_v1.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_owner_approved_fixture_dry_run_v1.schema.json", + "title": "AI Agent Owner Approved Fixture Dry Run", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "dry_run_truth", + "fixture_package", + "fixture_sets", + "dry_run_gates", + "simulation_steps", + "rollback_contract", + "approval_boundaries", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_owner_approved_fixture_dry_run_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_task_id", + "next_task_id", + "read_only_mode", + "runtime_authority" + ], + "properties": { + "overall_completion_percent": { "type": "integer" }, + "current_priority": { "type": "string" }, + "current_task_id": { "const": "P2-403F" }, + "next_task_id": { "const": "P2-403G" }, + "read_only_mode": { "const": true }, + "runtime_authority": { "const": "owner_approved_fixture_dry_run_only_no_live_write" }, + "status_note": { "type": "string" } + }, + "additionalProperties": false + }, + "dry_run_truth": { + "type": "object", + "properties": { + "owner_fixture_scope_approved": { "const": true }, + "production_write_approved": { "const": false }, + "fixture_dry_run_allowed": { "const": true }, + "km_write_allowed": { "const": false }, + "playbook_trust_write_allowed": { "const": false }, + "timeline_learning_write_allowed": { "const": false }, + "agent_replay_score_write_allowed": { "const": false }, + "gateway_queue_write_allowed": { "const": false }, + "telegram_send_allowed": { "const": false }, + "redis_consumer_group_allowed": { "const": false }, + "db_migration_allowed": { "const": false }, + "workflow_trigger_allowed": { "const": false }, + "runtime_worker_allowed": { "const": false }, + "host_or_cluster_command_allowed": { "const": false }, + "secret_or_paid_api_allowed": { "const": false }, + "live_learning_write_count": { "const": 0 }, + "live_playbook_trust_update_count": { "const": 0 }, + "live_km_update_count": { "const": 0 }, + "live_timeline_write_count": { "const": 0 }, + "live_replay_score_write_count": { "const": 0 }, + "live_gateway_queue_write_count": { "const": 0 }, + "live_telegram_send_count": { "const": 0 }, + "truth_note": { "type": "string" } + }, + "additionalProperties": false + }, + "fixture_package": { + "type": "object", + "required": [ + "required_fields", + "forbidden_fields", + "owner_review_required", + "rollback_required", + "no_write_proof_required" + ], + "properties": { + "required_fields": { "type": "array", "items": { "type": "string" } }, + "forbidden_fields": { "type": "array", "items": { "type": "string" } }, + "owner_review_required": { "const": true }, + "rollback_required": { "const": true }, + "no_write_proof_required": { "const": true }, + "operator_meaning": { "type": "string" } + }, + "additionalProperties": false + }, + "fixture_sets": { + "type": "array", + "items": { + "type": "object", + "required": ["fixture_id", "display_name", "scenario_type", "owner_agent", "status", "target_surface", "operator_visible_result", "blocked_runtime_action"], + "properties": { + "fixture_id": { "type": "string" }, + "display_name": { "type": "string" }, + "scenario_type": { "type": "string" }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "type": "string" }, + "target_surface": { "type": "string" }, + "operator_visible_result": { "type": "string" }, + "blocked_runtime_action": { "type": "string" } + }, + "additionalProperties": false + } + }, + "dry_run_gates": { + "type": "array", + "items": { + "type": "object", + "required": ["gate_id", "display_name", "owner_agent", "status", "required_evidence", "blocked_runtime_action"], + "properties": { + "gate_id": { "type": "string" }, + "display_name": { "type": "string" }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "status": { "type": "string" }, + "required_evidence": { "type": "string" }, + "blocked_runtime_action": { "type": "string" } + }, + "additionalProperties": false + } + }, + "simulation_steps": { + "type": "array", + "items": { + "type": "object", + "required": ["step_id", "display_name", "status", "expected_artifact"], + "properties": { + "step_id": { "type": "string" }, + "display_name": { "type": "string" }, + "status": { "type": "string" }, + "expected_artifact": { "type": "string" } + }, + "additionalProperties": false + } + }, + "rollback_contract": { + "type": "object", + "required": ["rollback_required", "rollback_steps"], + "properties": { + "rollback_required": { "const": true }, + "rollback_steps": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "approval_boundaries": { + "type": "object", + "additionalProperties": { "const": false } + }, + "display_redaction_contract": { + "type": "object", + "properties": { + "redaction_required": { "const": true }, + "raw_payload_display_allowed": { "const": false }, + "private_reasoning_display_allowed": { "const": false }, + "secret_value_display_allowed": { "const": false }, + "action_button_allowed": { "const": false }, + "allowed_frontend_content": { "type": "array", "items": { "type": "string" } }, + "forbidden_frontend_content": { "type": "array", "items": { "type": "string" } }, + "frontend_display_policy": { "type": "string" } + }, + "additionalProperties": false + }, + "rollups": { + "type": "object", + "required": [ + "fixture_set_count", + "dry_run_gate_count", + "simulation_step_count", + "approved_fixture_only_count", + "approval_required_gate_ids", + "blocked_runtime_action_count", + "required_field_count", + "forbidden_field_count", + "live_write_count_total", + "live_send_count_total", + "live_receipt_count_total" + ], + "properties": { + "fixture_set_count": { "type": "integer" }, + "dry_run_gate_count": { "type": "integer" }, + "simulation_step_count": { "type": "integer" }, + "approved_fixture_only_count": { "type": "integer" }, + "approval_required_gate_ids": { "type": "array", "items": { "type": "string" } }, + "blocked_runtime_action_count": { "type": "integer" }, + "required_field_count": { "type": "integer" }, + "forbidden_field_count": { "type": "integer" }, + "live_write_count_total": { "const": 0 }, + "live_send_count_total": { "const": 0 }, + "live_receipt_count_total": { "const": 0 } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index e8e81d770..da985c580 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 @@ -634,7 +634,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_communication_learning_contract_2026-06-11.json` | 2026-06-11 committed snapshot;完成度 `35%`,runtime worker / DB migration / Telegram direct send 全部 false | | `apps/api/src/services/ai_agent_communication_learning_contract.py` | 只讀 loader;強制驗證 runtime / migration / Telegram / SDK / route 權限都未開 | | `GET /api/v1/agents/agent-communication-learning-contract` | 治理 API;只回傳 committed contract,不啟動 worker、不碰 DB/Redis、不呼叫外部服務 | -| `docs/evaluations/ai_agent_interaction_learning_proof_2026-06-11.json` + `GET /api/v1/agents/agent-interaction-learning-proof` | P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F 互動、接手、學習、成長、read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package 與 owner-approved learning dry-run 證據面;目前 live session、message、handoff、learning write、Telegram receipt 全部 `0`,下一步 P2-403G | +| `docs/evaluations/ai_agent_interaction_learning_proof_2026-06-11.json` + `GET /api/v1/agents/agent-interaction-learning-proof` | P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F 互動、接手、學習、成長、read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run 與 fixture dry-run 證據面;目前 live session、message、handoff、learning write、Gateway queue、Telegram send 全部 `0`,下一步 P2-403G | | `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 主動營運委派與版本生命週期契約 @@ -714,7 +714,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 9. 建立 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate。✅ P2-403C 已完成;fixture-only dry-run、idempotency、redacted evidence 與治理頁已接入,但 Redis runtime consumer / ACK / replay 仍未授權。 10. 建立 learning writeback approval package,先固定 owner review、rollback、redaction 與 blocked write actions。✅ P2-403D 已完成;KM / PlayBook trust / timeline learning / replay score live write 仍未授權。 11. 建立 Telegram receipt approval package,先固定 queue、delivery、ack、failure、retry 與 redaction。✅ P2-403E 已完成;Gateway queue write、Bot API、delivery receipt write、retry worker 仍未授權。 -12. 建立 owner-approved learning dry-run preview,先固定批准後可產生的 dry-run preview、人工操作選項、驗證與 rollback。✅ P2-403F 已完成;owner approval received、dry-run preview generated、KM / PlayBook trust / timeline / replay score write、Telegram send 仍為 `0 / false`。下一步 P2-403G runtime write gate review。 +12. 建立 owner-approved learning dry-run preview,先固定批准後可產生的 dry-run preview、人工操作選項、驗證與 rollback。✅ P2-403F 已完成;owner approval received、dry-run preview generated、KM / PlayBook trust / timeline / replay score write、Telegram send 仍為 `0 / false`。 +13. 建立 owner-approved fixture dry-run 總包,將 learning writeback、Telegram receipt、handoff replay、operator feedback 的乾跑證據收斂到治理頁。✅ P2-403F 補強完成;Gateway queue、Telegram send、Redis consumer、runtime worker 仍為 `0 / false`。下一步 P2-403G runtime write gate review。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -759,6 +760,9 @@ Repo / registry / release notes / K8s / host / observability / backup evidence | `docs/schemas/ai_agent_owner_approved_learning_dry_run_v1.schema.json` | P2-403F owner-approved learning dry-run schema;強制 owner approval / generated preview / KM / PlayBook trust / timeline / replay score write / Telegram send 全部維持未啟用 | | `docs/evaluations/ai_agent_owner_approved_learning_dry_run_2026-06-11.json` | P2-403F committed snapshot;dry-run preview 欄位、人工操作選項、evidence gate、verification / rollback contract,live write total `0` | | `GET /api/v1/agents/agent-owner-approved-learning-dry-run` | 治理 API;只回傳 owner-approved learning dry-run 契約,不產生 live preview、不寫 KM、不更新 PlayBook trust、不寫 timeline / replay score、不發 Telegram | +| `docs/schemas/ai_agent_owner_approved_fixture_dry_run_v1.schema.json` | P2-403F owner-approved fixture dry-run schema;強制 KM / PlayBook / timeline / replay / Gateway queue / Telegram / Redis / worker / workflow / host / secret / paid API 全部維持未授權 | +| `docs/evaluations/ai_agent_owner_approved_fixture_dry_run_2026-06-11.json` | P2-403F committed snapshot;4 組 fixture set、5 個 dry-run gate、6 個 simulation step、no-write proof 與 live write / send / receipt total `0` | +| `GET /api/v1/agents/agent-owner-approved-fixture-dry-run` | 治理 API;只回傳 owner-approved fixture dry-run 批准包,不寫 KM、不送 Telegram、不開 Redis consumer、不啟動 runtime worker | | `/zh-TW/governance?tab=automation-inventory` | 顯示證據階梯、目前真相、三 Agent lane、可觀測訊號、runtime gates 與 redaction policy | **硬性紅線:** @@ -1846,7 +1850,9 @@ Phase 6 完成後 ### 2026-06-11 23:59 (台北) — §3.2 / §5 — 完成 P2-403F Owner-approved Learning Dry-run — 批准後先產生可審查 preview,不直接寫入錯誤知識 - 新增 `ai_agent_owner_approved_learning_dry_run_v1` schema / committed snapshot / loader / API / 測試,定義 owner approval、dry-run preview inputs / outputs、operator actions、evidence gates、verification 與 rollback contract。 +- 補上 `ai_agent_owner_approved_fixture_dry_run_v1` schema / committed snapshot / loader / API / 測試,把 learning writeback、Telegram receipt、handoff replay、operator feedback 收斂成 fixture-only dry-run 證據總包。 - `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-owner-approved-learning-dry-run`,顯示人工操作選項、dry-run gates、preview outputs、truth flags 與 generated preview count。 +- 同頁接入 `GET /api/v1/agents/agent-owner-approved-fixture-dry-run`,顯示 fixture sets、dry-run gates、simulation steps、no-write proof、redaction 與 live write / send / receipt total。 - 更新 `ai_agent_interaction_learning_proof_2026-06-11.json`:整體完成度 `88%`,current task `P2-403F`,next task `P2-403G`;live AgentSession / Redis events / handoff / learning write / Telegram digest receipt 全部仍為 `0`。 - 本波仍不產生 live dry-run preview、不寫 KM、不更新 PlayBook trust、不寫 timeline learning、不寫 replay score、不發 Telegram、不啟動 runtime worker、不讀取或輸出 secret;下一步 P2-403G 才 review runtime write gate。