diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py
index 2ec79a30a..ee81f3714 100644
--- a/apps/api/src/api/v1/agents.py
+++ b/apps/api/src/api/v1/agents.py
@@ -58,6 +58,9 @@ from src.services.ai_agent_deployment_layout import (
from src.services.ai_agent_proactive_operations_contract import (
load_latest_ai_agent_proactive_operations_contract,
)
+from src.services.ai_agent_tool_adoption_approval_package import (
+ load_latest_ai_agent_tool_adoption_approval_package,
+)
from src.services.ai_agent_version_freshness_snapshot import (
load_latest_ai_agent_version_freshness_snapshot,
)
@@ -614,6 +617,34 @@ async def get_agent_version_freshness_snapshot() -> dict[str, Any]:
) from exc
+@router.get(
+ "/agent-tool-adoption-approval-package",
+ response_model=dict[str, Any],
+ summary="取得 AI Agent 工具採用批准包",
+ description=(
+ "讀取最新已提交的 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包;"
+ "此端點不安裝工具、不寫 CI workflow、不下載漏洞資料庫、不查外部 registry、不升級套件、"
+ "不寫 lockfile、不 build/pull image、不建立 Gitea PR、不 auto merge、不發 Telegram、"
+ "不呼叫付費服務、不修改生產路由。"
+ ),
+)
+async def get_agent_tool_adoption_approval_package() -> dict[str, Any]:
+ """Return the latest read-only AI Agent tool adoption approval package."""
+ try:
+ return await asyncio.to_thread(load_latest_ai_agent_tool_adoption_approval_package)
+ 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_tool_adoption_approval_package_invalid", error=str(exc))
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail="AI Agent 工具採用批准包無效",
+ ) from exc
+
+
@router.get(
"/runtime-surface-inventory",
response_model=dict[str, Any],
diff --git a/apps/api/src/services/ai_agent_tool_adoption_approval_package.py b/apps/api/src/services/ai_agent_tool_adoption_approval_package.py
new file mode 100644
index 000000000..89322004d
--- /dev/null
+++ b/apps/api/src/services/ai_agent_tool_adoption_approval_package.py
@@ -0,0 +1,178 @@
+"""
+AI Agent tool adoption approval package snapshot.
+
+Loads the latest committed, read-only approval package for adopting tools such
+as Renovate, OSV-Scanner, Trivy, Syft, and Grype. This module never installs
+tools, writes CI workflows, downloads vulnerability databases, queries
+registries, creates PRs, sends Telegram messages, or changes production.
+"""
+
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Any
+
+from src.services.snapshot_paths import default_evaluations_dir
+
+_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__))
+_SNAPSHOT_PATTERN = "ai_agent_tool_adoption_approval_package_*.json"
+_SCHEMA_VERSION = "ai_agent_tool_adoption_approval_package_v1"
+_RUNTIME_AUTHORITY = "approval_package_only_no_tool_install_or_ci_change"
+
+
+def load_latest_ai_agent_tool_adoption_approval_package(
+ evaluations_dir: Path | None = None,
+) -> dict[str, Any]:
+ """Load the newest committed AI Agent tool adoption approval package."""
+ directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
+ candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
+ if not candidates:
+ raise FileNotFoundError(
+ f"no AI Agent tool adoption approval package snapshots found in {directory}"
+ )
+
+ latest = candidates[-1]
+ with latest.open(encoding="utf-8") as handle:
+ payload = json.load(handle)
+
+ if not isinstance(payload, dict):
+ raise ValueError(f"{latest}: expected JSON object")
+ _require_schema(payload, _SCHEMA_VERSION, str(latest))
+ _require_read_only_boundaries(payload, str(latest))
+ _require_rollup_consistency(payload, str(latest))
+ _require_tool_safety(payload, str(latest))
+ return payload
+
+
+def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None:
+ actual = payload.get("schema_version")
+ if actual != expected:
+ raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}")
+
+
+def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None:
+ program_status = payload.get("program_status") or {}
+ if program_status.get("read_only_mode") is not True:
+ raise ValueError(f"{label}: program_status.read_only_mode must be true")
+ if program_status.get("runtime_authority") != _RUNTIME_AUTHORITY:
+ raise ValueError(f"{label}: runtime_authority must stay {_RUNTIME_AUTHORITY}")
+
+ boundaries = payload.get("approval_boundaries") or {}
+ blocked_flags = {
+ "tool_install_allowed",
+ "ci_workflow_change_allowed",
+ "external_registry_lookup_allowed",
+ "vulnerability_database_download_allowed",
+ "package_upgrade_allowed",
+ "lockfile_write_allowed",
+ "docker_build_allowed",
+ "image_pull_allowed",
+ "gitea_pr_creation_allowed",
+ "auto_merge_allowed",
+ "telegram_direct_send_allowed",
+ "paid_service_enabled",
+ "production_route_change_allowed",
+ "secret_plaintext_allowed",
+ }
+ allowed = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False)
+ if allowed:
+ raise ValueError(f"{label}: approval boundaries must remain false: {allowed}")
+
+
+def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None:
+ tools = payload.get("tool_candidates") or []
+ sources = payload.get("source_evidence") or []
+ lanes = payload.get("adoption_lanes") or []
+ rollups = payload.get("rollups") or {}
+
+ expected_counts = {
+ "tool_count": len(tools),
+ "source_evidence_count": len(sources),
+ "adoption_lane_count": len(lanes),
+ }
+ mismatched = {
+ key: {"expected": expected, "actual": rollups.get(key)}
+ for key, expected in expected_counts.items()
+ if rollups.get(key) != expected
+ }
+ if mismatched:
+ raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}")
+
+ expected_lists = {
+ "approval_required_tool_ids": sorted(
+ tool.get("tool_id")
+ for tool in tools
+ if tool.get("adoption_status") == "approval_required"
+ ),
+ "ci_change_required_tool_ids": sorted(
+ tool.get("tool_id") for tool in tools if tool.get("ci_change_required") is True
+ ),
+ "secret_required_tool_ids": sorted(
+ tool.get("tool_id") for tool in tools if _requires_secret_review(tool)
+ ),
+ "external_data_required_tool_ids": sorted(
+ tool.get("tool_id") for tool in tools if _requires_external_data_gate(tool)
+ ),
+ }
+ for key, expected in expected_lists.items():
+ if sorted(rollups.get(key) or []) != expected:
+ raise ValueError(f"{label}: rollups.{key} mismatch")
+
+ zero_rollups = {
+ "tool_install_allowed_count",
+ "ci_change_allowed_count",
+ "auto_merge_allowed_count",
+ "telegram_direct_send_count",
+ }
+ nonzero = sorted(key for key in zero_rollups if rollups.get(key) != 0)
+ if nonzero:
+ raise ValueError(f"{label}: rollout safety counters must remain 0: {nonzero}")
+
+
+def _require_tool_safety(payload: dict[str, Any], label: str) -> None:
+ source_ids = {source.get("source_id") for source in payload.get("source_evidence") or []}
+ unsafe_tools = [
+ tool.get("tool_id")
+ for tool in payload.get("tool_candidates") or []
+ if tool.get("adoption_status") != "approval_required"
+ or not tool.get("approval_requirements")
+ or not tool.get("blocked_until_approval")
+ or not set(tool.get("official_source_refs") or []).issubset(source_ids)
+ or not tool.get("rollback_plan")
+ ]
+ if unsafe_tools:
+ raise ValueError(f"{label}: tools must stay approval-bound: {unsafe_tools}")
+
+ unsafe_lanes = [
+ lane.get("lane_id")
+ for lane in payload.get("adoption_lanes") or []
+ if not lane.get("approval_gate") or not lane.get("blocked_now")
+ ]
+ if unsafe_lanes:
+ raise ValueError(f"{label}: adoption lanes need approval gates: {unsafe_lanes}")
+
+ missing_required_fields = [
+ field.get("field_id")
+ for field in payload.get("approval_packet_fields") or []
+ if field.get("required") is True and not field.get("description")
+ ]
+ if missing_required_fields:
+ raise ValueError(f"{label}: approval fields need descriptions: {missing_required_fields}")
+
+
+def _requires_secret_review(tool: dict[str, Any]) -> bool:
+ requirement = str(tool.get("secret_requirement") or "").lower()
+ return "credentials" in requirement or "token" in requirement
+
+
+def _requires_external_data_gate(tool: dict[str, Any]) -> bool:
+ requirement = str(tool.get("external_data_requirement") or "").lower()
+ if requirement.startswith("no external"):
+ return False
+ return (
+ "currently blocked" in requirement
+ or "metadata required" in requirement
+ or "database required" in requirement
+ or "db required" in requirement
+ )
diff --git a/apps/api/tests/test_ai_agent_proactive_operations_contract.py b/apps/api/tests/test_ai_agent_proactive_operations_contract.py
index ae7fc7c4a..8865d6367 100644
--- a/apps/api/tests/test_ai_agent_proactive_operations_contract.py
+++ b/apps/api/tests/test_ai_agent_proactive_operations_contract.py
@@ -13,9 +13,9 @@ def test_load_latest_ai_agent_proactive_operations_contract_reads_committed_snap
data = load_latest_ai_agent_proactive_operations_contract()
assert data["schema_version"] == "ai_agent_proactive_operations_contract_v1"
- assert data["program_status"]["overall_completion_percent"] == 42
- assert data["program_status"]["current_task_id"] == "P2-402B"
- assert data["program_status"]["next_task_id"] == "P2-402C"
+ assert data["program_status"]["overall_completion_percent"] == 55
+ assert data["program_status"]["current_task_id"] == "P2-402C"
+ assert data["program_status"]["next_task_id"] == "P2-402D"
assert data["program_status"]["read_only_mode"] is True
assert data["program_status"]["runtime_authority"] == "contract_only_no_version_or_runtime_update"
assert data["approval_boundaries"]["runtime_version_update_allowed"] is False
diff --git a/apps/api/tests/test_ai_agent_proactive_operations_contract_api.py b/apps/api/tests/test_ai_agent_proactive_operations_contract_api.py
index 0e161656c..860901cff 100644
--- a/apps/api/tests/test_ai_agent_proactive_operations_contract_api.py
+++ b/apps/api/tests/test_ai_agent_proactive_operations_contract_api.py
@@ -16,9 +16,9 @@ def test_ai_agent_proactive_operations_contract_endpoint_returns_committed_snaps
assert response.status_code == 200
data = response.json()
assert data["schema_version"] == "ai_agent_proactive_operations_contract_v1"
- assert data["program_status"]["overall_completion_percent"] == 42
- assert data["program_status"]["current_task_id"] == "P2-402B"
- assert data["program_status"]["next_task_id"] == "P2-402C"
+ assert data["program_status"]["overall_completion_percent"] == 55
+ assert data["program_status"]["current_task_id"] == "P2-402C"
+ assert data["program_status"]["next_task_id"] == "P2-402D"
assert data["program_status"]["read_only_mode"] is True
assert data["approval_boundaries"]["runtime_version_update_allowed"] is False
assert data["approval_boundaries"]["package_upgrade_allowed"] is False
diff --git a/apps/api/tests/test_ai_agent_tool_adoption_approval_package.py b/apps/api/tests/test_ai_agent_tool_adoption_approval_package.py
new file mode 100644
index 000000000..7929e2b96
--- /dev/null
+++ b/apps/api/tests/test_ai_agent_tool_adoption_approval_package.py
@@ -0,0 +1,207 @@
+from __future__ import annotations
+
+import json
+
+import pytest
+
+from src.services.ai_agent_tool_adoption_approval_package import (
+ load_latest_ai_agent_tool_adoption_approval_package,
+)
+
+
+def test_load_latest_ai_agent_tool_adoption_approval_package_reads_committed_snapshot():
+ data = load_latest_ai_agent_tool_adoption_approval_package()
+
+ assert data["schema_version"] == "ai_agent_tool_adoption_approval_package_v1"
+ assert data["program_status"]["overall_completion_percent"] == 55
+ assert data["program_status"]["current_task_id"] == "P2-402C"
+ assert data["program_status"]["next_task_id"] == "P2-402D"
+ assert data["program_status"]["read_only_mode"] is True
+ assert (
+ data["program_status"]["runtime_authority"]
+ == "approval_package_only_no_tool_install_or_ci_change"
+ )
+ assert data["approval_boundaries"]["tool_install_allowed"] is False
+ assert data["approval_boundaries"]["ci_workflow_change_allowed"] is False
+ assert data["approval_boundaries"]["external_registry_lookup_allowed"] is False
+ assert data["approval_boundaries"]["vulnerability_database_download_allowed"] is False
+ assert data["approval_boundaries"]["gitea_pr_creation_allowed"] is False
+ assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False
+ assert data["rollups"]["tool_count"] == len(data["tool_candidates"]) == 5
+ assert data["rollups"]["source_evidence_count"] == len(data["source_evidence"]) == 5
+ assert data["rollups"]["adoption_lane_count"] == len(data["adoption_lanes"]) == 4
+ assert data["rollups"]["tool_install_allowed_count"] == 0
+ assert data["rollups"]["ci_change_allowed_count"] == 0
+ assert data["rollups"]["auto_merge_allowed_count"] == 0
+ assert data["rollups"]["telegram_direct_send_count"] == 0
+ assert set(data["rollups"]["approval_required_tool_ids"]) == {
+ "renovate_gitea",
+ "osv_scanner",
+ "trivy",
+ "syft",
+ "grype",
+ }
+ assert set(data["rollups"]["secret_required_tool_ids"]) == {
+ "renovate_gitea",
+ "trivy",
+ "syft",
+ "grype",
+ }
+ assert "syft" not in data["rollups"]["external_data_required_tool_ids"]
+
+
+def test_ai_agent_tool_adoption_approval_package_rejects_tool_install_allowed(tmp_path):
+ snapshot = _snapshot()
+ snapshot["approval_boundaries"]["tool_install_allowed"] = True
+ (tmp_path / "ai_agent_tool_adoption_approval_package_2026-06-11.json").write_text(
+ json.dumps(snapshot),
+ encoding="utf-8",
+ )
+
+ with pytest.raises(ValueError, match="approval boundaries"):
+ load_latest_ai_agent_tool_adoption_approval_package(tmp_path)
+
+
+def test_ai_agent_tool_adoption_approval_package_rejects_rollup_mismatch(tmp_path):
+ snapshot = _snapshot()
+ snapshot["rollups"]["tool_count"] = 99
+ (tmp_path / "ai_agent_tool_adoption_approval_package_2026-06-11.json").write_text(
+ json.dumps(snapshot),
+ encoding="utf-8",
+ )
+
+ with pytest.raises(ValueError, match="rollup counts"):
+ load_latest_ai_agent_tool_adoption_approval_package(tmp_path)
+
+
+def test_ai_agent_tool_adoption_approval_package_rejects_non_approval_tool(tmp_path):
+ snapshot = _snapshot()
+ snapshot["tool_candidates"][0]["adoption_status"] = "enabled"
+ snapshot["rollups"]["approval_required_tool_ids"] = []
+ (tmp_path / "ai_agent_tool_adoption_approval_package_2026-06-11.json").write_text(
+ json.dumps(snapshot),
+ encoding="utf-8",
+ )
+
+ with pytest.raises(ValueError, match="approval-bound"):
+ load_latest_ai_agent_tool_adoption_approval_package(tmp_path)
+
+
+def test_ai_agent_tool_adoption_approval_package_rejects_unknown_source_ref(tmp_path):
+ snapshot = _snapshot()
+ snapshot["tool_candidates"][0]["official_source_refs"] = ["unknown_source"]
+ (tmp_path / "ai_agent_tool_adoption_approval_package_2026-06-11.json").write_text(
+ json.dumps(snapshot),
+ encoding="utf-8",
+ )
+
+ with pytest.raises(ValueError, match="approval-bound"):
+ load_latest_ai_agent_tool_adoption_approval_package(tmp_path)
+
+
+def _snapshot() -> dict:
+ return {
+ "schema_version": "ai_agent_tool_adoption_approval_package_v1",
+ "generated_at": "2026-06-11T23:35:00+08:00",
+ "program_status": {
+ "overall_completion_percent": 55,
+ "current_priority": "P2",
+ "current_task_id": "P2-402C",
+ "next_task_id": "P2-402D",
+ "read_only_mode": True,
+ "runtime_authority": "approval_package_only_no_tool_install_or_ci_change",
+ "status_note": "測試。",
+ },
+ "source_evidence": [
+ {
+ "source_id": "renovate_gitea_platform_docs",
+ "tool_id": "renovate_gitea",
+ "name": "Renovate Gitea platform docs",
+ "url": "https://docs.renovatebot.com/modules/platform/gitea/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "測試。",
+ }
+ ],
+ "agent_review_matrix": [
+ {
+ "agent_id": "openclaw",
+ "role": "測試。",
+ "allowed_now": ["讀取批准包"],
+ "blocked_until_approval": ["安裝工具"],
+ "output": "verdict",
+ }
+ ],
+ "adoption_lanes": [
+ {
+ "lane_id": "dependency_update_pr_lane",
+ "display_name": "Dependency update PR lane",
+ "owner_agent": "hermes",
+ "tool_ids": ["renovate_gitea"],
+ "approval_gate": "approval_required",
+ "planned_output": "approval package",
+ "blocked_now": ["PR creation"],
+ }
+ ],
+ "tool_candidates": [
+ {
+ "tool_id": "renovate_gitea",
+ "display_name": "Renovate for Gitea",
+ "category": "dependency_update_pr",
+ "owner_agent": "hermes",
+ "adoption_status": "approval_required",
+ "risk_tier": "high",
+ "cost_profile": "runner cost requires approval",
+ "secret_requirement": "Gitea bot token required after approval.",
+ "external_data_requirement": "Package registry metadata required after approval; currently blocked.",
+ "ci_change_required": True,
+ "official_source_refs": ["renovate_gitea_platform_docs"],
+ "intended_scope": ["dependency update PR draft"],
+ "approval_requirements": ["bot token storage plan"],
+ "blocked_until_approval": ["PR creation"],
+ "recommendation": "測試。",
+ "rollback_plan": "測試。",
+ }
+ ],
+ "approval_packet_fields": [
+ {
+ "field_id": "operator_approval_id",
+ "required": True,
+ "owner_agent": "openclaw",
+ "description": "測試。",
+ }
+ ],
+ "approval_boundaries": {
+ "tool_install_allowed": False,
+ "ci_workflow_change_allowed": False,
+ "external_registry_lookup_allowed": False,
+ "vulnerability_database_download_allowed": False,
+ "package_upgrade_allowed": False,
+ "lockfile_write_allowed": False,
+ "docker_build_allowed": False,
+ "image_pull_allowed": False,
+ "gitea_pr_creation_allowed": False,
+ "auto_merge_allowed": False,
+ "telegram_direct_send_allowed": False,
+ "paid_service_enabled": False,
+ "production_route_change_allowed": False,
+ "secret_plaintext_allowed": False,
+ },
+ "rollups": {
+ "tool_count": 1,
+ "source_evidence_count": 1,
+ "adoption_lane_count": 1,
+ "approval_required_tool_ids": ["renovate_gitea"],
+ "ci_change_required_tool_ids": ["renovate_gitea"],
+ "secret_required_tool_ids": ["renovate_gitea"],
+ "external_data_required_tool_ids": ["renovate_gitea"],
+ "sbom_tool_ids": [],
+ "vulnerability_scan_tool_ids": [],
+ "update_pr_tool_ids": ["renovate_gitea"],
+ "tool_install_allowed_count": 0,
+ "ci_change_allowed_count": 0,
+ "auto_merge_allowed_count": 0,
+ "telegram_direct_send_count": 0,
+ "next_approval_task_ids": ["P2-402D"],
+ },
+ }
diff --git a/apps/api/tests/test_ai_agent_tool_adoption_approval_package_api.py b/apps/api/tests/test_ai_agent_tool_adoption_approval_package_api.py
new file mode 100644
index 000000000..5507ba423
--- /dev/null
+++ b/apps/api/tests/test_ai_agent_tool_adoption_approval_package_api.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+
+from src.api.v1.agents import router
+
+
+def test_agent_tool_adoption_approval_package_endpoint_returns_committed_snapshot():
+ app = FastAPI()
+ app.include_router(router, prefix="/api/v1")
+ client = TestClient(app)
+
+ response = client.get("/api/v1/agents/agent-tool-adoption-approval-package")
+
+ assert response.status_code == 200
+ data = response.json()
+ assert data["schema_version"] == "ai_agent_tool_adoption_approval_package_v1"
+ assert data["program_status"]["overall_completion_percent"] == 55
+ assert data["program_status"]["current_task_id"] == "P2-402C"
+ assert data["program_status"]["next_task_id"] == "P2-402D"
+ assert data["program_status"]["read_only_mode"] is True
+ assert data["approval_boundaries"]["tool_install_allowed"] is False
+ assert data["approval_boundaries"]["ci_workflow_change_allowed"] is False
+ assert data["approval_boundaries"]["external_registry_lookup_allowed"] is False
+ assert data["approval_boundaries"]["vulnerability_database_download_allowed"] is False
+ assert data["approval_boundaries"]["gitea_pr_creation_allowed"] is False
+ assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False
+ assert data["rollups"]["tool_count"] == 5
+ assert data["rollups"]["source_evidence_count"] == 5
+ assert data["rollups"]["adoption_lane_count"] == 4
+ assert data["rollups"]["tool_install_allowed_count"] == 0
+ assert data["rollups"]["ci_change_allowed_count"] == 0
+ assert data["rollups"]["auto_merge_allowed_count"] == 0
+ assert data["rollups"]["telegram_direct_send_count"] == 0
+ assert any(tool["tool_id"] == "renovate_gitea" for tool in data["tool_candidates"])
+ assert any(tool["tool_id"] == "osv_scanner" for tool in data["tool_candidates"])
+ assert any(tool["tool_id"] == "trivy" for tool in data["tool_candidates"])
+ assert any(tool["tool_id"] == "syft" for tool in data["tool_candidates"])
+ assert any(tool["tool_id"] == "grype" for tool in data["tool_candidates"])
diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md
index 0d100214b..9b93d71ff 100644
--- a/docs/LOGBOOK.md
+++ b/docs/LOGBOOK.md
@@ -28,6 +28,30 @@
- 本階段沒有 force push、沒有 destructive git、沒有讀取或輸出 Secret payload、沒有 SSH、沒有 active scan、沒有改 Alertmanager rule / receiver、沒有手動重啟 production service;部署由既有 Gitea CD 完成。
- IwoooS 整體仍維持 `64%`;active runtime gate 仍為 `0`;owner response received / accepted 仍為 `0 / false`。
+## 2026-06-11|P2-402C AI Agent 工具採用批准包
+
+**背景**:統帥要求 OpenClaw / Hermes / NemoTron 針對所有工具、套件、服務、主機與 AI Agent 版本管理做主動監控、管理、備份、優化配置與定期更新評估;本波接續 P2-402B repo-only 版本新鮮度快照,先建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 的採用批准包,避免尚未授權就安裝工具、改 CI 或查外部資料。
+
+**完成內容:**
+- 新增 `ai_agent_tool_adoption_approval_package_v1` schema。
+- 新增 committed snapshot:`docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json`。
+- 新增只讀 loader:`apps/api/src/services/ai_agent_tool_adoption_approval_package.py`。
+- 新增治理 API:`GET /api/v1/agents/agent-tool-adoption-approval-package`。
+- 新增 loader / API / safety boundary / rollup mismatch / unknown source ref 測試。
+- 新增分析報告:`docs/ai/AI_AGENT_TOOL_ADOPTION_APPROVAL_PACKAGE_2026-06-11.md`。
+- 同步 `AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`、`AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md` 與 MASTER §3.2.1c。
+
+**完成度與下一步:**
+- P2-402C:`100%`。
+- AI Agent 主動營運與版本生命週期整體:`55%`。
+- Current task:`P2-402C`。
+- Next task:`P2-402D` Telegram action-required digest policy。
+
+**安全邊界:**
+- 本波只做批准包與只讀 API。
+- 未安裝 Renovate / OSV-Scanner / Trivy / Syft / Grype。
+- 未修改 CI workflow、未查外部 registry、未下載 vulnerability DB、未升級套件、未寫 lockfile、未 build/pull image、未建立 Gitea PR、未 auto merge、未發 Telegram、未改 production route。
+
## 2026-06-11|P0 Telegram 重複告警靜音第二波修復
**背景**:第一波已修復 Alertmanager 缺少 `project_id` 導致 RLS fail-closed、告警被 degraded accepted no-retry 吃掉的主斷點。後續正式 logs 又確認另一條靜音路徑:同一指紋第一次告警進入背景 AI 分析後,第二次同指紋告警會被 `alertmanager_llm_inflight_suppressed` 擋下,只記錄 DB event,不送 Telegram,使用者仍會感覺「告警完全沒有出現」。
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 6df5b290f..d85cc50a5 100644
--- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md
+++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md
@@ -13,7 +13,7 @@
| 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 |
| OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` |
| OpenClaw / Hermes / NemoTron 主動溝通與學習契約 | 35% | P2-401A 已完成只讀 contract;runtime worker、DB migration、Telegram 實發、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1` schema、`ai_agent_communication_learning_contract_2026-06-11.json`、`GET /api/v1/agents/agent-communication-learning-contract`、MASTER §3.2.1b / §3.4.3 |
-| AI Agent 主動營運委派與版本生命週期 | 42% | P2-402A / P2-402B 已完成;已建立 repo-only 版本新鮮度快照與 API。定期排程、外部版本查詢、套件升級、主機更新、container pull、auto merge、Telegram 實發仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、MASTER §3.2.1c |
+| AI Agent 主動營運委派與版本生命週期 | 55% | P2-402A / P2-402B / P2-402C 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包與 API。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、auto merge、Telegram 實發仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_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`、MASTER §3.2.1c |
| 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 |
AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本身完成度:**100%**。
@@ -22,7 +22,7 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本
三 Agent 主動溝通與學習契約目前完成度:**35%**。已完成只讀 schema / snapshot / API / 測試與 MASTER 同步;下一步依優先順序推 `P2-401B` AgentSession / Redis Streams migration 與 worker gate,但在批准前仍不得啟動 runtime loop。
-AI Agent 主動營運委派與版本生命週期目前完成度:**42%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API,以及 `P2-402B` repo-only daily version freshness snapshot schema / committed snapshot / API / 測試;下一步是 `P2-402C` Renovate / OSV / Trivy / Syft / Grype 工具採用批准包,外部 registry / package source / host probe / Telegram 實發仍需 gate。
+AI Agent 主動營運委派與版本生命週期目前完成度:**55%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot,以及 `P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包;下一步是 `P2-402D` Telegram action-required digest policy,外部 registry / package source / host probe / 工具安裝 / CI 變更 / Telegram 實發仍需 gate。
完成度計算模型:
@@ -953,7 +953,7 @@ UI:
| P2-401E | 待辦 | 0 | Nemotron | sanitized replay scorer 與 5-record smoke 設計 | NemoTron replay smoke 批准包 | cost / data approval |
| P2-402A | 完成 | 100 | Hermes + OpenClaw + Nemotron | 定義 AI Agent 主動營運委派與版本生命週期:12 類版本 domain、24 類可委派能力、MCP/RAG/Telegram policy | `ai_agent_proactive_operations_contract_v1` / snapshot / 只讀 API / MASTER 同步 | 只讀;不啟用排程、不升級、不 host update、不 pull image、不 auto merge、不發 Telegram |
| P2-402B | 完成 | 100 | Hermes | 建立 repo-only daily version freshness snapshot | `ai_agent_version_freshness_snapshot_v1` / snapshot / 只讀 API / 測試;12 類 repo-only source、5 個 daily step、3 Agent role | 只讀;workflow schedule 未啟用、外部 lookup 未開、不得升級或發 Telegram |
-| P2-402C | 待辦 | 0 | OpenClaw | 建立 Renovate / OSV / Trivy / Syft / Grype 工具採用批准包 | 工具 / 費用 / secret / CI 變更批准包 | tool install + CI change approval |
+| P2-402C | 完成 | 100 | OpenClaw | 建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包 | `ai_agent_tool_adoption_approval_package_v1` / snapshot / 只讀 API / 測試;5 工具、5 官方來源、4 採用 lane、6 批准欄位 | 只讀;tool install、CI change、external lookup、vulnerability DB、PR、Telegram 全部未啟用 |
| P2-402D | 待辦 | 0 | OpenClaw | 建立 Telegram action-required digest policy | critical / action-required / failure-only digest | Telegram Gateway E2E |
| P2-402E | 待辦 | 0 | Hermes | 設計 Gitea PR 草案 lane | grouping、automerge=false、tests、rollback、owner response | bot / branch policy approval |
| P2-402F | 待辦 | 0 | OpenClaw | 建立 host OS / K3s / stateful services 版本只讀盤點 | host / K3s / DB / Redis / MinIO / Gitea 版本矩陣 | host readonly probe + maintenance window approval |
diff --git a/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md b/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md
index 7542c9395..e15539ef2 100644
--- a/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md
+++ b/docs/ai/AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md
@@ -1,7 +1,7 @@
# AI Agent 主動營運委派與版本生命週期分析報告
> 日期:2026-06-11(台北時間)
-> 文件定位:P2-402A / P2-402B 只讀契約摘要。權威細節以 MASTER §3.2.1c、`ai_agent_proactive_operations_contract_v1` 與 `ai_agent_version_freshness_snapshot_v1` 為準。
+> 文件定位:P2-402A / P2-402B / P2-402C 只讀契約摘要。權威細節以 MASTER §3.2.1c、`ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1` 與 `ai_agent_tool_adoption_approval_package_v1` 為準。
## 1. 本波完成度
@@ -9,7 +9,8 @@
|---|---:|---|
| 主動營運委派契約 | 100% | 已完成 schema / snapshot / API / 測試 |
| Repo-only 版本新鮮度快照 | 100% | P2-402B 已完成 schema / committed snapshot / API / 測試 |
-| 整體主動營運與版本生命週期 | 42% | 已完成架構、邊界與 repo-only freshness;runtime 排程與更新尚未開 gate |
+| 工具採用批准包 | 100% | P2-402C 已完成 Renovate / OSV-Scanner / Trivy / Syft / Grype schema / committed snapshot / API / 測試 |
+| 整體主動營運與版本生命週期 | 55% | 已完成架構、repo-only freshness 與工具採用批准包;runtime 排程、工具安裝、CI 變更與更新仍未開 gate |
## 2. 可交給 AI Agent 的工作分類
@@ -34,6 +35,9 @@
| `docs/schemas/ai_agent_version_freshness_snapshot_v1.schema.json` | Repo-only 版本新鮮度 schema;所有外部查詢與更新權限預設 false |
| `docs/evaluations/ai_agent_version_freshness_snapshot_2026-06-11.json` | 12 類 repo-only source、5 個 daily step、3 Agent role、5 個 action-required source |
| `GET /api/v1/agents/agent-version-freshness-snapshot` | 只讀 API;不觸發掃描、不查外部、不改 repo、不發 Telegram |
+| `docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json` | 工具採用批准包 schema;工具安裝、CI 變更、外部查詢、漏洞 DB、PR、Telegram 全部預設 false |
+| `docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json` | Renovate / OSV-Scanner / Trivy / Syft / Grype 官方來源、採用 lane、批准欄位、rollback plan |
+| `GET /api/v1/agents/agent-tool-adoption-approval-package` | 只讀 API;不安裝工具、不改 CI、不查外部、不建 PR、不發 Telegram |
## 4. P2-402B Repo-only 版本新鮮度快照
@@ -48,18 +52,29 @@
本波只把「每天要看哪些 repo 內版本來源」定義成可驗證資料面。每日排程、外部 registry 查詢、主機/K3s live probe、Telegram digest 與 Gitea PR lane 都仍是下一階段 gate。
-## 5. 下一步優先順序
+## 5. P2-402C 工具採用批准包
+
+| 工具 | 主責 Agent | 建議角色 | 目前狀態 |
+|---|---|---|---|
+| Renovate for Gitea | Hermes | 依賴更新 PR 草案、grouping、automerge=false | 只建立批准包;bot token、workflow、PR 全部未啟用 |
+| OSV-Scanner | OpenClaw | 依賴漏洞 advisory lookup 候選 | 只建立批准包;外部查詢與 DB lookup 未啟用 |
+| Trivy | OpenClaw | Container / filesystem / IaC / SBOM scanner 候選 | 只建立批准包;不安裝、不下載 DB、不掃 image |
+| Syft | Hermes | SBOM 產生候選 | 只建立批准包;不產 SBOM artifact、不上傳 artifact |
+| Grype | OpenClaw | Syft SBOM / image vulnerability cross-check 候選 | 只建立批准包;不安裝、不下載 DB、不送 Telegram |
+
+P2-402C 的重點是把「哪些工具可被採用、採用前要填哪些批准欄位、風險與 rollback 如何定義」固定成資料契約。它不是工具導入本身;工具安裝、CI workflow、外部 registry / vulnerability DB、Gitea PR、Telegram 實發都維持 blocked。
+
+## 6. 下一步優先順序
| ID | 優先 | 任務 | 關卡 |
|---|---|---|---|
-| P2-402B | 0 | repo-only daily version freshness snapshot | 已完成,只讀;workflow schedule 未啟用 |
-| P2-402C | 1 | Renovate / OSV / Trivy / Syft / Grype 採用批准包 | tool install / CI approval |
-| P2-402D | 2 | Telegram action-required digest policy | Telegram Gateway E2E |
-| P2-402E | 3 | Gitea PR 草案 lane | bot / branch policy approval |
-| P2-402F | 4 | host OS / K3s / stateful services 版本只讀盤點 | host probe / maintenance approval |
-| P2-402G | 5 | governance UI 顯示可委派能力 | frontend UI approval |
+| P2-402C | 0 | Renovate / OSV-Scanner / Trivy / Syft / Grype 採用批准包 | 已完成,只讀;tool install / CI change 未啟用 |
+| P2-402D | 1 | Telegram action-required digest policy | Telegram Gateway E2E |
+| P2-402E | 2 | Gitea PR 草案 lane | bot / branch policy approval |
+| P2-402F | 3 | host OS / K3s / stateful services 版本只讀盤點 | host probe / maintenance approval |
+| P2-402G | 4 | governance UI 顯示可委派能力 | frontend UI approval |
-## 6. 仍維持 false 的安全邊界
+## 7. 仍維持 false 的安全邊界
- `runtime_version_update_allowed=false`
- `package_upgrade_allowed=false`
diff --git a/docs/ai/AI_AGENT_TOOL_ADOPTION_APPROVAL_PACKAGE_2026-06-11.md b/docs/ai/AI_AGENT_TOOL_ADOPTION_APPROVAL_PACKAGE_2026-06-11.md
new file mode 100644
index 000000000..4992607d3
--- /dev/null
+++ b/docs/ai/AI_AGENT_TOOL_ADOPTION_APPROVAL_PACKAGE_2026-06-11.md
@@ -0,0 +1,72 @@
+# AI Agent 工具採用批准包工作報告
+
+> 日期:2026-06-11(台北時間)
+> 狀態:P2-402C 已完成;整體 AI Agent 主動營運與版本生命週期完成度 55%。
+> 邊界:本文件與對應 API 只提供批准包,不安裝工具、不改 CI、不查外部 registry、不下載漏洞 DB、不建立 PR、不 auto merge、不發 Telegram、不改 production route。
+
+## 1. 本波完成項目
+
+| 項目 | 狀態 | 產出 |
+|---|---|---|
+| 工具採用 schema | 完成 | `docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json` |
+| 工具採用 snapshot | 完成 | `docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json` |
+| API loader | 完成 | `apps/api/src/services/ai_agent_tool_adoption_approval_package.py` |
+| 只讀 API | 完成 | `GET /api/v1/agents/agent-tool-adoption-approval-package` |
+| 測試 | 完成 | loader / API / safety boundary / rollup mismatch / unknown source ref |
+| 主契約同步 | 完成 | `ai_agent_proactive_operations_contract_v1` 完成度 55%,current `P2-402C`,next `P2-402D` |
+
+## 2. 官方來源與工具角色
+
+| 工具 | 官方來源 | 主責 Agent | 專業角色 | 採用前必過關卡 |
+|---|---|---|---|---|
+| Renovate for Gitea | | Hermes | 依賴更新 PR 草案、grouping、automerge=false、reviewer policy | Gitea bot token、workflow、branch policy、PR storm rollback |
+| OSV-Scanner | | OpenClaw | 依賴漏洞 advisory lookup 候選 | 外部查詢、cache TTL、false-positive、severity mapping |
+| Trivy | | OpenClaw | container / filesystem / IaC / SBOM / Kubernetes scanner 候選 | DB cache、runner cost、registry credential、image/build 邊界 |
+| Syft | | Hermes | SBOM 產生候選 | SBOM schema、artifact retention、redaction、storage path |
+| Grype | | OpenClaw | Syft SBOM / image vulnerability cross-check 候選 | vulnerability DB cache、suppression、duplicate handling、Telegram gate |
+
+## 3. Agent 分工
+
+| Agent | 本波可做 | 本波不可做 |
+|---|---|---|
+| Hermes | 彙整官方來源、工具欄位、Gitea PR lane 草案、SBOM artifact policy 草案 | 建立 bot token、寫 workflow、產 SBOM、建立 PR |
+| OpenClaw | 仲裁 secret / CI / 費用 / false-positive / blast-radius,要求 HITL approval | 自行批准工具啟用、改 CI runner、改 Alertmanager / Telegram route、auto merge |
+| NemoTron | 建立離線 replay 欄位,評估 scanner output 如何影響 AI Agent / prompt / tool-call 品質 | 新增 SDK、呼叫付費模型、開 shadow/canary、改 provider route |
+
+## 4. 後續代辦與優先順序
+
+| 優先 | ID | 工作 | 完成標準 | 目前狀態 |
+|---:|---|---|---|---|
+| 1 | P2-402D | Telegram action-required digest policy | 定義 critical / action-required / failure-only,禁止成功洗版,接 Telegram Gateway E2E gate | 待辦 |
+| 2 | P2-402E | Gitea PR 草案 lane | Renovate grouping、automerge=false、測試要求、rollback、owner response | 待辦 |
+| 3 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 | 待辦 |
+| 4 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 | 待辦 |
+| 5 | 候選池 | SBOM artifact policy | Syft / Trivy artifact 格式、保存、redaction、消費者規則 | 未排正式編號 |
+| 6 | 候選池 | Scanner false-positive policy | OSV / Trivy / Grype 差異、suppression、severity mapping、owner SLA | 未排正式編號 |
+| 7 | 候選池 | Version history / RAG 回寫 | 每次版本觀測、批准、失敗、rollback 回寫 `version_history` / `upgrade_outcomes` | 未排正式編號 |
+
+## 5. 現在仍維持 false 的邊界
+
+- `tool_install_allowed=false`
+- `ci_workflow_change_allowed=false`
+- `external_registry_lookup_allowed=false`
+- `vulnerability_database_download_allowed=false`
+- `package_upgrade_allowed=false`
+- `lockfile_write_allowed=false`
+- `docker_build_allowed=false`
+- `image_pull_allowed=false`
+- `gitea_pr_creation_allowed=false`
+- `auto_merge_allowed=false`
+- `telegram_direct_send_allowed=false`
+- `paid_service_enabled=false`
+- `production_route_change_allowed=false`
+- `secret_plaintext_allowed=false`
+
+## 6. 進度同步方式
+
+每推進一個正式 task,必須同步更新四個地方:
+
+1. `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` 的完成度、current task、next task。
+2. `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md` 的狀態、百分比、關卡。
+3. `docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md` 的 §3.2.1c 與 changelog。
+4. `docs/LOGBOOK.md` 的正式工作紀錄、驗證結果與 deployment evidence。
diff --git a/docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json b/docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json
index e3750b0b5..2bca560c1 100644
--- a/docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json
+++ b/docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json
@@ -2,13 +2,13 @@
"schema_version": "ai_agent_proactive_operations_contract_v1",
"generated_at": "2026-06-11T21:30:00+08:00",
"program_status": {
- "overall_completion_percent": 42,
+ "overall_completion_percent": 55,
"current_priority": "P2",
- "current_task_id": "P2-402B",
- "next_task_id": "P2-402C",
+ "current_task_id": "P2-402C",
+ "next_task_id": "P2-402D",
"read_only_mode": true,
"runtime_authority": "contract_only_no_version_or_runtime_update",
- "status_note": "P2-402B 已建立 repo-only 版本新鮮度快照與只讀 API;本波仍不啟用排程、不升級套件、不更新主機、不 pull image、不 auto merge、不發 Telegram。"
+ "status_note": "P2-402C 已建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、schema、snapshot、只讀 API 與測試;本波仍不安裝工具、不改 CI、不查外部 registry、不下載漏洞 DB、不建立 PR、不發 Telegram。"
},
"external_source_evidence": [
{
@@ -619,16 +619,16 @@
"completion_percent": 100,
"owner_agent": "Hermes",
"summary": "建立 repo-only daily version freshness snapshot schema、committed snapshot、只讀 API 與測試;不查外部 registry、不改 workflow。",
- "next_gate": "P2-402C_tool_adoption_approval_package"
+ "next_gate": "P2-402C_completed"
},
{
"task_id": "P2-402C",
"priority": "P2",
- "status": "planned",
- "completion_percent": 0,
+ "status": "done",
+ "completion_percent": 100,
"owner_agent": "OpenClaw",
- "summary": "建立 Renovate / OSV / Trivy / Syft / Grype 工具採用批准包。",
- "next_gate": "tool_install_ci_change_and_secret_approval_required"
+ "summary": "建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、官方來源 evidence、採用 lane、批准欄位、schema、snapshot、只讀 API 與測試。",
+ "next_gate": "P2-402D_telegram_action_required_digest_policy"
},
{
"task_id": "P2-402D",
diff --git a/docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json b/docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json
new file mode 100644
index 000000000..e1018c472
--- /dev/null
+++ b/docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json
@@ -0,0 +1,471 @@
+{
+ "schema_version": "ai_agent_tool_adoption_approval_package_v1",
+ "generated_at": "2026-06-11T23:35:00+08:00",
+ "program_status": {
+ "overall_completion_percent": 55,
+ "current_priority": "P2",
+ "current_task_id": "P2-402C",
+ "next_task_id": "P2-402D",
+ "read_only_mode": true,
+ "runtime_authority": "approval_package_only_no_tool_install_or_ci_change",
+ "status_note": "P2-402C 建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包;本波只保留官方來源與採用門檻,不安裝工具、不改 CI、不下載漏洞 DB、不建立 PR、不發 Telegram。"
+ },
+ "source_evidence": [
+ {
+ "source_id": "renovate_gitea_platform_docs",
+ "tool_id": "renovate_gitea",
+ "name": "Renovate Gitea platform docs",
+ "url": "https://docs.renovatebot.com/modules/platform/gitea/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "只用於確認 Renovate 支援 Gitea 與 autodiscover / PR 權限需求;本波不啟用 bot、不寫 workflow、不建立 PR。"
+ },
+ {
+ "source_id": "osv_scanner_usage_docs",
+ "tool_id": "osv_scanner",
+ "name": "OSV-Scanner usage docs",
+ "url": "https://google.github.io/osv-scanner/usage/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "只用於確認 OSV-Scanner V2 使用面;本波不下載漏洞資料、不掃描專案、不呼叫外部查詢。"
+ },
+ {
+ "source_id": "trivy_docs",
+ "tool_id": "trivy",
+ "name": "Trivy official docs",
+ "url": "https://trivy.dev/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "只用於確認 Trivy 可作為漏洞 / IaC / SBOM / Kubernetes 類掃描候選;本波不安裝、不掃描、不下載 vulnerability DB。"
+ },
+ {
+ "source_id": "anchore_syft_docs",
+ "tool_id": "syft",
+ "name": "Anchore Syft open source docs",
+ "url": "https://anchore.com/opensource/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "只用於確認 Syft 是 SBOM 產生候選;本波不安裝、不產 SBOM、不寫 artifact。"
+ },
+ {
+ "source_id": "anchore_grype_docs",
+ "tool_id": "grype",
+ "name": "Anchore Grype open source docs",
+ "url": "https://anchore.com/opensource/",
+ "retrieved_at": "2026-06-11T23:35:00+08:00",
+ "evidence_type": "official_documentation",
+ "usage_boundary": "只用於確認 Grype 是 container / filesystem vulnerability scanner 候選;本波不安裝、不掃描、不下載資料庫。"
+ }
+ ],
+ "agent_review_matrix": [
+ {
+ "agent_id": "hermes",
+ "role": "彙整官方來源、repo-only freshness source、CI 變更點與 evidence refs。",
+ "allowed_now": [
+ "讀 committed snapshot",
+ "整理工具採用批准欄位",
+ "產出比較表與 next gate"
+ ],
+ "blocked_until_approval": [
+ "安裝工具",
+ "寫 workflow",
+ "下載漏洞資料庫",
+ "建立 Renovate PR"
+ ],
+ "output": "tool adoption evidence packet"
+ },
+ {
+ "agent_id": "openclaw",
+ "role": "仲裁工具採用風險、secret / 費用 / CI / false-positive / blast-radius 邊界。",
+ "allowed_now": [
+ "審核採用門檻",
+ "要求 HITL approval",
+ "定義 rollback 與 failure-only notification 條件"
+ ],
+ "blocked_until_approval": [
+ "批准自己的工具啟用",
+ "改 CI runner",
+ "改 Alertmanager 或 Telegram route",
+ "auto merge"
+ ],
+ "output": "HITL approval package verdict"
+ },
+ {
+ "agent_id": "nemotron",
+ "role": "離線比較掃描器輸出如何影響 AI Agent / model / prompt / tool-call replay 評分。",
+ "allowed_now": [
+ "建立離線 replay 欄位",
+ "標記 scanner assumption risk",
+ "建議多工具交叉驗證策略"
+ ],
+ "blocked_until_approval": [
+ "新增 SDK",
+ "呼叫付費模型",
+ "shadow/canary",
+ "production provider route"
+ ],
+ "output": "offline scanner-assumption review note"
+ }
+ ],
+ "adoption_lanes": [
+ {
+ "lane_id": "dependency_update_pr_lane",
+ "display_name": "Dependency update PR lane",
+ "owner_agent": "hermes",
+ "tool_ids": [
+ "renovate_gitea"
+ ],
+ "approval_gate": "gitea_bot_token_branch_policy_and_ci_workflow_approval",
+ "planned_output": "Renovate config / grouping / automerge=false / reviewer policy approval package",
+ "blocked_now": [
+ "建立 bot token",
+ "寫 renovate config",
+ "啟用 autodiscover",
+ "建立 PR",
+ "auto merge"
+ ]
+ },
+ {
+ "lane_id": "vulnerability_lookup_lane",
+ "display_name": "Vulnerability lookup lane",
+ "owner_agent": "openclaw",
+ "tool_ids": [
+ "osv_scanner",
+ "trivy",
+ "grype"
+ ],
+ "approval_gate": "external_source_cache_rate_limit_and_false_positive_policy_approval",
+ "planned_output": "CVE / OSV / advisory lookup approval package with cache and suppression policy",
+ "blocked_now": [
+ "下載 vulnerability DB",
+ "查外部 advisory",
+ "掃描 image / filesystem",
+ "把 scanner output 當唯一事實"
+ ]
+ },
+ {
+ "lane_id": "sbom_generation_lane",
+ "display_name": "SBOM generation lane",
+ "owner_agent": "hermes",
+ "tool_ids": [
+ "syft",
+ "trivy"
+ ],
+ "approval_gate": "artifact_retention_and_sbom_schema_approval",
+ "planned_output": "SPDX / CycloneDX / JSON artifact policy and storage path approval package",
+ "blocked_now": [
+ "安裝 Syft",
+ "產生 SBOM artifact",
+ "上傳 artifact",
+ "保存 package payload 超出批准範圍"
+ ]
+ },
+ {
+ "lane_id": "container_crosscheck_lane",
+ "display_name": "Container scanner cross-check lane",
+ "owner_agent": "openclaw",
+ "tool_ids": [
+ "trivy",
+ "syft",
+ "grype"
+ ],
+ "approval_gate": "container_scan_runbook_and_dedup_policy_approval",
+ "planned_output": "Trivy vs Syft+Grype cross-check runbook and false-positive review policy",
+ "blocked_now": [
+ "docker build",
+ "image pull",
+ "registry push",
+ "改 Harbor / ArgoCD / production route"
+ ]
+ }
+ ],
+ "tool_candidates": [
+ {
+ "tool_id": "renovate_gitea",
+ "display_name": "Renovate for Gitea",
+ "category": "dependency_update_pr",
+ "owner_agent": "hermes",
+ "adoption_status": "approval_required",
+ "risk_tier": "high",
+ "cost_profile": "open_source_self_hosted_candidate; runner usage and maintenance cost require approval",
+ "secret_requirement": "Gitea bot token / repo permission required after approval; no token is created or read in this package.",
+ "external_data_requirement": "Package registry metadata required after approval; currently blocked.",
+ "ci_change_required": true,
+ "official_source_refs": [
+ "renovate_gitea_platform_docs"
+ ],
+ "intended_scope": [
+ "dependency update PR draft",
+ "grouping",
+ "automerge=false",
+ "reviewer assignment"
+ ],
+ "approval_requirements": [
+ "Gitea bot identity and token storage plan",
+ "branch naming and PR grouping policy",
+ "automerge=false hard gate",
+ "rate limit and schedule policy",
+ "rollback plan for bad PR storm"
+ ],
+ "blocked_until_approval": [
+ "renovate config write",
+ "workflow schedule activation",
+ "bot token creation",
+ "PR creation",
+ "auto merge"
+ ],
+ "recommendation": "Adopt only after P2-402E Gitea PR lane and branch policy approval; do not enable during P2-402C.",
+ "rollback_plan": "Disable workflow / bot token, close generated PRs, revert Renovate config commit; no production route change allowed."
+ },
+ {
+ "tool_id": "osv_scanner",
+ "display_name": "OSV-Scanner",
+ "category": "vulnerability_lookup",
+ "owner_agent": "openclaw",
+ "adoption_status": "approval_required",
+ "risk_tier": "medium",
+ "cost_profile": "free_public_candidate; network and rate-limit policy still require approval",
+ "secret_requirement": "No project secret required for public lookup candidate; cache policy required.",
+ "external_data_requirement": "OSV vulnerability database lookup required after approval; currently blocked.",
+ "ci_change_required": true,
+ "official_source_refs": [
+ "osv_scanner_usage_docs"
+ ],
+ "intended_scope": [
+ "dependency vulnerability lookup",
+ "lockfile / manifest advisory matching",
+ "approval package evidence"
+ ],
+ "approval_requirements": [
+ "network egress and cache TTL policy",
+ "false-positive suppression process",
+ "severity mapping to dependency_risk_policy_v1",
+ "failure-only notification policy",
+ "artifact retention boundary"
+ ],
+ "blocked_until_approval": [
+ "external advisory lookup",
+ "vulnerability DB query",
+ "CI workflow write",
+ "package upgrade"
+ ],
+ "recommendation": "Adopt as first vulnerability lookup candidate after cache/rate-limit approval; keep output advisory-only until OpenClaw review.",
+ "rollback_plan": "Remove workflow step and cached advisory artifacts; keep prior repo-only freshness snapshot as fallback."
+ },
+ {
+ "tool_id": "trivy",
+ "display_name": "Trivy",
+ "category": "container_iac_vulnerability_scan",
+ "owner_agent": "openclaw",
+ "adoption_status": "approval_required",
+ "risk_tier": "high",
+ "cost_profile": "open_source_candidate; DB download, cache, runner time and false-positive review cost require approval",
+ "secret_requirement": "No plaintext secret allowed; registry credentials only after dedicated secret policy approval.",
+ "external_data_requirement": "Vulnerability DB and optional registry/image metadata required after approval; currently blocked.",
+ "ci_change_required": true,
+ "official_source_refs": [
+ "trivy_docs"
+ ],
+ "intended_scope": [
+ "container image vulnerability scan",
+ "filesystem scan",
+ "IaC / Kubernetes config scan",
+ "SBOM or misconfiguration signal"
+ ],
+ "approval_requirements": [
+ "runner cache and DB update policy",
+ "image pull/build boundary",
+ "registry credential handling",
+ "false-positive triage and suppression owner",
+ "cross-check with Syft / Grype before action-required escalation"
+ ],
+ "blocked_until_approval": [
+ "trivy install",
+ "vulnerability DB download",
+ "filesystem scan",
+ "image scan",
+ "Kubernetes live scan",
+ "CI workflow write"
+ ],
+ "recommendation": "Adopt after OSV baseline and SBOM artifact policy; use as primary container/IaC scanner only with OpenClaw gating.",
+ "rollback_plan": "Disable scan job, remove scanner cache, keep prior Dockerfile surface inventory; do not change images or deployments."
+ },
+ {
+ "tool_id": "syft",
+ "display_name": "Syft",
+ "category": "sbom_generation",
+ "owner_agent": "hermes",
+ "adoption_status": "approval_required",
+ "risk_tier": "medium",
+ "cost_profile": "open_source_candidate; runner time and artifact storage require approval",
+ "secret_requirement": "No secret required for repo filesystem SBOM; registry credentials blocked until approved.",
+ "external_data_requirement": "No external vulnerability DB needed for local SBOM generation; package cataloging scope still requires approval.",
+ "ci_change_required": true,
+ "official_source_refs": [
+ "anchore_syft_docs"
+ ],
+ "intended_scope": [
+ "SBOM generation from filesystem",
+ "container image SBOM after image policy approval",
+ "SPDX / CycloneDX / JSON artifact candidate"
+ ],
+ "approval_requirements": [
+ "SBOM schema and artifact retention decision",
+ "redaction and private package payload policy",
+ "artifact storage path",
+ "consumer policy for Grype / external scanners"
+ ],
+ "blocked_until_approval": [
+ "syft install",
+ "SBOM artifact write",
+ "image cataloging",
+ "artifact upload"
+ ],
+ "recommendation": "Adopt as SBOM source of truth candidate before broad scanner rollout; never treat SBOM as remediation approval.",
+ "rollback_plan": "Delete generated artifacts by retention policy and disable SBOM step; no package or image mutation."
+ },
+ {
+ "tool_id": "grype",
+ "display_name": "Grype",
+ "category": "vulnerability_scan_from_sbom_or_image",
+ "owner_agent": "openclaw",
+ "adoption_status": "approval_required",
+ "risk_tier": "medium",
+ "cost_profile": "open_source_candidate; DB download, cache, runner time and triage cost require approval",
+ "secret_requirement": "No plaintext secret allowed; registry credentials blocked until image scan policy approval.",
+ "external_data_requirement": "Vulnerability DB required after approval; currently blocked.",
+ "ci_change_required": true,
+ "official_source_refs": [
+ "anchore_grype_docs"
+ ],
+ "intended_scope": [
+ "scan Syft SBOM",
+ "scan image/filesystem after approval",
+ "cross-check Trivy vulnerability signal"
+ ],
+ "approval_requirements": [
+ "Syft SBOM input policy",
+ "vulnerability DB cache policy",
+ "severity and fix-available mapping",
+ "false-positive and duplicate handling",
+ "OpenClaw review before action-required Telegram digest"
+ ],
+ "blocked_until_approval": [
+ "grype install",
+ "vulnerability DB download",
+ "image/filesystem scan",
+ "CI workflow write",
+ "Telegram digest send"
+ ],
+ "recommendation": "Adopt as cross-check scanner paired with Syft and Trivy; use disagreement as review signal, not automatic blocker.",
+ "rollback_plan": "Disable Grype step and remove DB cache; preserve Syft SBOM artifact policy separately."
+ }
+ ],
+ "approval_packet_fields": [
+ {
+ "field_id": "operator_approval_id",
+ "required": true,
+ "owner_agent": "openclaw",
+ "description": "人工批准 ID;未填不得安裝工具、改 CI 或查外部。"
+ },
+ {
+ "field_id": "secret_storage_plan",
+ "required": true,
+ "owner_agent": "openclaw",
+ "description": "Gitea bot token / registry credentials / cache token 的存放與注入規則;不得記錄明文。"
+ },
+ {
+ "field_id": "ci_runner_cost_and_cache_plan",
+ "required": true,
+ "owner_agent": "hermes",
+ "description": "runner 時間、cache 目錄、DB 更新頻率、artifact retention 與清理策略。"
+ },
+ {
+ "field_id": "false_positive_triage_policy",
+ "required": true,
+ "owner_agent": "openclaw",
+ "description": "掃描器差異、誤報、suppression、severity mapping 與 owner response 流程。"
+ },
+ {
+ "field_id": "telegram_digest_gate",
+ "required": true,
+ "owner_agent": "openclaw",
+ "description": "P2-402D 才能啟用 action-required digest;成功掃描不得洗版。"
+ },
+ {
+ "field_id": "rollback_and_disable_plan",
+ "required": true,
+ "owner_agent": "hermes",
+ "description": "出現 PR storm、DB download failure、runner overload 或大量 false-positive 時的停用流程。"
+ }
+ ],
+ "approval_boundaries": {
+ "tool_install_allowed": false,
+ "ci_workflow_change_allowed": false,
+ "external_registry_lookup_allowed": false,
+ "vulnerability_database_download_allowed": false,
+ "package_upgrade_allowed": false,
+ "lockfile_write_allowed": false,
+ "docker_build_allowed": false,
+ "image_pull_allowed": false,
+ "gitea_pr_creation_allowed": false,
+ "auto_merge_allowed": false,
+ "telegram_direct_send_allowed": false,
+ "paid_service_enabled": false,
+ "production_route_change_allowed": false,
+ "secret_plaintext_allowed": false
+ },
+ "rollups": {
+ "tool_count": 5,
+ "source_evidence_count": 5,
+ "adoption_lane_count": 4,
+ "approval_required_tool_ids": [
+ "renovate_gitea",
+ "osv_scanner",
+ "trivy",
+ "syft",
+ "grype"
+ ],
+ "ci_change_required_tool_ids": [
+ "renovate_gitea",
+ "osv_scanner",
+ "trivy",
+ "syft",
+ "grype"
+ ],
+ "secret_required_tool_ids": [
+ "renovate_gitea",
+ "trivy",
+ "syft",
+ "grype"
+ ],
+ "external_data_required_tool_ids": [
+ "renovate_gitea",
+ "osv_scanner",
+ "trivy",
+ "grype"
+ ],
+ "sbom_tool_ids": [
+ "syft",
+ "trivy"
+ ],
+ "vulnerability_scan_tool_ids": [
+ "osv_scanner",
+ "trivy",
+ "grype"
+ ],
+ "update_pr_tool_ids": [
+ "renovate_gitea"
+ ],
+ "tool_install_allowed_count": 0,
+ "ci_change_allowed_count": 0,
+ "auto_merge_allowed_count": 0,
+ "telegram_direct_send_count": 0,
+ "next_approval_task_ids": [
+ "P2-402D",
+ "P2-402E",
+ "P2-402F",
+ "P2-402G"
+ ]
+ }
+}
diff --git a/docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json b/docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json
new file mode 100644
index 000000000..c71fdffaa
--- /dev/null
+++ b/docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json
@@ -0,0 +1,474 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://awoooi.wooo.work/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json",
+ "title": "AI Agent Tool Adoption Approval Package v1",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "schema_version",
+ "generated_at",
+ "program_status",
+ "source_evidence",
+ "agent_review_matrix",
+ "adoption_lanes",
+ "tool_candidates",
+ "approval_packet_fields",
+ "approval_boundaries",
+ "rollups"
+ ],
+ "properties": {
+ "schema_version": {
+ "const": "ai_agent_tool_adoption_approval_package_v1"
+ },
+ "generated_at": {
+ "type": "string"
+ },
+ "program_status": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "overall_completion_percent",
+ "current_priority",
+ "current_task_id",
+ "next_task_id",
+ "read_only_mode",
+ "runtime_authority",
+ "status_note"
+ ],
+ "properties": {
+ "overall_completion_percent": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100
+ },
+ "current_priority": {
+ "type": "string"
+ },
+ "current_task_id": {
+ "type": "string"
+ },
+ "next_task_id": {
+ "type": "string"
+ },
+ "read_only_mode": {
+ "const": true
+ },
+ "runtime_authority": {
+ "const": "approval_package_only_no_tool_install_or_ci_change"
+ },
+ "status_note": {
+ "type": "string"
+ }
+ }
+ },
+ "source_evidence": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "source_id",
+ "tool_id",
+ "name",
+ "url",
+ "retrieved_at",
+ "evidence_type",
+ "usage_boundary"
+ ],
+ "properties": {
+ "source_id": {
+ "type": "string"
+ },
+ "tool_id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "retrieved_at": {
+ "type": "string"
+ },
+ "evidence_type": {
+ "type": "string"
+ },
+ "usage_boundary": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "agent_review_matrix": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "agent_id",
+ "role",
+ "allowed_now",
+ "blocked_until_approval",
+ "output"
+ ],
+ "properties": {
+ "agent_id": {
+ "type": "string"
+ },
+ "role": {
+ "type": "string"
+ },
+ "allowed_now": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "blocked_until_approval": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "output": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "adoption_lanes": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "lane_id",
+ "display_name",
+ "owner_agent",
+ "tool_ids",
+ "approval_gate",
+ "planned_output",
+ "blocked_now"
+ ],
+ "properties": {
+ "lane_id": {
+ "type": "string"
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "owner_agent": {
+ "type": "string"
+ },
+ "tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "approval_gate": {
+ "type": "string"
+ },
+ "planned_output": {
+ "type": "string"
+ },
+ "blocked_now": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "tool_candidates": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "tool_id",
+ "display_name",
+ "category",
+ "owner_agent",
+ "adoption_status",
+ "risk_tier",
+ "cost_profile",
+ "secret_requirement",
+ "external_data_requirement",
+ "ci_change_required",
+ "official_source_refs",
+ "intended_scope",
+ "approval_requirements",
+ "blocked_until_approval",
+ "recommendation",
+ "rollback_plan"
+ ],
+ "properties": {
+ "tool_id": {
+ "type": "string"
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "category": {
+ "type": "string"
+ },
+ "owner_agent": {
+ "type": "string"
+ },
+ "adoption_status": {
+ "enum": [
+ "approval_required",
+ "defer_until_gate",
+ "rejected"
+ ]
+ },
+ "risk_tier": {
+ "enum": [
+ "low",
+ "medium",
+ "high"
+ ]
+ },
+ "cost_profile": {
+ "type": "string"
+ },
+ "secret_requirement": {
+ "type": "string"
+ },
+ "external_data_requirement": {
+ "type": "string"
+ },
+ "ci_change_required": {
+ "type": "boolean"
+ },
+ "official_source_refs": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string"
+ }
+ },
+ "intended_scope": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "approval_requirements": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string"
+ }
+ },
+ "blocked_until_approval": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "recommendation": {
+ "type": "string"
+ },
+ "rollback_plan": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "approval_packet_fields": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "field_id",
+ "required",
+ "owner_agent",
+ "description"
+ ],
+ "properties": {
+ "field_id": {
+ "type": "string"
+ },
+ "required": {
+ "type": "boolean"
+ },
+ "owner_agent": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "approval_boundaries": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "tool_install_allowed",
+ "ci_workflow_change_allowed",
+ "external_registry_lookup_allowed",
+ "vulnerability_database_download_allowed",
+ "package_upgrade_allowed",
+ "lockfile_write_allowed",
+ "docker_build_allowed",
+ "image_pull_allowed",
+ "gitea_pr_creation_allowed",
+ "auto_merge_allowed",
+ "telegram_direct_send_allowed",
+ "paid_service_enabled",
+ "production_route_change_allowed",
+ "secret_plaintext_allowed"
+ ],
+ "properties": {
+ "tool_install_allowed": {
+ "const": false
+ },
+ "ci_workflow_change_allowed": {
+ "const": false
+ },
+ "external_registry_lookup_allowed": {
+ "const": false
+ },
+ "vulnerability_database_download_allowed": {
+ "const": false
+ },
+ "package_upgrade_allowed": {
+ "const": false
+ },
+ "lockfile_write_allowed": {
+ "const": false
+ },
+ "docker_build_allowed": {
+ "const": false
+ },
+ "image_pull_allowed": {
+ "const": false
+ },
+ "gitea_pr_creation_allowed": {
+ "const": false
+ },
+ "auto_merge_allowed": {
+ "const": false
+ },
+ "telegram_direct_send_allowed": {
+ "const": false
+ },
+ "paid_service_enabled": {
+ "const": false
+ },
+ "production_route_change_allowed": {
+ "const": false
+ },
+ "secret_plaintext_allowed": {
+ "const": false
+ }
+ }
+ },
+ "rollups": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "tool_count",
+ "source_evidence_count",
+ "adoption_lane_count",
+ "approval_required_tool_ids",
+ "ci_change_required_tool_ids",
+ "secret_required_tool_ids",
+ "external_data_required_tool_ids",
+ "sbom_tool_ids",
+ "vulnerability_scan_tool_ids",
+ "update_pr_tool_ids",
+ "tool_install_allowed_count",
+ "ci_change_allowed_count",
+ "auto_merge_allowed_count",
+ "telegram_direct_send_count",
+ "next_approval_task_ids"
+ ],
+ "properties": {
+ "tool_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "source_evidence_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "adoption_lane_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "approval_required_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "ci_change_required_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "secret_required_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "external_data_required_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "sbom_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "vulnerability_scan_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "update_pr_tool_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "tool_install_allowed_count": {
+ "const": 0
+ },
+ "ci_change_allowed_count": {
+ "const": 0
+ },
+ "auto_merge_allowed_count": {
+ "const": 0
+ },
+ "telegram_direct_send_count": {
+ "const": 0
+ },
+ "next_approval_task_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
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 3e610aa5e..1c8d98f67 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
@@ -670,19 +670,23 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
| 檔案 / API | 用途 |
|---|---|
| `docs/schemas/ai_agent_proactive_operations_contract_v1.schema.json` | 主動營運委派、版本生命週期、MCP、RAG、Telegram policy、approval boundary 契約 |
-| `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` | 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory;完成度 `42%` |
+| `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` | 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory;完成度 `55%` |
| `apps/api/src/services/ai_agent_proactive_operations_contract.py` | 只讀 loader;強制 runtime update / package upgrade / host upgrade / workflow schedule / auto merge / Telegram direct send 全部 false |
| `GET /api/v1/agents/agent-proactive-operations-contract` | 治理 API;只回傳 committed snapshot,不啟用排程、不升級、不呼叫付費服務 |
| `docs/schemas/ai_agent_version_freshness_snapshot_v1.schema.json` | P2-402B repo-only 版本新鮮度 schema;鎖定 schedule / external lookup / upgrade / Telegram / PR / host probe 全部 false |
| `docs/evaluations/ai_agent_version_freshness_snapshot_2026-06-11.json` | P2-402B committed snapshot:12 類 repo-only 版本來源、5 個每日設計步驟、3 Agent 角色、5 個 action-required source |
| `GET /api/v1/agents/agent-version-freshness-snapshot` | 只讀 API;回傳 repo-only freshness snapshot,不觸發掃描、不查外部、不改 repo、不送 Telegram |
+| `docs/schemas/ai_agent_tool_adoption_approval_package_v1.schema.json` | P2-402C 工具採用批准包 schema;鎖定 tool install / CI change / external lookup / vulnerability DB / PR / Telegram 全部 false |
+| `docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json` | P2-402C committed snapshot:Renovate / OSV-Scanner / Trivy / Syft / Grype 官方來源、採用 lane、批准欄位與 rollback gate |
+| `GET /api/v1/agents/agent-tool-adoption-approval-package` | 只讀 API;只回傳工具採用批准包,不安裝工具、不改 CI、不查外部、不建 PR、不發 Telegram |
**採用順序:**
1. 先做 repo-only daily freshness:manifest / lockfile / Dockerfile / K8s YAML / runbook / snapshot。✅ P2-402B 已建立 committed snapshot/API;每日排程仍未授權。
-2. 再評估 external primary source weekly watch:Renovate、OSV-Scanner、Trivy、Syft、Grype、Kubernetes skew policy、Docker Scout。下一步 P2-402C 只建立工具採用批准包。
-3. 再進 Gitea PR 草案 lane:grouping、automerge=false、tests、rollback、owner response。
-4. 最後才進人工批准後的 dry-run / smoke / canary / production rollout。
+2. 再評估 external primary source weekly watch:Renovate、OSV-Scanner、Trivy、Syft、Grype、Kubernetes skew policy、Docker Scout。✅ P2-402C 已建立工具採用批准包;工具安裝、CI 變更、外部查詢仍未授權。
+3. 建立 Telegram action-required digest policy,先定義 critical / action-required / failure-only 通知門檻,禁止成功洗版。下一步 P2-402D。
+4. 再進 Gitea PR 草案 lane:grouping、automerge=false、tests、rollback、owner response。
+5. 最後才進人工批准後的 dry-run / smoke / canary / production rollout。
#### 3.2.2 核心缺口與災難場景
@@ -1318,6 +1322,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
| 主動溝通與學習契約 | `docs/evaluations/ai_agent_communication_learning_contract_2026-06-11.json` + `GET /api/v1/agents/agent-communication-learning-contract` | 先固定 OpenClaw / Hermes / NemoTron 主動溝通、MCP、RAG、學習與 redaction 邊界;不啟動 runtime worker | L4×D2 / L7×D4 |
| 主動營運委派與版本生命週期契約 | `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` + `GET /api/v1/agents/agent-proactive-operations-contract` | 先固定 12 類版本 domain、24 類可委派能力、MCP/RAG/Telegram 邊界;不啟用排程、不自動升版 | L4×D2 / L7×D4 / L6×D6 |
| Repo-only 版本新鮮度快照 | `docs/evaluations/ai_agent_version_freshness_snapshot_2026-06-11.json` + `GET /api/v1/agents/agent-version-freshness-snapshot` | P2-402B:讓 Hermes 可每日產生 repo-only freshness evidence packet;目前只讀 API,不啟用 schedule、不查外部、不升級 | L4×D2 / L7×D4 / L6×D6 |
+| 工具採用批准包 | `docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json` + `GET /api/v1/agents/agent-tool-adoption-approval-package` | P2-402C:讓 OpenClaw / Hermes / NemoTron 用官方來源評估 Renovate / OSV-Scanner / Trivy / Syft / Grype;目前只讀 API,不安裝工具、不改 CI、不查外部、不建 PR、不發 Telegram | L4×D2 / L7×D4 / L6×D6 |
**退出條件(量化)**
@@ -1695,6 +1700,12 @@ Phase 6 完成後
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `42%`,current task `P2-402B`,next task `P2-402C`。
- 本波仍不啟用 workflow schedule、不查外部 registry/CVE、不安裝或升級套件、不寫 lockfile、不 build/pull image、不 probe host、不建立 PR、不發 Telegram;P2-402C 才建立 Renovate / OSV / Trivy / Syft / Grype 工具採用批准包。
+### 2026-06-11 23:35 (台北) — §3.2 / §5 — 完成 P2-402C 工具採用批准包 — 回應統帥要求讓 Agent 主動管理套件、工具與服務版本但先走批准制
+
+- 新增 `ai_agent_tool_adoption_approval_package_v1` schema / committed snapshot / loader / API / 測試,定義 Renovate / OSV-Scanner / Trivy / Syft / Grype 的官方來源、採用 lane、secret / CI / external data gate、rollback plan 與 Agent 分工。
+- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `55%`,current task `P2-402C`,next task `P2-402D`。
+- 本波仍不安裝工具、不改 CI、不查外部 registry、不下載 vulnerability DB、不升級套件、不寫 lockfile、不 build/pull image、不建立 Gitea PR、不 auto merge、不發 Telegram;P2-402D 才建立 Telegram action-required digest policy。
+
### 2026-04-15 (台北) — 全檔 — 建立 v2 骨架,§0/§1 完成 — 統帥批准「單 MASTER + 4 道閘門」結構
- 從 v1(plans/2026-04-15-MASTER-ai-autonomous-flywheel.md)繼承核心發現