From f48fa76f50065d74587b013303f141a70d5f2789 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 19 Jun 2026 02:15:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(agents):=20=E6=96=B0=E5=A2=9E=20P2-411=20o?= =?UTF-8?q?wner=20acceptance=20event=20bus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 34 ++ ...agent_action_owner_acceptance_event_bus.py | 430 ++++++++++++++ ...agent_action_owner_acceptance_event_bus.py | 133 +++++ ...t_action_owner_acceptance_event_bus_api.py | 49 ++ apps/web/messages/en.json | 70 +++ apps/web/messages/zh-TW.json | 70 +++ .../tabs/automation-inventory-tab.tsx | 263 +++++++- .../src/app/[locale]/observability/page.tsx | 4 +- apps/web/src/lib/api-client.ts | 219 +++++++ docs/LOGBOOK.md | 46 ++ ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 17 +- ...owner_acceptance_event_bus_2026-06-19.json | 560 ++++++++++++++++++ ..._owner_acceptance_event_bus_v1.schema.json | 242 ++++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 13 + 14 files changed, 2139 insertions(+), 11 deletions(-) create mode 100644 apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py create mode 100644 apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py create mode 100644 apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py create mode 100644 docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json create mode 100644 docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index a5fccf531..458d1f2ba 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -49,6 +49,9 @@ from src.services.ai_agent_12_agent_war_room import ( from src.services.ai_agent_action_audit_ledger import ( load_latest_ai_agent_action_audit_ledger, ) +from src.services.ai_agent_action_owner_acceptance_event_bus import ( + load_latest_ai_agent_action_owner_acceptance_event_bus, +) from src.services.ai_agent_automation_backlog_snapshot import ( load_latest_ai_agent_automation_backlog_snapshot, ) @@ -972,6 +975,37 @@ async def get_agent_action_audit_ledger() -> dict[str, Any]: ) from exc +@router.get( + "/agent-action-owner-acceptance-event-bus", + response_model=dict[str, Any], + summary="取得 P2-411 AI Agent Owner Acceptance / Handoff Event Bus", + description=( + "讀取最新已提交的 P2-411 AI Agent owner acceptance / handoff event bus " + "no-write 快照;此端點只呈現 owner acceptance lane、handoff event template、" + "RAG memory proposal、verifier gate 與 no-write activation boundary。它不 publish event bus、" + "不寫 audit DB、不寫 timeline、不寫 KM、不更新 PlayBook trust、不寫 Gateway queue、" + "不送 Telegram、不呼叫 Bot API、不 dispatch worker、不寫 production、不讀 secret、" + "不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。" + ), +) +async def get_agent_action_owner_acceptance_event_bus() -> dict[str, Any]: + """回傳最新 P2-411 owner acceptance / handoff event bus 只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_action_owner_acceptance_event_bus) + 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_action_owner_acceptance_event_bus_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P2-411 AI Agent Owner Acceptance / Handoff Event Bus 快照無效", + ) from exc + + @router.get( "/agent-host-runaway-aiops-loop-readiness", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py b/apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py new file mode 100644 index 000000000..279466870 --- /dev/null +++ b/apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py @@ -0,0 +1,430 @@ +""" +P2-411 AI Agent action owner acceptance event bus snapshot. + +Loads the latest committed owner acceptance / handoff event bus baseline. This +module validates no-write owner acceptance lanes, handoff event templates, and +RAG memory proposals. It never publishes event bus messages, writes audit DB +rows, timeline events, KM, PlayBook trust, Gateway queues, Telegram messages, +secrets, hosts, Kubernetes resources, or production state. +""" + +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_action_owner_acceptance_event_bus_*.json" +_SCHEMA_VERSION = "ai_agent_action_owner_acceptance_event_bus_v1" +_RUNTIME_AUTHORITY = "agent_action_owner_acceptance_event_bus_no_write_committed_snapshot" +_EXPECTED_CURRENT_TASK = "P2-411" +_EXPECTED_NEXT_TASK = "P2-412" +_EXPECTED_SOURCE_SCHEMAS = { + "ai_agent_high_risk_owner_review_queue_v1", + "ai_agent_action_audit_ledger_v1", + "ai_agent_communication_learning_contract_v1", + "ai_agent_12_agent_war_room_v1", +} +_TRUE_TRUTH_FLAGS = { + "p2_409_owner_queue_loaded", + "p2_410_audit_ledger_loaded", + "communication_contract_loaded", + "war_room_loaded", + "owner_acceptance_envelope_required", + "handoff_protocol_ready", + "rag_memory_proposal_ready", + "event_bus_no_write_mode", + "redacted_evidence_only", + "high_critical_human_gate_required", + "low_medium_owner_scope_required_before_worker", +} +_FALSE_TRUTH_FLAGS = { + "owner_response_received", + "owner_response_accepted", + "owner_response_rejected", + "external_response_ingested", + "event_bus_publish_enabled", + "audit_db_write_enabled", + "timeline_write_enabled", + "km_write_enabled", + "playbook_trust_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "worker_dispatch_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", +} +_ZERO_TRUTH_COUNTS = { + "owner_response_received_count_24h", + "owner_response_accepted_count_24h", + "owner_response_rejected_count_24h", + "external_response_ingested_count_24h", + "event_bus_publish_count_24h", + "audit_db_write_count_24h", + "timeline_write_count_24h", + "km_write_count_24h", + "playbook_trust_write_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "bot_api_call_count_24h", + "worker_dispatch_count_24h", + "receipt_production_write_count_24h", + "production_write_count_24h", + "secret_read_count_24h", + "paid_api_call_count_24h", + "host_write_count_24h", + "kubectl_action_count_24h", + "destructive_operation_count_24h", +} +_FALSE_LANE_FLAGS = { + "response_received", + "acceptance_passed", + "acceptance_rejected", + "runtime_write_allowed", + "event_bus_publish_allowed", + "telegram_send_allowed", + "rag_write_allowed", +} +_FALSE_EVENT_FLAGS = { + "event_bus_write_allowed", + "audit_db_write_allowed", + "timeline_write_allowed", + "km_write_allowed", + "playbook_trust_write_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed", +} +_FALSE_PROPOSAL_FLAGS = { + "km_write_allowed", + "playbook_trust_write_allowed", + "embedding_write_allowed", +} +_TRUE_BOUNDARY_FLAGS = { + "committed_snapshot_read_allowed", + "owner_acceptance_lane_preview_allowed", + "handoff_event_template_preview_allowed", + "rag_memory_proposal_preview_allowed", + "governance_ui_projection_allowed", +} +_FALSE_BOUNDARY_FLAGS = { + "event_bus_publish_enabled", + "audit_db_write_enabled", + "timeline_write_enabled", + "km_write_enabled", + "playbook_trust_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "worker_dispatch_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", +} +_ZERO_ROLLUP_FIELDS = { + "owner_response_received_count", + "owner_response_accepted_count", + "owner_response_rejected_count", + "external_response_ingested_count", + "event_bus_publish_count", + "audit_db_write_count", + "timeline_write_count", + "km_write_count", + "playbook_trust_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "bot_api_call_count", + "worker_dispatch_count", + "receipt_production_write_count", + "production_write_count", + "secret_read_count", + "paid_api_call_count", + "host_write_count", + "kubectl_action_count", + "destructive_operation_count", +} +_FORBIDDEN_PUBLIC_TERMS = { + "批准" + "!", + "In app " + "browser", + "My request for " + "Codex", + "codex_" + "delegation", + "source_" + "thread_id", + "chain_of_thought", + "private reasoning text", + "authorization_header", + "telegram token value", + "raw_payload", + "raw prompt", + "internal collaboration transcript", + "工作視窗", + "對話內容", +} + + +def load_latest_ai_agent_action_owner_acceptance_event_bus( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed P2-411 no-write acceptance event bus snapshot.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent action owner acceptance event bus 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_sources(payload, label) + _require_truth(payload, label) + _require_owner_acceptance_lanes(payload, label) + _require_handoff_event_templates(payload, label) + _require_rag_memory_proposals(payload, label) + _require_verifier_gates(payload, label) + _require_activation_boundaries(payload, label) + _require_redaction_contract(payload, label) + _require_rollups(payload, label) + _require_no_forbidden_public_terms(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + expected = { + "overall_completion_percent": 100, + "current_priority": "P0", + "current_task_id": _EXPECTED_CURRENT_TASK, + "next_task_id": _EXPECTED_NEXT_TASK, + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_sources(payload: dict[str, Any], label: str) -> None: + if not payload.get("source_refs"): + raise ValueError(f"{label}: source_refs must not be empty") + sources = payload.get("source_readbacks") or [] + schemas = {item.get("source_schema_version") for item in sources} + missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas) + if missing: + raise ValueError(f"{label}: missing source schemas: {missing}") + for item in sources: + readback_id = item.get("readback_id") or "" + for field in ("source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"): + if not item.get(field): + raise ValueError(f"{label}: source readback {readback_id} missing {field}") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("event_bus_truth") or {} + missing_true = sorted(flag for flag in _TRUE_TRUTH_FLAGS if truth.get(flag) is not True) + if missing_true: + raise ValueError(f"{label}: event bus truth flags must remain true: {missing_true}") + unsafe_false = sorted(flag for flag in _FALSE_TRUTH_FLAGS if truth.get(flag) is not False) + if unsafe_false: + raise ValueError(f"{label}: event bus truth flags must remain false: {unsafe_false}") + non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: event bus live counts must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: event_bus_truth.truth_note is required") + + +def _require_owner_acceptance_lanes(payload: dict[str, Any], label: str) -> None: + lanes = payload.get("owner_acceptance_lanes") or [] + if len(lanes) < 1: + raise ValueError(f"{label}: owner_acceptance_lanes must not be empty") + source_ids = {item.get("readback_id") for item in payload.get("source_readbacks") or []} + risk_tiers = {lane.get("risk_tier") for lane in lanes} + if not {"medium", "high", "critical"}.issubset(risk_tiers): + raise ValueError(f"{label}: acceptance lanes must cover medium, high, and critical") + for lane in lanes: + lane_id = lane.get("lane_id") or "" + if lane.get("acceptance_status") not in { + "blocked_no_external_response", + "blocked_missing_fields", + "candidate_only_no_write", + }: + raise ValueError(f"{label}: lane {lane_id}.acceptance_status is invalid") + if lane.get("acceptance_decision") != "not_evaluated": + raise ValueError(f"{label}: lane {lane_id}.acceptance_decision must remain not_evaluated") + unsafe = sorted(flag for flag in _FALSE_LANE_FLAGS if lane.get(flag) is not False) + if unsafe: + raise ValueError(f"{label}: lane {lane_id} live flags must remain false: {unsafe}") + if lane.get("side_effect_count") != 0: + raise ValueError(f"{label}: lane {lane_id}.side_effect_count must remain zero") + for field in ("source_readback_ids", "required_owner_fields", "required_evidence_refs", "next_gate"): + if not lane.get(field): + raise ValueError(f"{label}: lane {lane_id} missing {field}") + missing_sources = sorted(set(lane.get("source_readback_ids") or []) - source_ids) + if missing_sources: + raise ValueError(f"{label}: lane {lane_id} references missing source readbacks: {missing_sources}") + + +def _require_handoff_event_templates(payload: dict[str, Any], label: str) -> None: + events = payload.get("handoff_event_templates") or [] + if len(events) < 1: + raise ValueError(f"{label}: handoff_event_templates must not be empty") + lane_ids = {item.get("lane_id") for item in payload.get("owner_acceptance_lanes") or []} + stages = {event.get("event_stage") for event in events} + required_stages = { + "owner_response_hold", + "owner_response_rejection", + "candidate_ready_no_write", + "handoff_request", + "rag_memory_proposal", + "no_send_rehearsal", + } + missing_stages = sorted(required_stages - stages) + if missing_stages: + raise ValueError(f"{label}: handoff event stages missing: {missing_stages}") + for event in events: + event_id = event.get("event_id") or "" + unsafe = sorted(flag for flag in _FALSE_EVENT_FLAGS if event.get(flag) is not False) + if unsafe: + raise ValueError(f"{label}: event {event_id} write/send flags must remain false: {unsafe}") + if event.get("side_effect_count") != 0: + raise ValueError(f"{label}: event {event_id}.side_effect_count must remain zero") + for field in ("source_lane_ids", "required_event_fields", "blocked_writes", "next_gate"): + if not event.get(field): + raise ValueError(f"{label}: event {event_id} missing {field}") + missing_lanes = sorted(set(event.get("source_lane_ids") or []) - lane_ids) + if missing_lanes: + raise ValueError(f"{label}: event {event_id} references missing lanes: {missing_lanes}") + + +def _require_rag_memory_proposals(payload: dict[str, Any], label: str) -> None: + proposals = payload.get("rag_memory_proposals") or [] + if len(proposals) < 1: + raise ValueError(f"{label}: rag_memory_proposals must not be empty") + event_ids = {item.get("event_id") for item in payload.get("handoff_event_templates") or []} + for proposal in proposals: + proposal_id = proposal.get("proposal_id") or "" + if proposal.get("proposal_status") != "proposal_only_no_write": + raise ValueError(f"{label}: proposal {proposal_id}.proposal_status must remain proposal_only_no_write") + unsafe = sorted(flag for flag in _FALSE_PROPOSAL_FLAGS if proposal.get(flag) is not False) + if unsafe: + raise ValueError(f"{label}: proposal {proposal_id} write flags must remain false: {unsafe}") + if proposal.get("side_effect_count") != 0: + raise ValueError(f"{label}: proposal {proposal_id}.side_effect_count must remain zero") + for field in ("target_store", "source_event_ids", "required_redaction_checks"): + if not proposal.get(field): + raise ValueError(f"{label}: proposal {proposal_id} missing {field}") + missing_events = sorted(set(proposal.get("source_event_ids") or []) - event_ids) + if missing_events: + raise ValueError(f"{label}: proposal {proposal_id} references missing events: {missing_events}") + + +def _require_verifier_gates(payload: dict[str, Any], label: str) -> None: + gates = payload.get("verifier_gates") or [] + if len(gates) < 1: + raise ValueError(f"{label}: verifier_gates must not be empty") + for gate in gates: + gate_id = gate.get("gate_id") or "" + if not gate.get("required_checks"): + raise ValueError(f"{label}: verifier gate {gate_id} missing required_checks") + if not gate.get("failure_if_missing"): + raise ValueError(f"{label}: verifier gate {gate_id} missing failure_if_missing") + for field in ("live_verifier_allowed", "receipt_write_allowed", "runtime_action_allowed"): + if gate.get(field) is not False: + raise ValueError(f"{label}: verifier gate {gate_id}.{field} must remain false") + + +def _require_activation_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = payload.get("activation_boundaries") or {} + missing = sorted(field for field in _TRUE_BOUNDARY_FLAGS if boundaries.get(field) is not True) + if missing: + raise ValueError(f"{label}: activation boundaries must remain true: {missing}") + unsafe = sorted(field for field in _FALSE_BOUNDARY_FLAGS if boundaries.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: activation boundaries must remain false: {unsafe}") + + +def _require_redaction_contract(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + required_false = { + "unsafe_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_prompt_display_allowed", + "work_window_transcript_display_allowed", + } + if contract.get("redaction_required") is not True: + raise ValueError(f"{label}: redaction_required must remain true") + unsafe = sorted(field for field in required_false if contract.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: display redaction flags must remain false: {unsafe}") + if not contract.get("allowed_display_fields") or not contract.get("blocked_display_fields"): + raise ValueError(f"{label}: display redaction contract must list allowed and blocked fields") + + +def _require_rollups(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + lanes = payload.get("owner_acceptance_lanes") or [] + events = payload.get("handoff_event_templates") or [] + proposals = payload.get("rag_memory_proposals") or [] + gates = payload.get("verifier_gates") or [] + sources = payload.get("source_readbacks") or [] + expected_counts = { + "source_readback_count": len(sources), + "owner_acceptance_lane_count": len(lanes), + "medium_lane_count": sum(1 for lane in lanes if lane.get("risk_tier") == "medium"), + "high_lane_count": sum(1 for lane in lanes if lane.get("risk_tier") == "high"), + "critical_lane_count": sum(1 for lane in lanes if lane.get("risk_tier") == "critical"), + "handoff_event_template_count": len(events), + "rag_memory_proposal_count": len(proposals), + "verifier_gate_count": len(gates), + "required_owner_field_count": sum(len(lane.get("required_owner_fields") or []) for lane in lanes), + "blocked_runtime_action_count": len( + { + blocked + for event in events + for blocked in event.get("blocked_writes") or [] + } + ), + } + mismatches = _mismatches(rollups, expected_counts) + if mismatches: + raise ValueError(f"{label}: rollup counts mismatch: {mismatches}") + non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: live write/send rollups must remain zero: {non_zero}") + + +def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None: + haystack = json.dumps(payload, ensure_ascii=False) + hits = sorted(term for term in _FORBIDDEN_PUBLIC_TERMS if term in haystack) + if hits: + raise ValueError(f"{label}: forbidden public terms detected: {hits}") + + +def _mismatches(source: dict[str, Any], expected: dict[str, Any]) -> dict[str, Any]: + return { + field: {"expected": value, "actual": source.get(field)} + for field, value in expected.items() + if source.get(field) != value + } diff --git a/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py b/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py new file mode 100644 index 000000000..b07eaae4a --- /dev/null +++ b/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_action_owner_acceptance_event_bus import ( + load_latest_ai_agent_action_owner_acceptance_event_bus, +) + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_COMMITTED_SNAPSHOT = ( + _REPO_ROOT + / "docs" + / "evaluations" + / "ai_agent_action_owner_acceptance_event_bus_2026-06-19.json" +) + + +def test_load_latest_ai_agent_action_owner_acceptance_event_bus_reads_newest_file(tmp_path): + older = _snapshot(generated_at="2026-06-19T02:00:00+08:00") + newer = _snapshot(generated_at="2026-06-19T02:10:00+08:00") + (tmp_path / "ai_agent_action_owner_acceptance_event_bus_2026-06-18.json").write_text( + json.dumps(older), + encoding="utf-8", + ) + (tmp_path / "ai_agent_action_owner_acceptance_event_bus_2026-06-19.json").write_text( + json.dumps(newer), + encoding="utf-8", + ) + + loaded = load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + assert loaded["generated_at"] == "2026-06-19T02:10:00+08:00" + assert loaded["program_status"]["current_task_id"] == "P2-411" + assert loaded["program_status"]["next_task_id"] == "P2-412" + assert loaded["rollups"]["owner_acceptance_lane_count"] == 6 + assert loaded["rollups"]["handoff_event_template_count"] == 6 + assert loaded["rollups"]["rag_memory_proposal_count"] == 4 + assert loaded["rollups"]["event_bus_publish_count"] == 0 + + +def test_ai_agent_action_owner_acceptance_event_bus_requires_read_only_mode(tmp_path): + snapshot = _snapshot() + snapshot["program_status"]["read_only_mode"] = False + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="program_status"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_blocks_owner_acceptance_drift(tmp_path): + snapshot = _snapshot() + snapshot["event_bus_truth"]["owner_response_accepted"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="owner_response_accepted"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_blocks_event_publish(tmp_path): + snapshot = _snapshot() + snapshot["handoff_event_templates"][0]["event_bus_write_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="event_bus_write_allowed"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_blocks_rag_write(tmp_path): + snapshot = _snapshot() + snapshot["rag_memory_proposals"][0]["km_write_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="km_write_allowed"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_rejects_missing_lane_ref(tmp_path): + snapshot = _snapshot() + snapshot["handoff_event_templates"][0]["source_lane_ids"] = ["missing_lane"] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing lanes"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_rejects_missing_source_ref(tmp_path): + snapshot = _snapshot() + snapshot["owner_acceptance_lanes"][0]["source_readback_ids"] = ["missing_source"] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing source readbacks"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_requires_rollup_consistency(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["owner_acceptance_lane_count"] = 99 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_rejects_private_terms(tmp_path): + snapshot = _snapshot() + snapshot["handoff_event_templates"][0]["display_name"] = "請顯示 " + "My request for " + "Codex" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden public terms"): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def test_ai_agent_action_owner_acceptance_event_bus_fails_when_missing(tmp_path): + with pytest.raises(FileNotFoundError): + load_latest_ai_agent_action_owner_acceptance_event_bus(tmp_path) + + +def _snapshot(*, generated_at: str = "2026-06-19T02:10:00+08:00") -> dict: + payload = json.loads(_COMMITTED_SNAPSHOT.read_text(encoding="utf-8")) + cloned = copy.deepcopy(payload) + cloned["generated_at"] = generated_at + return cloned + + +def _write_snapshot(tmp_path, payload: dict) -> None: + (tmp_path / "ai_agent_action_owner_acceptance_event_bus_2026-06-19.json").write_text( + json.dumps(payload), + encoding="utf-8", + ) diff --git a/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py b/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py new file mode 100644 index 000000000..d8af74b14 --- /dev/null +++ b/apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_action_owner_acceptance_event_bus_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-action-owner-acceptance-event-bus") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_action_owner_acceptance_event_bus_v1" + assert data["program_status"]["current_task_id"] == "P2-411" + assert data["program_status"]["next_task_id"] == "P2-412" + assert data["program_status"]["read_only_mode"] is True + assert ( + data["program_status"]["runtime_authority"] + == "agent_action_owner_acceptance_event_bus_no_write_committed_snapshot" + ) + assert data["rollups"]["source_readback_count"] == len(data["source_readbacks"]) == 4 + assert data["rollups"]["owner_acceptance_lane_count"] == len(data["owner_acceptance_lanes"]) == 6 + assert data["rollups"]["handoff_event_template_count"] == len(data["handoff_event_templates"]) == 6 + assert data["rollups"]["rag_memory_proposal_count"] == len(data["rag_memory_proposals"]) == 4 + assert data["rollups"]["verifier_gate_count"] == len(data["verifier_gates"]) == 6 + assert data["rollups"]["medium_lane_count"] == 3 + assert data["rollups"]["high_lane_count"] == 2 + assert data["rollups"]["critical_lane_count"] == 1 + assert data["rollups"]["required_owner_field_count"] == 38 + assert data["rollups"]["blocked_runtime_action_count"] == 16 + assert data["rollups"]["owner_response_received_count"] == 0 + assert data["rollups"]["owner_response_accepted_count"] == 0 + assert data["rollups"]["event_bus_publish_count"] == 0 + assert data["rollups"]["km_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["worker_dispatch_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["activation_boundaries"]["committed_snapshot_read_allowed"] is True + assert data["activation_boundaries"]["event_bus_publish_enabled"] is False + assert data["activation_boundaries"]["telegram_send_enabled"] is False + assert data["activation_boundaries"]["km_write_enabled"] is False + assert data["display_redaction_contract"]["redaction_required"] is True + assert data["display_redaction_contract"]["work_window_transcript_display_allowed"] is False diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index cb5284d2c..6bbaf1b79 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4147,6 +4147,76 @@ "writeback_blocked": "writeback blocked" } }, + "actionOwnerEventBus": { + "title": "P2-411 Owner Acceptance / 交接事件總線", + "subtitle": "{current} → {next};owner 驗收 lane {lanes};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "no-write 事件總線", + "events": "交接事件 {count}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "lanes": "Owner 驗收 lane", + "medium": "中風險", + "high": "高風險", + "critical": "Critical", + "events": "交接事件", + "rag": "RAG 提案", + "gates": "Verifier gates", + "fields": "Owner 欄位", + "live": "Live 寫入" + }, + "sections": { + "lanes": "Owner acceptance lanes", + "boundaries": "No-write 邊界", + "events": "交接事件", + "rag": "RAG 記憶提案", + "gates": "Verifier gates", + "truth": "事件總線 truth" + }, + "labels": { + "fields": "owner 欄位 {count}", + "sideEffects": "side effects {count}", + "eventBus": "事件總線 publish", + "publishTotal": "publish total {count}", + "ragWrite": "RAG / KM 寫入", + "ragDetail": "proposal {count}", + "telegram": "Telegram 實發", + "queueBotWorker": "queue {queue} / bot {bot} / worker {worker}", + "proposal": "status {status}", + "generated": "generated {generated}", + "redaction": "redaction {value}", + "ownerAccepted": "owner accepted {count}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "sre": "SRE", + "security": "Security", + "devops": "DevOps" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + }, + "statuses": { + "blocked_no_external_response": "no external response", + "blocked_missing_fields": "missing fields", + "candidate_only_no_write": "candidate only" + }, + "stages": { + "owner_response_hold": "owner hold", + "owner_response_rejection": "owner rejection", + "candidate_ready_no_write": "candidate ready", + "handoff_request": "handoff request", + "rag_memory_proposal": "RAG proposal", + "no_send_rehearsal": "no-send rehearsal" + } + }, "hostRunawayAiops": { "title": "P3-009 Host Runaway AIOps 閉環", "subtitle": "{current} → {next};閉環階段 {stages};阻擋 runtime 操作 {blocked}。", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index cb5284d2c..6bbaf1b79 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4147,6 +4147,76 @@ "writeback_blocked": "writeback blocked" } }, + "actionOwnerEventBus": { + "title": "P2-411 Owner Acceptance / 交接事件總線", + "subtitle": "{current} → {next};owner 驗收 lane {lanes};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "no-write 事件總線", + "events": "交接事件 {count}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "lanes": "Owner 驗收 lane", + "medium": "中風險", + "high": "高風險", + "critical": "Critical", + "events": "交接事件", + "rag": "RAG 提案", + "gates": "Verifier gates", + "fields": "Owner 欄位", + "live": "Live 寫入" + }, + "sections": { + "lanes": "Owner acceptance lanes", + "boundaries": "No-write 邊界", + "events": "交接事件", + "rag": "RAG 記憶提案", + "gates": "Verifier gates", + "truth": "事件總線 truth" + }, + "labels": { + "fields": "owner 欄位 {count}", + "sideEffects": "side effects {count}", + "eventBus": "事件總線 publish", + "publishTotal": "publish total {count}", + "ragWrite": "RAG / KM 寫入", + "ragDetail": "proposal {count}", + "telegram": "Telegram 實發", + "queueBotWorker": "queue {queue} / bot {bot} / worker {worker}", + "proposal": "status {status}", + "generated": "generated {generated}", + "redaction": "redaction {value}", + "ownerAccepted": "owner accepted {count}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "sre": "SRE", + "security": "Security", + "devops": "DevOps" + }, + "riskTiers": { + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical" + }, + "statuses": { + "blocked_no_external_response": "no external response", + "blocked_missing_fields": "missing fields", + "candidate_only_no_write": "candidate only" + }, + "stages": { + "owner_response_hold": "owner hold", + "owner_response_rejection": "owner rejection", + "candidate_ready_no_write": "candidate ready", + "handoff_request": "handoff request", + "rag_memory_proposal": "RAG proposal", + "no_send_rehearsal": "no-send rehearsal" + } + }, "hostRunawayAiops": { "title": "P3-009 Host Runaway AIOps 閉環", "subtitle": "{current} → {next};閉環階段 {stages};阻擋 runtime 操作 {blocked}。", 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 ca5472f90..a922c5d9d 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 @@ -51,6 +51,7 @@ import { type AiAgentLowMediumRiskWhitelistSnapshot, type AiAgentHighRiskOwnerReviewQueueSnapshot, type AiAgentActionAuditLedgerSnapshot, + type AiAgentActionOwnerAcceptanceEventBusSnapshot, type HostRunawayAiopsLoopReadinessSnapshot, type AiAgentCandidateOperationDryRunEvidenceSnapshot, type AiAgentCriticReviewerResultCaptureSnapshot, @@ -729,6 +730,7 @@ export function AutomationInventoryTab() { const [lowMediumRiskWhitelist, setLowMediumRiskWhitelist] = useState(null) const [highRiskOwnerReviewQueue, setHighRiskOwnerReviewQueue] = useState(null) const [actionAuditLedger, setActionAuditLedger] = useState(null) + const [actionOwnerAcceptanceEventBus, setActionOwnerAcceptanceEventBus] = useState(null) const [hostRunawayAiops, setHostRunawayAiops] = useState(null) const [proactiveOperations, setProactiveOperations] = useState(null) const [interactionLearningProof, setInteractionLearningProof] = useState(null) @@ -825,6 +827,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentLowMediumRiskWhitelist(), apiClient.getAiAgentHighRiskOwnerReviewQueue(), apiClient.getAiAgentActionAuditLedger(), + apiClient.getAiAgentActionOwnerAcceptanceEventBus(), apiClient.getHostRunawayAiopsLoopReadiness(), apiClient.getAiAgentProactiveOperationsContract(), apiClient.getAiAgentInteractionLearningProof(), @@ -914,6 +917,7 @@ export function AutomationInventoryTab() { lowMediumRiskWhitelistResult, highRiskOwnerReviewQueueResult, actionAuditLedgerResult, + actionOwnerAcceptanceEventBusResult, hostRunawayAiopsResult, proactiveOperationsResult, interactionLearningProofResult, @@ -1000,6 +1004,7 @@ export function AutomationInventoryTab() { setLowMediumRiskWhitelist(lowMediumRiskWhitelistResult.status === 'fulfilled' ? lowMediumRiskWhitelistResult.value : null) setHighRiskOwnerReviewQueue(highRiskOwnerReviewQueueResult.status === 'fulfilled' ? highRiskOwnerReviewQueueResult.value : null) setActionAuditLedger(actionAuditLedgerResult.status === 'fulfilled' ? actionAuditLedgerResult.value : null) + setActionOwnerAcceptanceEventBus(actionOwnerAcceptanceEventBusResult.status === 'fulfilled' ? actionOwnerAcceptanceEventBusResult.value : null) setHostRunawayAiops(hostRunawayAiopsResult.status === 'fulfilled' ? hostRunawayAiopsResult.value : null) setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null) setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null) @@ -1711,6 +1716,59 @@ export function AutomationInventoryTab() { .slice(0, 5) }, [actionAuditLedger]) + const visibleActionOwnerAcceptanceLanes = useMemo(() => { + if (!actionOwnerAcceptanceEventBus) return [] + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...actionOwnerAcceptanceEventBus.owner_acceptance_lanes] + .sort((a, b) => { + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.lane_id.localeCompare(b.lane_id) + }) + .slice(0, 6) + }, [actionOwnerAcceptanceEventBus]) + + const visibleActionOwnerHandoffEvents = useMemo(() => { + if (!actionOwnerAcceptanceEventBus) return [] + const stagePriority = { + owner_response_rejection: 0, + owner_response_hold: 1, + no_send_rehearsal: 2, + handoff_request: 3, + rag_memory_proposal: 4, + candidate_ready_no_write: 5, + } as Record + return [...actionOwnerAcceptanceEventBus.handoff_event_templates] + .sort((a, b) => { + const leftStage = stagePriority[a.event_stage] ?? 6 + const rightStage = stagePriority[b.event_stage] ?? 6 + if (leftStage !== rightStage) return leftStage - rightStage + return a.event_id.localeCompare(b.event_id) + }) + .slice(0, 6) + }, [actionOwnerAcceptanceEventBus]) + + const visibleActionOwnerRagProposals = useMemo(() => { + if (!actionOwnerAcceptanceEventBus) return [] + return [...actionOwnerAcceptanceEventBus.rag_memory_proposals] + .sort((a, b) => a.proposal_id.localeCompare(b.proposal_id)) + .slice(0, 4) + }, [actionOwnerAcceptanceEventBus]) + + const visibleActionOwnerVerifierGates = useMemo(() => { + if (!actionOwnerAcceptanceEventBus) return [] + const agentPriority = { security: 0, openclaw: 1, hermes: 2, nemotron: 3, sre: 4, devops: 5 } as Record + return [...actionOwnerAcceptanceEventBus.verifier_gates] + .sort((a, b) => { + const leftAgent = agentPriority[a.owner_agent] ?? 6 + const rightAgent = agentPriority[b.owner_agent] ?? 6 + if (leftAgent !== rightAgent) return leftAgent - rightAgent + return a.gate_id.localeCompare(b.gate_id) + }) + .slice(0, 6) + }, [actionOwnerAcceptanceEventBus]) + const visibleReportRuntimeLanes = useMemo(() => { if (!reportRuntimeReadiness) return [] const priority = { blocked_by_runtime_gate: 0, ready_for_owner_review: 1 } as Record @@ -2643,7 +2701,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !highRiskOwnerReviewQueue || !actionAuditLedger || !hostRunawayAiops || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !highRiskOwnerReviewQueue || !actionAuditLedger || !actionOwnerAcceptanceEventBus || !hostRunawayAiops || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -2983,6 +3041,35 @@ export function AutomationInventoryTab() { + actionAuditLedger.rollups.kubectl_action_count + actionAuditLedger.rollups.destructive_operation_count ) + const actionOwnerEventBusOverall = actionOwnerAcceptanceEventBus.program_status.overall_completion_percent + const actionOwnerEventBusLanes = actionOwnerAcceptanceEventBus.rollups.owner_acceptance_lane_count + const actionOwnerEventBusMedium = actionOwnerAcceptanceEventBus.rollups.medium_lane_count + const actionOwnerEventBusHigh = actionOwnerAcceptanceEventBus.rollups.high_lane_count + const actionOwnerEventBusCritical = actionOwnerAcceptanceEventBus.rollups.critical_lane_count + const actionOwnerEventBusEvents = actionOwnerAcceptanceEventBus.rollups.handoff_event_template_count + const actionOwnerEventBusRag = actionOwnerAcceptanceEventBus.rollups.rag_memory_proposal_count + const actionOwnerEventBusGates = actionOwnerAcceptanceEventBus.rollups.verifier_gate_count + const actionOwnerEventBusFields = actionOwnerAcceptanceEventBus.rollups.required_owner_field_count + const actionOwnerEventBusBlocked = actionOwnerAcceptanceEventBus.rollups.blocked_runtime_action_count + const actionOwnerEventBusAccepted = actionOwnerAcceptanceEventBus.rollups.owner_response_accepted_count + const actionOwnerEventBusLiveTotal = ( + actionOwnerAcceptanceEventBus.rollups.event_bus_publish_count + + actionOwnerAcceptanceEventBus.rollups.audit_db_write_count + + actionOwnerAcceptanceEventBus.rollups.timeline_write_count + + actionOwnerAcceptanceEventBus.rollups.km_write_count + + actionOwnerAcceptanceEventBus.rollups.playbook_trust_write_count + + actionOwnerAcceptanceEventBus.rollups.gateway_queue_write_count + + actionOwnerAcceptanceEventBus.rollups.telegram_send_count + + actionOwnerAcceptanceEventBus.rollups.bot_api_call_count + + actionOwnerAcceptanceEventBus.rollups.worker_dispatch_count + + actionOwnerAcceptanceEventBus.rollups.receipt_production_write_count + + actionOwnerAcceptanceEventBus.rollups.production_write_count + + actionOwnerAcceptanceEventBus.rollups.secret_read_count + + actionOwnerAcceptanceEventBus.rollups.paid_api_call_count + + actionOwnerAcceptanceEventBus.rollups.host_write_count + + actionOwnerAcceptanceEventBus.rollups.kubectl_action_count + + actionOwnerAcceptanceEventBus.rollups.destructive_operation_count + ) const hostRunawayOverall = hostRunawayAiops.program_status.overall_completion_percent const hostRunawayStages = hostRunawayAiops.rollups.loop_stage_count const hostRunawayAlertLanes = hostRunawayAiops.rollups.alert_lane_count @@ -5023,6 +5110,180 @@ export function AutomationInventoryTab() {
+ +
+
+
+
+ +
+
+ + {t('actionOwnerEventBus.title')} + + + {t('actionOwnerEventBus.subtitle', { + current: actionOwnerAcceptanceEventBus.program_status.current_task_id, + next: actionOwnerAcceptanceEventBus.program_status.next_task_id, + lanes: actionOwnerEventBusLanes, + blocked: actionOwnerEventBusBlocked, + })} + +
+
+
+ + + +
+
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('actionOwnerEventBus.sections.lanes')} + {visibleActionOwnerAcceptanceLanes.map(lane => { + const tone = lane.risk_tier === 'critical' ? 'danger' : lane.risk_tier === 'high' ? 'warn' : 'ok' + return ( +
+
+ + {redactPublicText(lane.display_name)} + + +
+ + {redactPublicText(lane.required_owner_fields.slice(0, 5).join(' / '))} + +
+ + + + +
+ + {redactPublicText(lane.next_gate)} + +
+ ) + })} +
+ +
+
+ {t('actionOwnerEventBus.sections.boundaries')} +
+ + + +
+
+ +
+ {t('actionOwnerEventBus.sections.events')} +
+ {visibleActionOwnerHandoffEvents.slice(0, 4).map(event => ( + + ))} +
+
+ +
+ {t('actionOwnerEventBus.sections.rag')} +
+ {visibleActionOwnerRagProposals.map(proposal => ( + + ))} +
+
+ +
+ {t('actionOwnerEventBus.sections.gates')} +
+ {visibleActionOwnerVerifierGates.slice(0, 3).map(gate => ( + + ))} +
+
+ +
+ {t('actionOwnerEventBus.sections.truth')} + + {redactPublicText(actionOwnerAcceptanceEventBus.event_bus_truth.truth_note)} + +
+ + + +
+
+
+
+
+
+
diff --git a/apps/web/src/app/[locale]/observability/page.tsx b/apps/web/src/app/[locale]/observability/page.tsx index c6d7dffac..4c2f74cb6 100644 --- a/apps/web/src/app/[locale]/observability/page.tsx +++ b/apps/web/src/app/[locale]/observability/page.tsx @@ -579,8 +579,8 @@ export default function ObservabilityPage({ params }: { params: { locale: string detail={t('sections.assetLedgerDetail')} />
- {assetLedgerRows.map(row => ( - + {assetLedgerRows.map(({ key, ...row }) => ( + ))}
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 3dbd3e8cb..60430dc5b 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -364,6 +364,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentActionOwnerAcceptanceEventBus() { + const res = await fetch(`${API_BASE_URL}/agents/agent-action-owner-acceptance-event-bus`) + return handleResponse(res) + }, + async getHostRunawayAiopsLoopReadiness() { const res = await fetch(`${API_BASE_URL}/agents/agent-host-runaway-aiops-loop-readiness`) return handleResponse(res) @@ -3932,6 +3937,220 @@ export interface AiAgentActionAuditLedgerSnapshot { }> } +export interface AiAgentActionOwnerAcceptanceEventBusSnapshot { + schema_version: 'ai_agent_action_owner_acceptance_event_bus_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' + current_task_id: 'P2-411' + next_task_id: 'P2-412' + read_only_mode: true + runtime_authority: 'agent_action_owner_acceptance_event_bus_no_write_committed_snapshot' + status_note: string + } + source_refs: string[] + source_readbacks: Array<{ + readback_id: string + source_schema_version: string + source_ref: string + endpoint: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + status: string + key_readback: string + next_action: string + }> + event_bus_truth: { + p2_409_owner_queue_loaded: true + p2_410_audit_ledger_loaded: true + communication_contract_loaded: true + war_room_loaded: true + owner_acceptance_envelope_required: true + handoff_protocol_ready: true + rag_memory_proposal_ready: true + event_bus_no_write_mode: true + redacted_evidence_only: true + high_critical_human_gate_required: true + low_medium_owner_scope_required_before_worker: true + owner_response_received: false + owner_response_accepted: false + owner_response_rejected: false + external_response_ingested: false + event_bus_publish_enabled: false + audit_db_write_enabled: false + timeline_write_enabled: false + km_write_enabled: false + playbook_trust_write_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + worker_dispatch_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + destructive_operation_enabled: false + owner_response_received_count_24h: number + owner_response_accepted_count_24h: number + owner_response_rejected_count_24h: number + external_response_ingested_count_24h: number + event_bus_publish_count_24h: number + audit_db_write_count_24h: number + timeline_write_count_24h: number + km_write_count_24h: number + playbook_trust_write_count_24h: number + gateway_queue_write_count_24h: number + telegram_send_count_24h: number + bot_api_call_count_24h: number + worker_dispatch_count_24h: number + receipt_production_write_count_24h: number + production_write_count_24h: number + secret_read_count_24h: number + paid_api_call_count_24h: number + host_write_count_24h: number + kubectl_action_count_24h: number + destructive_operation_count_24h: number + truth_note: string + } + owner_acceptance_lanes: Array<{ + lane_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + source_readback_ids: string[] + required_owner_fields: string[] + required_evidence_refs: string[] + acceptance_status: 'blocked_no_external_response' | 'blocked_missing_fields' | 'candidate_only_no_write' + acceptance_decision: 'not_evaluated' + response_received: false + acceptance_passed: false + acceptance_rejected: false + runtime_write_allowed: false + event_bus_publish_allowed: false + telegram_send_allowed: false + rag_write_allowed: false + side_effect_count: number + next_gate: string + }> + handoff_event_templates: Array<{ + event_id: string + display_name: string + producer_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + consumer_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + event_stage: string + risk_tier: 'low' | 'medium' | 'high' | 'critical' + source_lane_ids: string[] + required_event_fields: string[] + blocked_writes: string[] + event_bus_write_allowed: false + audit_db_write_allowed: false + timeline_write_allowed: false + km_write_allowed: false + playbook_trust_write_allowed: false + gateway_queue_write_allowed: false + telegram_send_allowed: false + production_write_allowed: false + side_effect_count: number + next_gate: string + }> + rag_memory_proposals: Array<{ + proposal_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + target_store: string + source_event_ids: string[] + required_redaction_checks: string[] + proposal_status: 'proposal_only_no_write' + km_write_allowed: false + playbook_trust_write_allowed: false + embedding_write_allowed: false + side_effect_count: number + }> + verifier_gates: Array<{ + gate_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + required_checks: string[] + failure_if_missing: string + live_verifier_allowed: false + receipt_write_allowed: false + runtime_action_allowed: false + }> + activation_boundaries: { + committed_snapshot_read_allowed: true + owner_acceptance_lane_preview_allowed: true + handoff_event_template_preview_allowed: true + rag_memory_proposal_preview_allowed: true + governance_ui_projection_allowed: true + event_bus_publish_enabled: false + audit_db_write_enabled: false + timeline_write_enabled: false + km_write_enabled: false + playbook_trust_write_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + worker_dispatch_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + destructive_operation_enabled: false + } + display_redaction_contract: { + redaction_required: true + unsafe_payload_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + raw_prompt_display_allowed: false + work_window_transcript_display_allowed: false + allowed_display_fields: string[] + blocked_display_fields: string[] + } + rollups: { + source_readback_count: number + owner_acceptance_lane_count: number + medium_lane_count: number + high_lane_count: number + critical_lane_count: number + handoff_event_template_count: number + rag_memory_proposal_count: number + verifier_gate_count: number + required_owner_field_count: number + blocked_runtime_action_count: number + owner_response_received_count: number + owner_response_accepted_count: number + owner_response_rejected_count: number + external_response_ingested_count: number + event_bus_publish_count: number + audit_db_write_count: number + timeline_write_count: number + km_write_count: number + playbook_trust_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + bot_api_call_count: number + worker_dispatch_count: number + receipt_production_write_count: number + production_write_count: number + secret_read_count: number + paid_api_call_count: number + host_write_count: number + kubectl_action_count: number + destructive_operation_count: number + } + next_actions: Array<{ + task_id: string + priority: 'P0' | 'P1' | 'P2' | 'P3' + summary: string + gate: string + }> +} + export interface AiAgentReportAutomationReviewSnapshot { schema_version: 'ai_agent_report_automation_review_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index dfb8defab..8c246e87e 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,49 @@ +## 2026-06-19|P2-411 Owner Acceptance Event Bus 本地完成 + +**背景**:P2-409 已把 high / critical 動作固定成 Owner Review Queue,P2-410 已把 AI Agent 分類、拒收、no-send preview、high-risk pause 與 verifier receipt 固定成行動審計帳本;下一步必須讓 OpenClaw、Hermes、NemoTron 的接手、學習與 owner acceptance 不只停在敘述,而是有可讀回的 no-write event bus 基線。此段仍不得把「可視化」誤解成 event bus publish、RAG write、Telegram send 或 production write 已放行。 + +**完成內容**: +- 新增 `docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json`。 +- 新增 `docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json`,`generated_at=2026-06-19T02:10:00+08:00`。 +- 新增 `apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py` 與 `GET /api/v1/agents/agent-action-owner-acceptance-event-bus`。 +- 新增 `apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py` 與 `apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-411 卡片,顯示 owner acceptance lane、handoff event template、RAG memory proposal、verifier gate、truth note 與 live total `0`。 +- `apps/web/src/app/[locale]/observability/page.tsx` 順手修正既有 duplicate React `key` typecheck 錯誤:render 時先把 row `key` 解構出來,UI 行為不變。 + +**固定數字**: +- Source readback `4`:P2-409 high-risk owner queue、P2-410 action audit ledger、communication learning contract、12-Agent War Room。 +- Owner acceptance lane `6`:中低風險 worker 範圍、高風險 owner 封包、Telegram 出口、RAG 記憶學習、Agent 交接事件總線、Critical secret / 費用邊界。 +- Handoff event template `6`。 +- RAG memory proposal `4`。 +- Verifier gate `6`。 +- Required owner field `38`。 +- Blocked runtime action `16`。 +- Owner response received / accepted / rejected、external response ingested、event bus publish、audit DB write、timeline write、KM write、PlayBook trust write、Gateway queue write、Telegram send、Bot API call、worker dispatch、receipt production write、production write、secret read、paid API call、host write、kubectl action、destructive operation 全部 `0 / false`。 + +**本地驗證**: +- `python3 -m json.tool docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json`。 +- `python3 -m json.tool docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json`。 +- `PYTHONPATH=apps/api python3 -m py_compile apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py apps/api/src/api/v1/agents.py apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus.py apps/api/tests/test_ai_agent_action_owner_acceptance_event_bus_api.py`。 +- P2-409 + P2-410 + P2-411 regression:`35 passed`。 +- `pnpm --filter @awoooi/web typecheck` 通過。 +- `missing_in_en=0 extra_in_en=0 type_diff=0`。 +- `SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`、`SECURITY_MIRROR_PROGRESS_GUARD_OK`、`IWOOOS_CONFIG_CONTROL_GUARD_OK`。 +- `DOC_SECRET_SANITY_OK scanned_files=4`。 +- `git diff --check` 通過。 + +**完成度同步**: +- P2-411 Owner Acceptance / Agent Event Bus / RAG proposal no-write 基線:本地 `100%`,正式讀回 `0%`。 +- AgentOps 治理與可觀測基礎:`91% -> 92%`。 +- OpenClaw / Hermes / NemoTron 佈建布局:`45% -> 47%`。 +- Agent 主動溝通 / 接手 / 學習:設計證據維持 `100%`,runtime protocol `35% -> 38%`;event bus publish 與 learning write 仍未開。 +- Telegram Bot / TG 群組契約:`53% -> 54%`,實發仍 `0%`。 +- 中低風險自動化:`65% -> 66%`,auto worker 仍未開。 +- 高風險審核:`86% -> 87%`,owner accepted 仍為 `0`。 + +**下一步**:推送 Gitea main,等待 code-review / CD,完成 production API、governance desktop / mobile browser smoke 與部署 marker readback;正式驗證前不得宣稱 P2-411 已在 production 完成。 + +**邊界**:本段是 committed snapshot / API / governance UI / typecheck 的本地完成,不是 owner response accepted、event bus publish、audit DB write、timeline write、KM write、PlayBook trust update、Gateway queue write、Telegram send、Bot API call、worker dispatch、receipt production write、production write、secret read、paid API call、host write、kubectl action、destructive operation、runtime worker 或 action button。 + ## 2026-06-19|Telegram 告警可讀性防退化 Guard 完成 **背景**:Telegram 群組曾收到 Host CPU / root Node.js / Prisma generate 類告警時,訊息直接包含 process list、完整路徑、package JSON、URL 與過長命令列,值班者難以判讀,也容易把內部路徑、raw payload 或敏感片段帶到通知通道。本輪把既有 `TelegramGateway` AI 事件卡 formatter 補成可重跑 guard,避免未來退化。 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 415e4dd70..d67e6626b 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -13,13 +13,13 @@ | 項目 | 目前完成度 | 本次判讀 | 下一個有效動作 | |---|---:|---|---| | 本工作清單細化 | 100% | 已把所有工作流拆成 P0 / P1 / P2 / P3 | 同步 LOGBOOK 與 MASTER §8 | -| AgentOps 治理與可觀測基礎 | 91% | 已有 schema / snapshot / API / UI / gate;P2-407 已正式把日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成 no-write 分析草稿;P2-408 已正式部署中 / 低風險白名單、dry-run verifier、rollback proof、audit reason 與 production readback;P2-409 已正式完成高風險 Owner Review Queue production API / governance UI / desktop smoke;P2-410 已正式完成 action audit ledger production API / governance UI / desktop + mobile smoke | P2-411 Agent Event Bus 與 handoff protocol;補 Runs mobile smoke;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | -| OpenClaw / Hermes / NemoTron 佈建布局 | 45% | 目前是只讀 layout 與治理頁可視化,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | -| Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 35% | 互動證據、War Room 與 readback gate 已齊;Event Bus、RAG writeback、PlayBook trust 寫入仍未開 | 先做 no-write event stream,再做 Owner-approved writeback | +| AgentOps 治理與可觀測基礎 | 92% | 已有 schema / snapshot / API / UI / gate;P2-407 已正式把日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成 no-write 分析草稿;P2-408 已正式部署中 / 低風險白名單、dry-run verifier、rollback proof、audit reason 與 production readback;P2-409 已正式完成高風險 Owner Review Queue production API / governance UI / desktop smoke;P2-410 已正式完成 action audit ledger production API / governance UI / desktop + mobile smoke;P2-411 已本地完成 owner acceptance / handoff event / RAG proposal no-write 基線 | 推送 P2-411 正式讀回;補 Runs mobile smoke;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | +| OpenClaw / Hermes / NemoTron 佈建布局 | 47% | 目前是只讀 layout、治理頁可視化與 P2-411 event bus 接手協議,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | +| Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 38% | 互動證據、War Room、readback gate 與 P2-411 no-write handoff event / RAG proposal 基線已齊;Event Bus publish、RAG writeback、PlayBook trust 寫入仍未開 | 先完成 P2-411 正式讀回,再做 Owner-approved writeback | | 日報 / 週報 / 月報 | 可視化 100%,no-write 分析 100%,實發 0% | 報表批准包、P2-406B owner review、P2-407 no-write analyst 與治理頁已可見;Telegram 實發、receipt production write、AI analysis live runtime 仍為 0 | P2-408 白名單與 P2-406F no-send scheduler | -| Telegram Bot / TG 群組 | 契約 53%,實發 0% | no-send preview、dry-run、owner review gate、P2-406B receipt readback owner review、Telegram egress inventory / owner request draft、P2-409 高風險 queue 與 P2-410 no-send / no-new-bypass audit template 已串起;P2-410 治理頁已正式顯示 Telegram event / live total;live send / Bot API / Gateway queue 未批准 | P2-406D no-send envelope ledger、P2-406E failure-only digest route;收到合格 owner approval 後才評估 P2-406C one-message canary | -| 中低風險自動化 | 65% | P2-408 已正式部署 6 筆候選白名單、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason 與 3 類高風險分流;P2-409 已把 high / critical 與 medium live execution 風險接到 Owner Review Queue 並正式讀回;P2-410 已補 low / medium audit event、dry-run hold 與 verifier receipt gate,治理頁 projection 已正式驗證;實際 auto worker 仍未開 | P2-411 Event Bus;之後才評估 dry-run auto worker | -| 高風險審核 | 86% | P2-409 已正式完成 7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist;P2-410 已補 high-risk pause、critical rejection 與 owner queue audit event,治理頁 projection 已正式驗證;所有高風險 action 都 pause,owner accepted 仍為 0 | P2-411 owner response acceptance readback | +| Telegram Bot / TG 群組 | 契約 54%,實發 0% | no-send preview、dry-run、owner review gate、P2-406B receipt readback owner review、Telegram egress inventory / owner request draft、P2-409 高風險 queue、P2-410 no-send / no-new-bypass audit template 與 P2-411 Telegram 出口驗收 lane 已串起;live send / Bot API / Gateway queue 未批准 | P2-406D no-send envelope ledger、P2-406E failure-only digest route;收到合格 owner approval 後才評估 P2-406C one-message canary | +| 中低風險自動化 | 66% | P2-408 已正式部署 6 筆候選白名單、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason 與 3 類高風險分流;P2-409 已把 high / critical 與 medium live execution 風險接到 Owner Review Queue;P2-410 已補 low / medium audit event;P2-411 已本地建立中低風險 worker 範圍驗收 lane;實際 auto worker 仍未開 | P2-411 正式讀回;之後才評估 dry-run auto worker | +| 高風險審核 | 87% | P2-409 已正式完成 7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist;P2-410 已補 high-risk pause、critical rejection 與 owner queue audit event;P2-411 已本地建立 high / critical owner acceptance lanes;owner accepted 仍為 0 | P2-411 正式讀回與 P2-412 fixture-only rehearsal | | 市場主流 Agent 追蹤 | 55% | 已有市場治理頁與 weekly watch;需擴充成固定外部來源評分與回放 | P2-412 週期性 market watch + scorecard | | 版本生命週期自動化 | 45% | repo-only snapshot 與採用批准包已完成;安裝、升級、PR creation、host update 仍未開 | P2-413 版本情報與 no-write upgrade proposal | @@ -69,7 +69,7 @@ | 8 | P2-408 | P0 | 中 / 低風險自動處理白名單 | OpenClaw + SRE | 正式驗證完成 | `ai_agent_low_medium_risk_whitelist_v1` schema / snapshot / API / tests / governance UI 已正式部署;feature commit `b36f4b97`、deploy marker `cd1c4407`、Gitea code-review `#3209`、CD `#3208` success;production API 回 current `P2-408`、next `P2-409`、completion `100`;6 筆候選、3 low、3 medium、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason、3 類 high-risk redirect、3 個 owner gate、27 個 blocked runtime action;desktop / mobile smoke 可見 OpenClaw / Hermes / NemoTron、AwoooI SRE 戰情室與 `live total 0`;auto worker / Telegram / Gateway / Bot API / production write / secret read / paid API / host write / kubectl 仍為 0 | | 9 | P2-409 | P0 | 高風險 Owner Review Queue | OpenClaw | 正式驗證完成 | `ai_agent_high_risk_owner_review_queue_v1` schema / snapshot / API / tests / governance UI 已正式部署;deploy marker `38e60192`;production API 回 current `P2-409`、next `P2-410`、completion `100`;7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist、42 個 blocked runtime action;owner response accepted、live execution、Gateway queue、Telegram send、Bot API、receipt production write、production write、secret read、paid API、host write、kubectl、destructive operation 全部 `0` | | 10 | P2-410 | P0 | Agent action audit ledger | Hermes + Security | 正式驗證完成 | `ai_agent_action_audit_ledger_v1` schema / snapshot / API / tests / governance UI 已正式部署;API deploy marker `38e60192`、UI deploy marker `7a9e1cfd`;production API 回 current `P2-410`、next `P2-411`、completion `100`;desktop / mobile browser smoke 可見 P2-410、行動審計事件、Verifier receipt gates、`live write total 0`;7 個 source readback、8 個 audit event template、4 個 low / medium event、3 個 high-risk event、1 個 critical event、2 個 report gap event、2 個 Telegram event、5 個 verifier receipt gate、48 個 required audit field、23 個 blocked runtime action;audit DB / timeline / KM / PlayBook trust / Gateway / Telegram / Bot API / production write 全部 `0` | -| 11 | P2-411 | P1 | Agent Event Bus 與 handoff protocol | OpenClaw + Hermes + NemoTron | 待辦 | agent_message schema、handoff state、retry、dedup、trace_id,並承接 P2-410 audit event source ref | +| 11 | P2-411 | P1 | Owner acceptance / Agent Event Bus / RAG proposal no-write 基線 | OpenClaw + Hermes + NemoTron | 本地驗證完成,待正式讀回 | `ai_agent_action_owner_acceptance_event_bus_v1` schema / snapshot / API / tests / governance UI 已完成;6 條 owner acceptance lane、6 個 handoff event template、4 個 RAG memory proposal、6 個 verifier gate、38 個 required owner field、16 個 blocked runtime action;owner response received / accepted、event bus publish、KM / PlayBook trust write、Gateway queue、Telegram send、Bot API、worker dispatch、production write 全部 `0` | | 12 | P2-412 | P1 | 市場主流 AI Agent 定期評估 | Market + OpenClaw | 待辦 | 每週 primary-source watch、候選入池、scorecard、替換 gate | | 13 | P2-413 | P1 | AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期 | DevOps + NemoTron | 待辦 | 版本 inventory、release diff、升級建議、PR 草稿 lane、rollback plan | | 14 | P2-414 | P1 | MCP tool registry / capability attestation | Security + DevOps | 待辦 | 工具能力、風險、scope、owner、consent、blocked actions 可讀 | @@ -148,7 +148,7 @@ |---|---:|---|---| | Agent 市場治理 | 72% | 進行中 | `agent_market_governance_snapshot_v1`、API、UI 分頁、每週觀察流程 | | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | -| 工具 / 服務 / 套件 AI 自動化 | 99% | P2-004 依賴 / 供應鏈漂移監控讀回已完成,且已納入 P2-406B receipt readback owner review、P2-407 no-write analysis、P2-408 中 / 低風險白名單、P2-409 高風險 Owner Review Queue 與 P2-410 action audit ledger;Approvals / Runs / Alerts / Telegram 告警卡已能看到 KM / PlayBook / 腳本 / 排程 / Verifier 的沉澱狀態;P2-409 / P2-410 已正式驗證;下一主線是 P2-411 Event Bus 與 Observability / Tenants / Knowledge Base 同款總帳 | 新增 `dependency_supply_chain_drift_monitor_v1`、`ai_agent_receipt_readback_owner_review_v1`、`ai_agent_report_no_write_analysis_runtime_v1`、`ai_agent_low_medium_risk_whitelist_v1`、`ai_agent_high_risk_owner_review_queue_v1`、`ai_agent_action_audit_ledger_v1` schema / snapshot / API / tests;P2-408 已正式驗證 feature commit `b36f4b97` / deploy marker `cd1c4407`;P2-409 / P2-410 API 已正式驗證 deploy marker `38e60192`;P2-410 UI 已正式驗證 deploy marker `7a9e1cfd`;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | +| 工具 / 服務 / 套件 AI 自動化 | 99.1% | P2-004 依賴 / 供應鏈漂移監控讀回已完成,且已納入 P2-406B receipt readback owner review、P2-407 no-write analysis、P2-408 中 / 低風險白名單、P2-409 高風險 Owner Review Queue、P2-410 action audit ledger 與 P2-411 no-write owner acceptance event bus;Approvals / Runs / Alerts / Telegram 告警卡已能看到 KM / PlayBook / 腳本 / 排程 / Verifier 的沉澱狀態;P2-409 / P2-410 已正式驗證,P2-411 已本地驗證;下一主線是正式讀回與 Observability / Tenants / Knowledge Base 同款總帳 | 新增 `dependency_supply_chain_drift_monitor_v1`、`ai_agent_receipt_readback_owner_review_v1`、`ai_agent_report_no_write_analysis_runtime_v1`、`ai_agent_low_medium_risk_whitelist_v1`、`ai_agent_high_risk_owner_review_queue_v1`、`ai_agent_action_audit_ledger_v1`、`ai_agent_action_owner_acceptance_event_bus_v1` schema / snapshot / API / tests;P2-408 已正式驗證 feature commit `b36f4b97` / deploy marker `cd1c4407`;P2-409 / P2-410 API 已正式驗證 deploy marker `38e60192`;P2-410 UI 已正式驗證 deploy marker `7a9e1cfd`;P2-411 待正式讀回;仍不得外部 CVE / license / registry / Agent market lookup、不得升級、不得寫 lockfile、不得 Docker build、不得 Telegram 實發 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | | OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 到 P2-144 已完成只讀證據面、runtime / report / result-capture gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold / readback gate、release verifier preflight / owner review packet、release decision hold / readback、release decision next handoff、release decision input prep、12-Agent War Room、owner response 預檢與 owner response 回讀;P2-141 基線與 S4.9 owner release packet 補強皆已正式驗證,P2-142 12-Agent War Room 已完成 production readback 與 desktop / mobile smoke,P2-143 owner response 預檢已完成 production readback 與 in-app browser smoke,P2-144 owner response 回讀已完成 production API readback 與 desktop / mobile smoke。runtime worker、DB migration、production Redis consumer group、canonical runtime readback、live query、runtime score、result capture write、Telegram 實發、delivery receipt E2E、live report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、`docs/evaluations/ai_agent_result_capture_release_decision_owner_response_readback_2026-06-14.json`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個回覆讀回 lane、18 個 owner 必填欄位、6 個 readback validation check、6 個 rejection guard、5 個 operator action、等待外部回覆 `5`、未收件 lane `5`、正式寫入 / 發送 `0`;P2-142 feature commit `5de4b3f3`、deploy marker `1a2c9e36`、Gitea CD run `4232` success、production API readback、desktop / mobile in-app browser smoke;P2-143 feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、production API readback、desktop / mobile in-app browser smoke;MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | @@ -159,6 +159,7 @@ | P2-408 中 / 低風險自動處理白名單 | 100%(正式驗證完成) | 已把 P2-407 no-write 分析建議轉成中 / 低風險候選白名單、dry-run verifier、rollback proof、audit reason 與高風險分流,並完成 production API / desktop / mobile browser smoke;下一步是 P2-409 Owner Review Queue | `ai_agent_low_medium_risk_whitelist_v1` schema、`docs/evaluations/ai_agent_low_medium_risk_whitelist_2026-06-18.json`、`GET /api/v1/agents/agent-low-medium-risk-whitelist`、governance `automation-inventory` P2-408 卡片;feature commit `b36f4b97`、deploy marker `cd1c4407`、Gitea code-review `#3209` / CD `#3208` success;6 筆 whitelist candidate、3 low、3 medium、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason、3 類 high-risk redirect、3 個 owner gate、27 個 blocked runtime action;本地 API/service regression `12 passed`;production API assert PASS;desktop `1280x720` / mobile `390x844` 可見 P2-408、OpenClaw / Hermes / NemoTron、AwoooI SRE 戰情室、`live total 0`;console error `0`、水平溢位 `0`、工作視窗片語命中 `0`;web typecheck 因本 worktree 缺 `apps/web/node_modules` / `tsc` 未執行;auto worker / low risk execution / medium risk execution / Telegram send / Gateway queue / Bot API / receipt production write / production write / secret read / paid API / host write / kubectl / destructive operation 全部 `0 / false` | | P2-409 高風險 Owner Review Queue | 100%(正式驗證完成) | 已把 high / critical、Telegram / Gateway / Bot API、host / kubectl、secret / paid provider、report source gap work item write 與 OpenClaw 角色調整全部固定為 paused owner review,並完成 production API / governance UI / desktop smoke;下一步由 P2-410 audit ledger 與 P2-411 Event Bus 承接 | `ai_agent_high_risk_owner_review_queue_v1` schema、`docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json`、`GET /api/v1/agents/agent-high-risk-owner-review-queue`;deploy marker `38e60192`;7 個 source readback、7 個 queue item、5 個 high、2 個 critical、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist、42 個 blocked runtime action;本地 API/service regression `13 passed`;production API readback `HTTP 200`;owner response accepted / live execution / Gateway / Telegram / Bot API / production write / secret read / paid API / host write / kubectl / destructive operation 全部 `0 / false` | | P2-410 AI Agent action audit ledger | 100%(正式驗證完成) | 已把 AI Agent 分類、拒收、no-send preview、high-risk pause、critical rejection 與 result route blocked 固定成 immutable audit event template 與 verifier receipt gate;production API 已讀回,治理頁 action audit projection 已正式驗證;下一步是 P2-411 Event Bus | `ai_agent_action_audit_ledger_v1` schema、`docs/evaluations/ai_agent_action_audit_ledger_2026-06-19.json`、`GET /api/v1/agents/agent-action-audit-ledger`;API deploy marker `38e60192`、UI deploy marker `7a9e1cfd`;7 個 source readback、8 個 audit event template、4 個 low / medium event、3 個 high-risk event、1 個 critical event、2 個 report gap event、2 個 Telegram event、5 個 verifier receipt gate、48 個 required audit field、23 個 blocked runtime action;本地 API/service regression `11 passed`,P2-409 + P2-410 regression `24 passed`;production API readback `HTTP 200`;desktop / mobile governance smoke 可見 P2-410、行動審計事件、Verifier receipt gates、`live write total 0`;console error `0`、水平溢位 `false`、工作視窗片語 `0`;audit DB / timeline / KM / PlayBook trust / Gateway / Telegram / Bot API / production write 全部 `0 / false` | +| P2-411 Owner acceptance / Agent Event Bus / RAG proposal no-write 基線 | 100%(本地驗證完成,待正式讀回) | 已把 P2-409 高風險 queue、P2-410 action audit ledger、12-Agent War Room 與 communication learning contract 收斂成 owner acceptance lane、handoff event template、RAG memory proposal 與 verifier gate;治理頁可看到 AI Agent 互相接手、學習提案與阻擋原因,但 event bus publish / RAG write / Telegram send 尚未開 | `ai_agent_action_owner_acceptance_event_bus_v1` schema、`docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json`、`GET /api/v1/agents/agent-action-owner-acceptance-event-bus`;6 條 owner acceptance lane、6 個 handoff event template、4 個 RAG memory proposal、6 個 verifier gate、38 個 required owner field、16 個 blocked runtime action;本地 P2-409 + P2-410 + P2-411 regression `35 passed`、web typecheck、JSON parse、Python compile、i18n parity、source-control owner response guard、security mirror progress guard、IWOOOS config control guard、doc secret sanity、`git diff --check` 通過;owner response received / accepted、external response ingested、event bus publish、audit DB / timeline / KM / PlayBook trust write、Gateway queue、Telegram send、Bot API、worker dispatch、receipt / production write、secret、paid API、host、kubectl、destructive 全部 `0 / false` | | Owner response 預檢與拒收邊界 | 100% | P2-143 已完成正式部署與 production readback;承接 P2-141 input prep 與 P2-142 War Room,只建立 owner / verifier / rollback / maintenance / live-apply 五類外部回覆的 intake 預檢、必填欄位與拒收規則;正式 owner response 尚未收到、未接受、未寫入 | `ai_agent_result_capture_release_decision_owner_response_preflight_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-preflight`、feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、5 個 response intake lane、18 個 required owner field、6 個 validation check、6 個 rejection guard、5 個 operator action;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | Owner response 回讀狀態 | 100% | P2-144 已完成正式部署與 production readback;承接 P2-143 preflight,只讀回五類外部回覆仍未收到、未接受、未拒絕、未保存 | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個 response readback lane、18 個 required owner field、6 個 readback validation check、6 個 readback rejection guard、5 個 operator action、waiting external response `5`、no external response received `5`;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | diff --git a/docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json b/docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json new file mode 100644 index 000000000..3a02de23e --- /dev/null +++ b/docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json @@ -0,0 +1,560 @@ +{ + "schema_version": "ai_agent_action_owner_acceptance_event_bus_v1", + "generated_at": "2026-06-19T02:10:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P0", + "current_task_id": "P2-411", + "next_task_id": "P2-412", + "read_only_mode": true, + "runtime_authority": "agent_action_owner_acceptance_event_bus_no_write_committed_snapshot", + "status_note": "P2-411 承接 P2-409 高風險 Owner Review Queue 與 P2-410 行動審計帳本,把 owner response acceptance、Agent handoff event 與 RAG memory proposal 固定成 no-write event bus 基線;目前只允許 committed snapshot 與治理頁讀回,不 publish event、不寫 KM、不送 Telegram。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json", + "docs/evaluations/ai_agent_action_audit_ledger_2026-06-19.json", + "docs/evaluations/ai_agent_12_agent_war_room_2026-06-04.json", + "docs/evaluations/ai_agent_communication_learning_contract_2026-06-04.json" + ], + "source_readbacks": [ + { + "readback_id": "p2_409_high_risk_owner_queue", + "source_schema_version": "ai_agent_high_risk_owner_review_queue_v1", + "source_ref": "docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json", + "endpoint": "GET /api/v1/agents/agent-high-risk-owner-review-queue", + "owner_agent": "openclaw", + "status": "loaded", + "key_readback": "Queue item 7、approval packet 7、rejection guard 8、blocked runtime action 42;owner response received / accepted 皆為 0。", + "next_action": "P2-411 將 queue item 轉成 owner acceptance lane,仍不得離開 paused / blocked 狀態。" + }, + { + "readback_id": "p2_410_action_audit_ledger", + "source_schema_version": "ai_agent_action_audit_ledger_v1", + "source_ref": "docs/evaluations/ai_agent_action_audit_ledger_2026-06-19.json", + "endpoint": "GET /api/v1/agents/agent-action-audit-ledger", + "owner_agent": "nemotron", + "status": "loaded", + "key_readback": "Audit event template 8、verifier receipt gate 5、required audit fields 48;audit DB / timeline / KM / Telegram / production write 皆為 0。", + "next_action": "P2-411 將 audit event template 提升為 no-write handoff event template,不 publish runtime event bus。" + }, + { + "readback_id": "agent_communication_learning_contract", + "source_schema_version": "ai_agent_communication_learning_contract_v1", + "source_ref": "docs/evaluations/ai_agent_communication_learning_contract_2026-06-04.json", + "endpoint": "GET /api/v1/agents/agent-communication-learning-contract", + "owner_agent": "hermes", + "status": "loaded", + "key_readback": "OpenClaw / Hermes / NemoTron 溝通、學習與記錄契約已存在,但 learning write、KM write、Telegram send 與 runtime worker 尚未開。", + "next_action": "P2-411 只建立 RAG memory proposal lane,等待 owner acceptance 與 redaction gate。" + }, + { + "readback_id": "agent_12_war_room", + "source_schema_version": "ai_agent_12_agent_war_room_v1", + "source_ref": "docs/evaluations/ai_agent_12_agent_war_room_2026-06-04.json", + "endpoint": "GET /api/v1/agents/agent-12-agent-war-room", + "owner_agent": "openclaw", + "status": "loaded", + "key_readback": "12-Agent 分工與 handoff 概念已可視化;目前仍是戰情室 readback,沒有 runtime delegation write。", + "next_action": "P2-411 將 agent handoff 轉成可審計 event template,保持 no-write。" + } + ], + "event_bus_truth": { + "p2_409_owner_queue_loaded": true, + "p2_410_audit_ledger_loaded": true, + "communication_contract_loaded": true, + "war_room_loaded": true, + "owner_acceptance_envelope_required": true, + "handoff_protocol_ready": true, + "rag_memory_proposal_ready": true, + "event_bus_no_write_mode": true, + "redacted_evidence_only": true, + "high_critical_human_gate_required": true, + "low_medium_owner_scope_required_before_worker": true, + "owner_response_received": false, + "owner_response_accepted": false, + "owner_response_rejected": false, + "external_response_ingested": false, + "event_bus_publish_enabled": false, + "audit_db_write_enabled": false, + "timeline_write_enabled": false, + "km_write_enabled": false, + "playbook_trust_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "worker_dispatch_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false, + "owner_response_received_count_24h": 0, + "owner_response_accepted_count_24h": 0, + "owner_response_rejected_count_24h": 0, + "external_response_ingested_count_24h": 0, + "event_bus_publish_count_24h": 0, + "audit_db_write_count_24h": 0, + "timeline_write_count_24h": 0, + "km_write_count_24h": 0, + "playbook_trust_write_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_send_count_24h": 0, + "bot_api_call_count_24h": 0, + "worker_dispatch_count_24h": 0, + "receipt_production_write_count_24h": 0, + "production_write_count_24h": 0, + "secret_read_count_24h": 0, + "paid_api_call_count_24h": 0, + "host_write_count_24h": 0, + "kubectl_action_count_24h": 0, + "destructive_operation_count_24h": 0, + "truth_note": "P2-411 是 owner acceptance / handoff / RAG proposal 的 no-write event bus 基線;沒有任何外部正式回覆被收件或接受,也沒有任何事件 publish、KM 寫入、Telegram 實發、worker dispatch 或 production write。" + }, + "owner_acceptance_lanes": [ + { + "lane_id": "low_medium_worker_scope_acceptance", + "display_name": "中低風險 worker 範圍驗收", + "owner_agent": "openclaw", + "risk_tier": "medium", + "source_readback_ids": ["p2_410_action_audit_ledger"], + "required_owner_fields": ["owner_role", "approved_risk_scope", "allowed_candidate_ids", "dry_run_scope", "rollback_owner", "postcheck_plan"], + "required_evidence_refs": ["audit_low_risk_candidate_classified", "audit_medium_risk_dry_run_hold"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-412 fixture-only dry-run worker receipt rehearsal" + }, + { + "lane_id": "high_risk_owner_packet_acceptance", + "display_name": "高風險 owner 封包驗收", + "owner_agent": "openclaw", + "risk_tier": "high", + "source_readback_ids": ["p2_409_high_risk_owner_queue", "p2_410_action_audit_ledger"], + "required_owner_fields": ["owner_role", "decision", "decision_reason", "affected_scope", "approval_packet_id", "rollback_owner", "postcheck_evidence_ref"], + "required_evidence_refs": ["approval_packet_ref", "audit_high_risk_owner_queue_pause"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-412 fixture-only dry-run worker receipt rehearsal" + }, + { + "lane_id": "telegram_egress_acceptance", + "display_name": "Telegram 出口驗收", + "owner_agent": "hermes", + "risk_tier": "high", + "source_readback_ids": ["p2_409_high_risk_owner_queue", "p2_410_action_audit_ledger"], + "required_owner_fields": ["canonical_room_env", "message_shape_contract", "redaction_proof", "delivery_receipt_expectation", "dedupe_key", "rollback_owner"], + "required_evidence_refs": ["packet_high_live_telegram_gateway_send", "audit_sre_digest_no_send_preview", "audit_telegram_no_new_bypass_guard"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-412 no-send delivery rehearsal only" + }, + { + "lane_id": "rag_memory_learning_acceptance", + "display_name": "RAG 記憶學習驗收", + "owner_agent": "hermes", + "risk_tier": "medium", + "source_readback_ids": ["p2_410_action_audit_ledger", "agent_communication_learning_contract"], + "required_owner_fields": ["knowledge_scope", "source_event_ids", "redaction_attestation", "forgetting_policy", "rollback_owner", "verifier_plan"], + "required_evidence_refs": ["audit_result_route_writeback_blocked", "agent_communication_learning_contract"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-412 RAG fixture receipt rehearsal" + }, + { + "lane_id": "handoff_event_bus_acceptance", + "display_name": "Agent 交接事件總線驗收", + "owner_agent": "nemotron", + "risk_tier": "medium", + "source_readback_ids": ["p2_410_action_audit_ledger", "agent_12_war_room"], + "required_owner_fields": ["producer_agent", "consumer_agent", "handoff_reason", "accepted_scope", "verifier_gate_id", "failure_route"], + "required_evidence_refs": ["agent_12_war_room", "audit_result_route_writeback_blocked"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-412 event bus fixture publish rehearsal" + }, + { + "lane_id": "critical_secret_cost_acceptance", + "display_name": "Critical secret / 費用邊界驗收", + "owner_agent": "security", + "risk_tier": "critical", + "source_readback_ids": ["p2_409_high_risk_owner_queue", "p2_410_action_audit_ledger"], + "required_owner_fields": ["secret_name_only", "paid_provider_scope", "privacy_egress_scope", "cost_cap", "joint_owner", "rollback_owner", "audit_reason"], + "required_evidence_refs": ["packet_critical_secret_paid_provider_boundary", "audit_critical_runtime_action_rejected"], + "acceptance_status": "blocked_no_external_response", + "acceptance_decision": "not_evaluated", + "response_received": false, + "acceptance_passed": false, + "acceptance_rejected": false, + "runtime_write_allowed": false, + "event_bus_publish_allowed": false, + "telegram_send_allowed": false, + "rag_write_allowed": false, + "side_effect_count": 0, + "next_gate": "security and cost joint owner envelope" + } + ], + "handoff_event_templates": [ + { + "event_id": "event_owner_response_missing", + "display_name": "Owner response 缺口 hold 事件", + "producer_agent": "openclaw", + "consumer_agent": "hermes", + "event_stage": "owner_response_hold", + "risk_tier": "high", + "source_lane_ids": ["high_risk_owner_packet_acceptance", "telegram_egress_acceptance"], + "required_event_fields": ["lane_id", "missing_owner_fields", "blocked_action", "redacted_evidence_ref", "next_gate"], + "blocked_writes": ["event bus publish", "Gateway queue write", "Telegram send", "runtime worker dispatch"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "owner envelope completeness check" + }, + { + "event_id": "event_owner_response_rejected", + "display_name": "Owner response 拒收事件", + "producer_agent": "security", + "consumer_agent": "openclaw", + "event_stage": "owner_response_rejection", + "risk_tier": "critical", + "source_lane_ids": ["critical_secret_cost_acceptance"], + "required_event_fields": ["lane_id", "rejection_guard_id", "redaction_state", "cost_boundary_state", "operator_next_step"], + "blocked_writes": ["secret read", "paid API call", "provider switch", "production write"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "security owner redacted resubmission" + }, + { + "event_id": "event_owner_response_candidate_ready", + "display_name": "Owner response 候選 ready 事件", + "producer_agent": "openclaw", + "consumer_agent": "nemotron", + "event_stage": "candidate_ready_no_write", + "risk_tier": "medium", + "source_lane_ids": ["low_medium_worker_scope_acceptance"], + "required_event_fields": ["candidate_ids", "allowed_scope", "dry_run_only", "rollback_owner", "postcheck_plan"], + "blocked_writes": ["runtime worker dispatch", "receipt production write", "KM write", "PlayBook trust write"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "fixture-only dry-run receipt rehearsal" + }, + { + "event_id": "event_agent_handoff_requested", + "display_name": "Agent 交接請求事件", + "producer_agent": "nemotron", + "consumer_agent": "hermes", + "event_stage": "handoff_request", + "risk_tier": "medium", + "source_lane_ids": ["handoff_event_bus_acceptance"], + "required_event_fields": ["producer_agent", "consumer_agent", "handoff_reason", "accepted_scope", "failure_route", "receipt_expectation"], + "blocked_writes": ["event bus publish", "timeline write", "task assignment write", "Telegram send"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "handoff receipt fixture rehearsal" + }, + { + "event_id": "event_rag_memory_proposal", + "display_name": "RAG 記憶提案事件", + "producer_agent": "hermes", + "consumer_agent": "openclaw", + "event_stage": "rag_memory_proposal", + "risk_tier": "medium", + "source_lane_ids": ["rag_memory_learning_acceptance"], + "required_event_fields": ["proposal_id", "target_store", "redaction_checks", "forgetting_policy", "verifier_plan"], + "blocked_writes": ["KM write", "embedding write", "PlayBook trust write", "fine-tune dataset write"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "RAG proposal owner acceptance" + }, + { + "event_id": "event_telegram_no_send_rehearsal", + "display_name": "Telegram no-send rehearsal 事件", + "producer_agent": "hermes", + "consumer_agent": "sre", + "event_stage": "no_send_rehearsal", + "risk_tier": "high", + "source_lane_ids": ["telegram_egress_acceptance"], + "required_event_fields": ["preview_hash", "dedupe_key", "canonical_room_env", "no_send_boundary", "delivery_receipt_expectation"], + "blocked_writes": ["Gateway queue write", "Telegram send", "Bot API call", "receipt production write"], + "event_bus_write_allowed": false, + "audit_db_write_allowed": false, + "timeline_write_allowed": false, + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "no-send fixture receipt rehearsal" + } + ], + "rag_memory_proposals": [ + { + "proposal_id": "rag_acceptance_contract", + "display_name": "Owner acceptance 合約記憶提案", + "owner_agent": "hermes", + "target_store": "knowledge_entries", + "source_event_ids": ["event_owner_response_missing", "event_owner_response_candidate_ready"], + "required_redaction_checks": ["no secret value", "no unredacted prompt", "redacted evidence ref only"], + "proposal_status": "proposal_only_no_write", + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "embedding_write_allowed": false, + "side_effect_count": 0 + }, + { + "proposal_id": "rag_handoff_outcome", + "display_name": "Agent 交接結果記憶提案", + "owner_agent": "nemotron", + "target_store": "agent_handoff_memory", + "source_event_ids": ["event_agent_handoff_requested"], + "required_redaction_checks": ["agent ids only", "no private reasoning", "no unsafe payload"], + "proposal_status": "proposal_only_no_write", + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "embedding_write_allowed": false, + "side_effect_count": 0 + }, + { + "proposal_id": "rag_rejection_guard", + "display_name": "拒收規則記憶提案", + "owner_agent": "security", + "target_store": "security_rejection_playbooks", + "source_event_ids": ["event_owner_response_rejected"], + "required_redaction_checks": ["secret name only", "no token hash", "no payload sample"], + "proposal_status": "proposal_only_no_write", + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "embedding_write_allowed": false, + "side_effect_count": 0 + }, + { + "proposal_id": "rag_no_send_receipt", + "display_name": "No-send receipt 記憶提案", + "owner_agent": "sre", + "target_store": "notification_receipt_memory", + "source_event_ids": ["event_telegram_no_send_rehearsal"], + "required_redaction_checks": ["dedupe key only", "no chat id value", "no message payload"], + "proposal_status": "proposal_only_no_write", + "km_write_allowed": false, + "playbook_trust_write_allowed": false, + "embedding_write_allowed": false, + "side_effect_count": 0 + } + ], + "verifier_gates": [ + { + "gate_id": "gate_owner_envelope_complete", + "display_name": "Owner envelope 完整性", + "owner_agent": "openclaw", + "required_checks": ["owner_role exists", "decision exists", "affected_scope exists", "rollback_owner exists", "postcheck_plan exists"], + "failure_if_missing": "Owner envelope 欄位不完整時,只能維持 blocked_no_external_response。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + }, + { + "gate_id": "gate_redacted_evidence_only", + "display_name": "只允許脫敏 evidence", + "owner_agent": "security", + "required_checks": ["no secret value", "no raw payload", "no private reasoning", "metadata refs only"], + "failure_if_missing": "未遮罩資料不得進入 event template 或 RAG proposal。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + }, + { + "gate_id": "gate_no_event_bus_publish", + "display_name": "禁止 event bus publish", + "owner_agent": "nemotron", + "required_checks": ["event_bus_publish_count=0", "worker_dispatch_count=0", "timeline_write_count=0"], + "failure_if_missing": "任何 publish / dispatch / write 非 0 都不能宣稱 P2-411 no-write baseline。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + }, + { + "gate_id": "gate_no_send_no_queue", + "display_name": "No-send / no-queue 邊界", + "owner_agent": "sre", + "required_checks": ["gateway_queue_write_count=0", "telegram_send_count=0", "bot_api_call_count=0"], + "failure_if_missing": "Telegram 或 Gateway 任一實發非 0 時必須退回 high-risk owner queue。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + }, + { + "gate_id": "gate_rag_proposal_only", + "display_name": "RAG 只允許 proposal", + "owner_agent": "hermes", + "required_checks": ["km_write_count=0", "embedding_write_count=0", "playbook_trust_write_count=0"], + "failure_if_missing": "RAG 只能產 proposal,不得直接寫 knowledge store 或 trust score。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + }, + { + "gate_id": "gate_rollback_postcheck_required", + "display_name": "必須具備 rollback / postcheck", + "owner_agent": "sre", + "required_checks": ["rollback_owner exists", "postcheck_plan exists", "failure_route exists", "stop_condition exists"], + "failure_if_missing": "缺 rollback / postcheck 的 acceptance lane 不能進入 P2-412 fixture rehearsal。", + "live_verifier_allowed": false, + "receipt_write_allowed": false, + "runtime_action_allowed": false + } + ], + "activation_boundaries": { + "committed_snapshot_read_allowed": true, + "owner_acceptance_lane_preview_allowed": true, + "handoff_event_template_preview_allowed": true, + "rag_memory_proposal_preview_allowed": true, + "governance_ui_projection_allowed": true, + "event_bus_publish_enabled": false, + "audit_db_write_enabled": false, + "timeline_write_enabled": false, + "km_write_enabled": false, + "playbook_trust_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "worker_dispatch_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false + }, + "display_redaction_contract": { + "redaction_required": true, + "unsafe_payload_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_prompt_display_allowed": false, + "work_window_transcript_display_allowed": false, + "allowed_display_fields": ["lane_id", "display_name", "owner_agent", "risk_tier", "acceptance_status", "event_id", "proposal_id", "required_owner_fields", "blocked_writes", "next_gate"], + "blocked_display_fields": ["機密明文", "授權標頭", "unredacted Telegram payload", "private reasoning", "unredacted prompt", "internal collaboration content"] + }, + "rollups": { + "source_readback_count": 4, + "owner_acceptance_lane_count": 6, + "medium_lane_count": 3, + "high_lane_count": 2, + "critical_lane_count": 1, + "handoff_event_template_count": 6, + "rag_memory_proposal_count": 4, + "verifier_gate_count": 6, + "required_owner_field_count": 38, + "blocked_runtime_action_count": 16, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "external_response_ingested_count": 0, + "event_bus_publish_count": 0, + "audit_db_write_count": 0, + "timeline_write_count": 0, + "km_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "worker_dispatch_count": 0, + "receipt_production_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "paid_api_call_count": 0, + "host_write_count": 0, + "kubectl_action_count": 0, + "destructive_operation_count": 0 + }, + "next_actions": [ + { + "task_id": "P2-412", + "priority": "P0", + "summary": "建立 fixture-only dry-run worker receipt rehearsal,使用 P2-411 acceptance lane 和 handoff event template,但不 publish live event bus。", + "gate": "event_bus_publish=0 / worker_dispatch=0 / Telegram send=0 until owner acceptance is real and redacted" + }, + { + "task_id": "P2-413", + "priority": "P0", + "summary": "把 P2-411 no-write event bus 投影到 Runs、Work Items、SRE digest preview 與治理頁,但不新增 action button。", + "gate": "frontend projection only; no Gateway queue write or production receipt write" + } + ] +} diff --git a/docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json b/docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json new file mode 100644 index 000000000..4f755c2e7 --- /dev/null +++ b/docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json @@ -0,0 +1,242 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-agent-action-owner-acceptance-event-bus-v1", + "title": "AWOOOI AI Agent action owner acceptance event bus v1", + "description": "P2-411 承接 P2-409 / P2-410,建立 owner response acceptance、Agent handoff event 與 RAG memory proposal 的 no-write 基線。此 schema 只允許 committed snapshot、owner acceptance lane、handoff event template、RAG proposal 與 governance readback;不授權 event bus publish、audit DB、timeline、KM、PlayBook trust、Gateway queue、Telegram、Bot API、production write、secret read、host write、kubectl 或不可逆操作。", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "source_readbacks", + "event_bus_truth", + "owner_acceptance_lanes", + "handoff_event_templates", + "rag_memory_proposals", + "verifier_gates", + "activation_boundaries", + "display_redaction_contract", + "rollups", + "next_actions" + ], + "properties": { + "schema_version": { "type": "string", "const": "ai_agent_action_owner_acceptance_event_bus_v1" }, + "generated_at": { "type": "string", "minLength": 1 }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_priority", + "current_task_id", + "next_task_id", + "read_only_mode", + "runtime_authority", + "status_note" + ], + "properties": { + "overall_completion_percent": { "type": "integer", "const": 100 }, + "current_priority": { "type": "string", "const": "P0" }, + "current_task_id": { "type": "string", "const": "P2-411" }, + "next_task_id": { "type": "string", "const": "P2-412" }, + "read_only_mode": { "type": "boolean", "const": true }, + "runtime_authority": { "type": "string", "const": "agent_action_owner_acceptance_event_bus_no_write_committed_snapshot" }, + "status_note": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, + "source_readbacks": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/source_readback" } }, + "event_bus_truth": { "type": "object", "additionalProperties": { "type": ["boolean", "integer", "string"] } }, + "owner_acceptance_lanes": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/owner_acceptance_lane" } }, + "handoff_event_templates": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/handoff_event_template" } }, + "rag_memory_proposals": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/rag_memory_proposal" } }, + "verifier_gates": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/verifier_gate" } }, + "activation_boundaries": { "type": "object", "additionalProperties": { "type": "boolean" } }, + "display_redaction_contract": { "type": "object", "additionalProperties": { "type": ["boolean", "array", "string"] } }, + "rollups": { "type": "object", "additionalProperties": { "type": ["integer", "array"] } }, + "next_actions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/next_action" } } + }, + "$defs": { + "source_readback": { + "type": "object", + "required": [ + "readback_id", + "source_schema_version", + "source_ref", + "endpoint", + "owner_agent", + "status", + "key_readback", + "next_action" + ], + "properties": { + "readback_id": { "type": "string", "minLength": 1 }, + "source_schema_version": { "type": "string", "minLength": 1 }, + "source_ref": { "type": "string", "minLength": 1 }, + "endpoint": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "status": { "type": "string", "minLength": 1 }, + "key_readback": { "type": "string", "minLength": 1 }, + "next_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "owner_acceptance_lane": { + "type": "object", + "required": [ + "lane_id", + "display_name", + "owner_agent", + "risk_tier", + "source_readback_ids", + "required_owner_fields", + "required_evidence_refs", + "acceptance_status", + "acceptance_decision", + "response_received", + "acceptance_passed", + "acceptance_rejected", + "runtime_write_allowed", + "event_bus_publish_allowed", + "telegram_send_allowed", + "rag_write_allowed", + "side_effect_count", + "next_gate" + ], + "properties": { + "lane_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "risk_tier": { "type": "string", "enum": ["low", "medium", "high", "critical"] }, + "source_readback_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_owner_fields": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_evidence_refs": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "acceptance_status": { "type": "string", "enum": ["blocked_no_external_response", "blocked_missing_fields", "candidate_only_no_write"] }, + "acceptance_decision": { "type": "string", "const": "not_evaluated" }, + "response_received": { "type": "boolean", "const": false }, + "acceptance_passed": { "type": "boolean", "const": false }, + "acceptance_rejected": { "type": "boolean", "const": false }, + "runtime_write_allowed": { "type": "boolean", "const": false }, + "event_bus_publish_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "rag_write_allowed": { "type": "boolean", "const": false }, + "side_effect_count": { "type": "integer", "const": 0 }, + "next_gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "handoff_event_template": { + "type": "object", + "required": [ + "event_id", + "display_name", + "producer_agent", + "consumer_agent", + "event_stage", + "risk_tier", + "source_lane_ids", + "required_event_fields", + "blocked_writes", + "event_bus_write_allowed", + "audit_db_write_allowed", + "timeline_write_allowed", + "km_write_allowed", + "playbook_trust_write_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed", + "side_effect_count", + "next_gate" + ], + "properties": { + "event_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "producer_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "consumer_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "event_stage": { "type": "string", "minLength": 1 }, + "risk_tier": { "type": "string", "enum": ["low", "medium", "high", "critical"] }, + "source_lane_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_event_fields": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "blocked_writes": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "event_bus_write_allowed": { "type": "boolean", "const": false }, + "audit_db_write_allowed": { "type": "boolean", "const": false }, + "timeline_write_allowed": { "type": "boolean", "const": false }, + "km_write_allowed": { "type": "boolean", "const": false }, + "playbook_trust_write_allowed": { "type": "boolean", "const": false }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "production_write_allowed": { "type": "boolean", "const": false }, + "side_effect_count": { "type": "integer", "const": 0 }, + "next_gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "rag_memory_proposal": { + "type": "object", + "required": [ + "proposal_id", + "display_name", + "owner_agent", + "target_store", + "source_event_ids", + "required_redaction_checks", + "proposal_status", + "km_write_allowed", + "playbook_trust_write_allowed", + "embedding_write_allowed", + "side_effect_count" + ], + "properties": { + "proposal_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "target_store": { "type": "string", "minLength": 1 }, + "source_event_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_redaction_checks": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "proposal_status": { "type": "string", "const": "proposal_only_no_write" }, + "km_write_allowed": { "type": "boolean", "const": false }, + "playbook_trust_write_allowed": { "type": "boolean", "const": false }, + "embedding_write_allowed": { "type": "boolean", "const": false }, + "side_effect_count": { "type": "integer", "const": 0 } + }, + "additionalProperties": false + }, + "verifier_gate": { + "type": "object", + "required": [ + "gate_id", + "display_name", + "owner_agent", + "required_checks", + "failure_if_missing", + "live_verifier_allowed", + "receipt_write_allowed", + "runtime_action_allowed" + ], + "properties": { + "gate_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "type": "string", "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "required_checks": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "failure_if_missing": { "type": "string", "minLength": 1 }, + "live_verifier_allowed": { "type": "boolean", "const": false }, + "receipt_write_allowed": { "type": "boolean", "const": false }, + "runtime_action_allowed": { "type": "boolean", "const": false } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "required": ["task_id", "priority", "summary", "gate"], + "properties": { + "task_id": { "type": "string", "minLength": 1 }, + "priority": { "type": "string", "enum": ["P0", "P1", "P2", "P3"] }, + "summary": { "type": "string", "minLength": 1 }, + "gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + }, + "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 30bc36d09..5714b91b0 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 @@ -5178,6 +5178,19 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 **裁決:** P2-409 已正式驗證完成;P2-410 API 與治理頁 projection 已正式驗證完成。這不是 audit DB write、timeline write、KM write、PlayBook trust update、Gateway queue write、Telegram send、Bot API call、receipt production write、production write、secret read、paid API、host write、kubectl action、destructive operation、runtime worker 或 OpenClaw 角色替換。下一步進入 `P2-411`:Agent Event Bus 與 handoff protocol。 +### 2026-06-19 02:12 (台北) — §8 / P2-411 — 新增 Owner Acceptance / Agent Event Bus / RAG proposal no-write 基線 + +**觸發**:統帥要求能看見所有 AI Agent 的互動、溝通、學習與成長,而且高風險由人工審核、中低風險才可在 gate 內自動化;P2-409 已有 Owner Review Queue,P2-410 已有 action audit ledger,但仍缺一個「owner response acceptance、Agent handoff event、RAG memory proposal」的同一條 no-write 基線。 + +**已推進:** +- 新增 `docs/schemas/ai_agent_action_owner_acceptance_event_bus_v1.schema.json` 與 `docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json`。 +- 新增 `apps/api/src/services/ai_agent_action_owner_acceptance_event_bus.py` 與 `GET /api/v1/agents/agent-action-owner-acceptance-event-bus`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-411 卡片,顯示 owner acceptance lane、handoff event template、RAG memory proposal、verifier gate、redaction truth 與 live total `0`。 +- P2-411 固定 source readback `4`、owner acceptance lane `6`、handoff event template `6`、RAG memory proposal `4`、verifier gate `6`、required owner field `38`、blocked runtime action `16`。 +- 本地驗證:JSON parse、Python compile、P2-409 + P2-410 + P2-411 regression `35 passed`、web typecheck、i18n parity、source-control owner response guard、security mirror progress guard、IWOOOS config control guard、doc secret sanity 與 `git diff --check` 通過。 + +**裁決:** P2-411 是 committed snapshot / API / governance UI 的 owner acceptance event bus 基線,不是 owner response accepted、event bus publish、audit DB write、timeline write、KM write、PlayBook trust update、Gateway queue write、Telegram send、Bot API call、worker dispatch、receipt production write、production write、secret read、paid API call、host write、kubectl action、destructive operation、runtime worker 或 action button。正式讀回前不得宣稱 production 完成;下一步是 Gitea CD、production API / browser smoke,再由 `P2-412` 承接 fixture-only rehearsal。 + ### 2026-06-18 14:20 (台北) — §8 / Host CPU AIOps — 新增 110 runaway process 監控 / 告警 / PlayBook / gated remediation **觸發**:110 CPU 滿載已確認是跨專案 stockPlatform headless Chrome smoke 遺留 5 組 orphan process group,精準 SIGTERM 後 `REMAINING_AFTER_TERM=0`;後續 load 仍高則是 AWOOOI / VibeWork / 2026 World Cup Gitea Actions build/test。這證明泛用 `HostHighCpuLoad` 不足以支撐 AI 自動化產品,必須能把 orphan process、合法 CI load、Docker/Sentry/Harbor 事故分開。