feat(governance): 接入三 Agent 佈建布局
All checks were successful
CD Pipeline / tests (push) Successful in 1m24s
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / build-and-deploy (push) Successful in 6m5s
CD Pipeline / post-deploy-checks (push) Successful in 1m37s

This commit is contained in:
Your Name
2026-06-11 11:27:50 +08:00
parent 3418e014bc
commit e427af3cb2
13 changed files with 2308 additions and 41 deletions

View File

@@ -35,70 +35,73 @@ from pydantic import BaseModel, Field
from src.core.logging import get_logger
from src.core.sse import get_publisher
from src.services.agent_market_governance_snapshot import (
load_latest_agent_market_governance_snapshot,
)
from src.services.agent_service import (
AgentService,
TaskState,
get_agent_service,
)
from src.services.ai_agent_automation_backlog_snapshot import (
load_latest_ai_agent_automation_backlog_snapshot,
)
from src.services.ai_agent_automation_inventory_snapshot import (
load_latest_ai_agent_automation_inventory_snapshot,
)
from src.services.agent_market_governance_snapshot import (
load_latest_agent_market_governance_snapshot,
from src.services.ai_agent_deployment_layout import (
load_latest_ai_agent_deployment_layout,
)
from src.services.backup_dr_target_inventory import (
load_latest_backup_dr_target_inventory,
from src.services.ai_provider_route_matrix import (
load_latest_ai_provider_route_matrix,
)
from src.services.backup_dr_readiness_matrix import (
load_latest_backup_dr_readiness_matrix,
)
from src.services.backup_dr_target_inventory import (
load_latest_backup_dr_target_inventory,
)
from src.services.backup_notification_policy import (
load_latest_backup_notification_policy,
)
from src.services.backup_restore_drill_approval_package_template import (
load_latest_backup_restore_drill_approval_package_template,
)
from src.services.offsite_escrow_readiness_status import (
load_latest_offsite_escrow_readiness_status,
)
from src.services.runtime_surface_inventory import (
load_latest_runtime_surface_inventory,
)
from src.services.gitea_workflow_runner_health import (
load_latest_gitea_workflow_runner_health,
)
from src.services.observability_contract_matrix import (
load_latest_observability_contract_matrix,
)
from src.services.ai_provider_route_matrix import (
load_latest_ai_provider_route_matrix,
)
from src.services.service_health_gap_matrix import (
load_latest_service_health_gap_matrix,
)
from src.services.service_health_failure_notification_policy import (
load_latest_service_health_failure_notification_policy,
)
from src.services.package_supply_chain_inventory import (
load_latest_package_supply_chain_inventory,
)
from src.services.javascript_package_inventory import (
load_latest_javascript_package_inventory,
)
from src.services.docker_build_surface_inventory import (
load_latest_docker_build_surface_inventory,
from src.services.dependency_drift_check_plan import (
load_latest_dependency_drift_check_plan,
)
from src.services.dependency_risk_policy import (
load_latest_dependency_risk_policy,
)
from src.services.dependency_drift_check_plan import (
load_latest_dependency_drift_check_plan,
)
from src.services.dependency_upgrade_approval_package_template import (
load_latest_dependency_upgrade_approval_package_template,
)
from src.services.agent_service import (
AgentService,
TaskState,
get_agent_service,
from src.services.docker_build_surface_inventory import (
load_latest_docker_build_surface_inventory,
)
from src.services.gitea_workflow_runner_health import (
load_latest_gitea_workflow_runner_health,
)
from src.services.javascript_package_inventory import (
load_latest_javascript_package_inventory,
)
from src.services.observability_contract_matrix import (
load_latest_observability_contract_matrix,
)
from src.services.offsite_escrow_readiness_status import (
load_latest_offsite_escrow_readiness_status,
)
from src.services.package_supply_chain_inventory import (
load_latest_package_supply_chain_inventory,
)
from src.services.runtime_surface_inventory import (
load_latest_runtime_surface_inventory,
)
from src.services.service_health_failure_notification_policy import (
load_latest_service_health_failure_notification_policy,
)
from src.services.service_health_gap_matrix import (
load_latest_service_health_gap_matrix,
)
router = APIRouter(prefix="/agents", tags=["Agent Teams"])
@@ -494,6 +497,33 @@ async def get_automation_backlog_snapshot() -> dict[str, Any]:
) from exc
@router.get(
"/agent-deployment-layout",
response_model=dict[str, Any],
summary="取得 AI Agent 佈建布局快照",
description=(
"讀取最新已提交的 OpenClaw / Hermes / NemoTron 佈建布局快照;"
"此端點不部署 Agent、不呼叫外部模型、不送 Telegram、不碰 DB/Redis、不讀 Secret payload、"
"不批准 SDK/API/shadow/canary/生產路由或主機變更。"
),
)
async def get_agent_deployment_layout() -> dict[str, Any]:
"""Return the latest read-only AI Agent deployment layout snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_deployment_layout)
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_deployment_layout_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],

View File

@@ -0,0 +1,135 @@
"""
AI Agent deployment layout snapshot.
Loads the latest committed, read-only layout for OpenClaw, Hermes, and
NemoTron across hosts, packages, tools, services, projects, web surfaces,
learning loops, and Telegram notification boundaries. This module never
deploys agents, sends Telegram messages, calls providers, or approves writes.
"""
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_deployment_layout_*.json"
_SCHEMA_VERSION = "ai_agent_deployment_layout_v1"
def load_latest_ai_agent_deployment_layout(
evaluations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load the newest committed AI Agent deployment layout snapshot."""
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
if not candidates:
raise FileNotFoundError(f"no AI Agent deployment layout 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_layout(payload, str(latest))
_require_rollup_consistency(payload, str(latest))
_require_frontend_redaction(payload, str(latest))
_require_target_boundaries(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_layout(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("deployment_authority") != "layout_only_no_runtime_deploy":
raise ValueError(f"{label}: deployment_authority must stay layout_only_no_runtime_deploy")
boundaries = payload.get("approval_boundaries") or {}
blocked_flags = {
"sdk_installation_allowed",
"paid_api_call_allowed",
"shadow_or_canary_allowed",
"production_routing_allowed",
"destructive_operation_allowed",
"secret_plaintext_allowed",
"autonomous_host_mutation_allowed",
"telegram_direct_send_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:
targets = payload.get("deployment_targets") or []
rollups = payload.get("rollups") or {}
if rollups.get("total_targets") != len(targets):
raise ValueError(f"{label}: rollups.total_targets must match deployment_targets")
if rollups.get("by_domain") != _count_by(targets, "domain_id"):
raise ValueError(f"{label}: rollups.by_domain must match deployment_targets")
if rollups.get("by_primary_agent") != _count_by(targets, "primary_agent"):
raise ValueError(f"{label}: rollups.by_primary_agent must match deployment_targets")
if rollups.get("by_deployment_state") != _count_by(targets, "deployment_state"):
raise ValueError(f"{label}: rollups.by_deployment_state must match deployment_targets")
if rollups.get("by_telegram_policy") != _count_by(targets, "telegram_policy"):
raise ValueError(f"{label}: rollups.by_telegram_policy must match deployment_targets")
blocked_target_ids = sorted(
target.get("target_id")
for target in targets
if target.get("deployment_state") == "blocked_by_gate"
or target.get("automation_level") == "blocked"
)
if sorted(rollups.get("blocked_target_ids") or []) != blocked_target_ids:
raise ValueError(f"{label}: rollups.blocked_target_ids must match blocked targets")
def _require_frontend_redaction(payload: dict[str, Any], label: str) -> None:
redaction = ((payload.get("collaboration_contract") or {}).get("frontend_redaction") or {})
if redaction.get("operator_conversation_display_allowed") is not False:
raise ValueError(f"{label}: operator conversation display must stay false")
if redaction.get("agent_private_reasoning_display_allowed") is not False:
raise ValueError(f"{label}: agent private reasoning display must stay false")
def _require_target_boundaries(payload: dict[str, Any], label: str) -> None:
targets = payload.get("deployment_targets") or []
missing = [
target.get("target_id")
for target in targets
if not target.get("approval_gate")
or not target.get("telegram_policy")
or not target.get("communication_channels")
]
if missing:
raise ValueError(f"{label}: deployment targets missing boundary fields: {sorted(missing)}")
invalid_nemotron_runtime = [
target.get("target_id")
for target in targets
if target.get("primary_agent") == "nemotron"
and target.get("automation_level") not in {"observe_only", "blocked"}
]
if invalid_nemotron_runtime:
raise ValueError(f"{label}: Nemotron targets must stay observe_only or blocked")
def _count_by(items: list[dict[str, Any]], key: str) -> dict[str, int]:
counts: dict[str, int] = {}
for item in items:
value = item.get(key)
counts[value] = counts.get(value, 0) + 1
return counts

View File

@@ -0,0 +1,186 @@
from __future__ import annotations
import json
import pytest
from src.services.ai_agent_deployment_layout import (
load_latest_ai_agent_deployment_layout,
)
def test_load_latest_ai_agent_deployment_layout_reads_committed_snapshot():
data = load_latest_ai_agent_deployment_layout()
assert data["schema_version"] == "ai_agent_deployment_layout_v1"
assert data["program_status"]["read_only_mode"] is True
assert data["program_status"]["deployment_authority"] == "layout_only_no_runtime_deploy"
assert data["rollups"]["total_targets"] == len(data["deployment_targets"]) == 42
assert data["rollups"]["by_primary_agent"]["openclaw"] == 17
assert data["rollups"]["by_primary_agent"]["hermes"] == 23
assert data["rollups"]["by_primary_agent"]["nemotron"] == 2
assert data["rollups"]["blocked_target_ids"] == [
"host_120",
"nemotron_replay_pipeline",
]
assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False
assert data["approval_boundaries"]["production_routing_allowed"] is False
assert data["collaboration_contract"]["frontend_redaction"]["operator_conversation_display_allowed"] is False
nemotron_targets = [
target for target in data["deployment_targets"] if target["primary_agent"] == "nemotron"
]
assert {target["automation_level"] for target in nemotron_targets} <= {"observe_only", "blocked"}
def test_load_latest_ai_agent_deployment_layout_rejects_runtime_authority(tmp_path):
snapshot = _snapshot()
snapshot["approval_boundaries"]["production_routing_allowed"] = True
(tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="approval boundaries"):
load_latest_ai_agent_deployment_layout(tmp_path)
def test_load_latest_ai_agent_deployment_layout_rejects_rollup_mismatch(tmp_path):
snapshot = _snapshot()
snapshot["rollups"]["by_primary_agent"]["openclaw"] = 99
(tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="by_primary_agent"):
load_latest_ai_agent_deployment_layout(tmp_path)
def test_load_latest_ai_agent_deployment_layout_rejects_nemotron_runtime(tmp_path):
snapshot = _snapshot()
snapshot["deployment_targets"][0]["primary_agent"] = "nemotron"
snapshot["deployment_targets"][0]["automation_level"] = "hitl_execute_after_approval"
snapshot["rollups"]["by_primary_agent"] = {"nemotron": 1}
(tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="Nemotron targets"):
load_latest_ai_agent_deployment_layout(tmp_path)
def _snapshot() -> dict:
return {
"schema_version": "ai_agent_deployment_layout_v1",
"generated_at": "2026-06-11T18:20:00+08:00",
"program_status": {
"overall_completion_percent": 10,
"current_priority": "P1",
"current_task_id": "P1-401",
"next_task_id": "P1-402",
"read_only_mode": True,
"deployment_authority": "layout_only_no_runtime_deploy",
},
"agent_contracts": [
{
"agent_id": "openclaw",
"display_name": "OpenClaw",
"primary_specialty": "生產仲裁",
"deployment_lane": "production_decision_core",
"allowed_autonomy": ["只讀診斷"],
"must_delegate_to": ["Hermes"],
"blocked_actions": ["production_write"],
"learning_scope": ["incident"],
},
{
"agent_id": "hermes",
"display_name": "Hermes",
"primary_specialty": "治理",
"deployment_lane": "governance",
"allowed_autonomy": ["只讀盤點"],
"must_delegate_to": ["OpenClaw"],
"blocked_actions": ["production_write"],
"learning_scope": ["docs"],
},
{
"agent_id": "nemotron",
"display_name": "NemoTron",
"primary_specialty": "離線評估",
"deployment_lane": "offline_evaluator",
"allowed_autonomy": ["smoke"],
"must_delegate_to": ["OpenClaw"],
"blocked_actions": ["production_route"],
"learning_scope": ["replay"],
},
],
"domains": [
{
"domain_id": "hosts",
"display_name": "主機",
"description": "測試主機",
}
],
"deployment_targets": [
{
"target_id": "host_110",
"domain_id": "hosts",
"display_name": "110",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["monitor"],
"telegram_policy": "failure_only",
"learning_inputs": ["metrics"],
"communication_channels": ["Prometheus"],
"approval_gate": "read_only_allowed",
"evidence_refs": ["infra/ansible/inventory/hosts.yml"],
"next_action": "觀察。",
}
],
"collaboration_contract": {
"message_bus": "Redis Streams",
"audit_trail": "AgentSession",
"handoff_rules": ["OpenClaw 仲裁"],
"frontend_redaction": {
"operator_conversation_display_allowed": False,
"agent_private_reasoning_display_allowed": False,
"display_policy": "只顯示摘要。",
},
},
"learning_contract": {
"event_sources": ["incident"],
"feedback_loops": ["trust"],
"growth_metrics": ["precision"],
"retention_policy": "redacted evidence only",
},
"telegram_contract": {
"primary_gateway": "telegram_gateway.py",
"bot_roles": ["OpenClaw"],
"notification_classes": ["failure"],
"redaction_policy": "redacted",
"e2e_validation": "ADR-035",
},
"rollups": {
"total_targets": 1,
"by_domain": {"hosts": 1},
"by_primary_agent": {"openclaw": 1},
"by_deployment_state": {"active_governed": 1},
"by_telegram_policy": {"failure_only": 1},
"blocked_target_ids": [],
"approval_required_target_ids": [],
},
"approval_boundaries": {
"sdk_installation_allowed": False,
"paid_api_call_allowed": False,
"shadow_or_canary_allowed": False,
"production_routing_allowed": False,
"destructive_operation_allowed": False,
"secret_plaintext_allowed": False,
"autonomous_host_mutation_allowed": False,
"telegram_direct_send_allowed": False,
},
}

View File

@@ -0,0 +1,31 @@
from __future__ import annotations
from fastapi import FastAPI
from fastapi.testclient import TestClient
from src.api.v1.agents import router
def test_ai_agent_deployment_layout_endpoint_returns_committed_snapshot():
app = FastAPI()
app.include_router(router, prefix="/api/v1")
client = TestClient(app)
response = client.get("/api/v1/agents/agent-deployment-layout")
assert response.status_code == 200
data = response.json()
assert data["schema_version"] == "ai_agent_deployment_layout_v1"
assert data["program_status"]["overall_completion_percent"] == 45
assert data["program_status"]["current_task_id"] == "P1-402"
assert data["program_status"]["next_task_id"] == "P1-403"
assert data["program_status"]["read_only_mode"] is True
assert data["rollups"]["total_targets"] == 42
assert data["rollups"]["by_domain"]["hosts"] == 6
assert data["rollups"]["by_domain"]["websites"] == 5
assert data["rollups"]["by_primary_agent"]["nemotron"] == 2
assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False
assert data["approval_boundaries"]["autonomous_host_mutation_allowed"] is False
assert data["collaboration_contract"]["frontend_redaction"]["operator_conversation_display_allowed"] is False
assert any(target["target_id"] == "telegram_gateway" for target in data["deployment_targets"])
assert any(target["target_id"] == "nemotron_replay_pipeline" for target in data["deployment_targets"])

View File

@@ -2965,6 +2965,72 @@
"taskBoundaries": "任務邊界",
"explicitApprovalTasks": "需明確批准"
},
"deploymentLayout": {
"title": "OpenClaw / Hermes / NemoTron 佈建布局",
"source": "{generated} · {current} → {next}",
"targetsTitle": "優先佈建目標",
"targetsShown": "顯示 {count}/{total}",
"agentContractTitle": "Agent 專長分工",
"telegramTitle": "Telegram Bot 告警合約",
"telegramDetail": "統一入口 {gateway},通知類別 {classes} 種Agent 不直接持有 token也不直接發送。",
"learningTitle": "主動學習與協作",
"learningDetail": "事件來源 {sources}、回饋迴圈 {loops}、成長指標 {metrics};先落只讀證據,再走批准關卡。",
"redactionLocked": "前端不顯示對話內容",
"directSendBlocked": "Telegram 直送禁止",
"frontendSafe": "只顯示狀態、證據與邊界",
"metrics": {
"targets": "佈建目標",
"openclaw": "OpenClaw",
"hermes": "Hermes",
"nemotron": "NemoTron",
"approval": "需批准",
"blocked": "阻擋"
},
"labels": {
"domain": "領域",
"primary": "主責",
"support": "協作",
"telegram": "告警",
"automation": "自動化",
"evidence": "證據",
"bus": "協作匯流"
},
"agents": {
"openclaw": "OpenClaw",
"hermes": "Hermes",
"nemotron": "NemoTron"
},
"domains": {
"hosts": "主機",
"packages": "套件",
"tools": "工具",
"services": "服務",
"projects": "專案",
"websites": "網站前後台",
"learning": "學習協作"
},
"deploymentStates": {
"active_governed": "治理中",
"read_only_layout": "只讀布局",
"blocked_by_gate": "關卡阻擋",
"planned": "規劃中",
"candidate_only": "候選"
},
"automationLevels": {
"observe_only": "只讀觀察",
"prepare_only": "只準備提案",
"dry_run_only": "僅乾跑",
"hitl_execute_after_approval": "批准後 HITL 執行",
"blocked": "阻擋"
},
"telegramPolicies": {
"failure_only": "僅失敗",
"action_required": "需處置",
"approval_required": "需批准",
"daily_summary_only": "僅日報",
"no_direct_notify": "不直送"
}
},
"overview": {
"title": "決策指揮摘要",
"mode": "只讀決策支援",

View File

@@ -2965,6 +2965,72 @@
"taskBoundaries": "任務邊界",
"explicitApprovalTasks": "需明確批准"
},
"deploymentLayout": {
"title": "OpenClaw / Hermes / NemoTron 佈建布局",
"source": "{generated} · {current} → {next}",
"targetsTitle": "優先佈建目標",
"targetsShown": "顯示 {count}/{total}",
"agentContractTitle": "Agent 專長分工",
"telegramTitle": "Telegram Bot 告警合約",
"telegramDetail": "統一入口 {gateway},通知類別 {classes} 種Agent 不直接持有 token也不直接發送。",
"learningTitle": "主動學習與協作",
"learningDetail": "事件來源 {sources}、回饋迴圈 {loops}、成長指標 {metrics};先落只讀證據,再走批准關卡。",
"redactionLocked": "前端不顯示對話內容",
"directSendBlocked": "Telegram 直送禁止",
"frontendSafe": "只顯示狀態、證據與邊界",
"metrics": {
"targets": "佈建目標",
"openclaw": "OpenClaw",
"hermes": "Hermes",
"nemotron": "NemoTron",
"approval": "需批准",
"blocked": "阻擋"
},
"labels": {
"domain": "領域",
"primary": "主責",
"support": "協作",
"telegram": "告警",
"automation": "自動化",
"evidence": "證據",
"bus": "協作匯流"
},
"agents": {
"openclaw": "OpenClaw",
"hermes": "Hermes",
"nemotron": "NemoTron"
},
"domains": {
"hosts": "主機",
"packages": "套件",
"tools": "工具",
"services": "服務",
"projects": "專案",
"websites": "網站前後台",
"learning": "學習協作"
},
"deploymentStates": {
"active_governed": "治理中",
"read_only_layout": "只讀布局",
"blocked_by_gate": "關卡阻擋",
"planned": "規劃中",
"candidate_only": "候選"
},
"automationLevels": {
"observe_only": "只讀觀察",
"prepare_only": "只準備提案",
"dry_run_only": "僅乾跑",
"hitl_execute_after_approval": "批准後 HITL 執行",
"blocked": "阻擋"
},
"telegramPolicies": {
"failure_only": "僅失敗",
"action_required": "需處置",
"approval_required": "需批准",
"daily_summary_only": "僅日報",
"no_direct_notify": "不直送"
}
},
"overview": {
"title": "決策指揮摘要",
"mode": "只讀決策支援",

View File

@@ -35,6 +35,7 @@ import { GlassCard } from '@/components/ui/glass-card'
import { StatusOrb } from '@/components/ui/status-orb'
import {
apiClient,
type AiAgentDeploymentLayoutSnapshot,
type AiProviderRouteMatrixSnapshot,
type AiAgentAutomationBacklogSnapshot,
type AiAgentAutomationInventorySnapshot,
@@ -311,6 +312,7 @@ export function AutomationInventoryTab() {
const [giteaHealth, setGiteaHealth] = useState<GiteaWorkflowRunnerHealthSnapshot | null>(null)
const [observabilityMatrix, setObservabilityMatrix] = useState<ObservabilityContractMatrixSnapshot | null>(null)
const [providerRouteMatrix, setProviderRouteMatrix] = useState<AiProviderRouteMatrixSnapshot | null>(null)
const [deploymentLayout, setDeploymentLayout] = useState<AiAgentDeploymentLayoutSnapshot | null>(null)
const [serviceHealthGapMatrix, setServiceHealthGapMatrix] = useState<ServiceHealthGapMatrixSnapshot | null>(null)
const [serviceHealthNotificationPolicy, setServiceHealthNotificationPolicy] = useState<ServiceHealthFailureNotificationPolicySnapshot | null>(null)
const [loading, setLoading] = useState(true)
@@ -329,6 +331,7 @@ export function AutomationInventoryTab() {
apiClient.getGiteaWorkflowRunnerHealth(),
apiClient.getObservabilityContractMatrix(),
apiClient.getAiProviderRouteMatrix(),
apiClient.getAiAgentDeploymentLayout(),
apiClient.getServiceHealthGapMatrix(),
apiClient.getServiceHealthFailureNotificationPolicy(),
] as const
@@ -346,6 +349,7 @@ export function AutomationInventoryTab() {
giteaHealthResult,
observabilityMatrixResult,
providerRouteMatrixResult,
deploymentLayoutResult,
serviceHealthGapMatrixResult,
serviceHealthNotificationPolicyResult,
] = results
@@ -360,6 +364,7 @@ export function AutomationInventoryTab() {
setGiteaHealth(giteaHealthResult.status === 'fulfilled' ? giteaHealthResult.value : null)
setObservabilityMatrix(observabilityMatrixResult.status === 'fulfilled' ? observabilityMatrixResult.value : null)
setProviderRouteMatrix(providerRouteMatrixResult.status === 'fulfilled' ? providerRouteMatrixResult.value : null)
setDeploymentLayout(deploymentLayoutResult.status === 'fulfilled' ? deploymentLayoutResult.value : null)
setServiceHealthGapMatrix(serviceHealthGapMatrixResult.status === 'fulfilled' ? serviceHealthGapMatrixResult.value : null)
setServiceHealthNotificationPolicy(serviceHealthNotificationPolicyResult.status === 'fulfilled' ? serviceHealthNotificationPolicyResult.value : null)
setError([
@@ -372,6 +377,7 @@ export function AutomationInventoryTab() {
giteaHealthResult,
observabilityMatrixResult,
providerRouteMatrixResult,
deploymentLayoutResult,
serviceHealthGapMatrixResult,
serviceHealthNotificationPolicyResult,
].some(result => result.status === 'rejected'))
@@ -409,6 +415,35 @@ export function AutomationInventoryTab() {
.filter(group => group.items.length > 0)
}, [backlog])
const visibleDeploymentTargets = useMemo(() => {
if (!deploymentLayout) return []
const statePriority = {
blocked_by_gate: 0,
active_governed: 1,
read_only_layout: 2,
planned: 3,
candidate_only: 4,
} as Record<string, number>
const telegramPriority = {
approval_required: 0,
action_required: 1,
failure_only: 2,
daily_summary_only: 3,
no_direct_notify: 4,
} as Record<string, number>
return [...deploymentLayout.deployment_targets]
.sort((a, b) => {
const stateLeft = statePriority[a.deployment_state] ?? 5
const stateRight = statePriority[b.deployment_state] ?? 5
if (stateLeft !== stateRight) return stateLeft - stateRight
const telegramLeft = telegramPriority[a.telegram_policy] ?? 5
const telegramRight = telegramPriority[b.telegram_policy] ?? 5
if (telegramLeft !== telegramRight) return telegramLeft - telegramRight
return a.target_id.localeCompare(b.target_id)
})
.slice(0, 12)
}, [deploymentLayout])
const visibleReadinessRows = useMemo(() => {
if (!backupReadiness) return []
const priority = { blocked: 0, action_required: 1, deferred: 2, ready: 3 } as Record<string, number>
@@ -552,7 +587,7 @@ export function AutomationInventoryTab() {
)
}
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -619,6 +654,12 @@ export function AutomationInventoryTab() {
+ providerRouteMatrix.rollups.shadow_or_canary_allowed_count
+ providerRouteMatrix.rollups.runtime_route_change_allowed_count
)
const deploymentLayoutTargets = deploymentLayout.rollups.total_targets
const deploymentLayoutOpenClawTargets = deploymentLayout.rollups.by_primary_agent.openclaw ?? 0
const deploymentLayoutHermesTargets = deploymentLayout.rollups.by_primary_agent.hermes ?? 0
const deploymentLayoutNemotronTargets = deploymentLayout.rollups.by_primary_agent.nemotron ?? 0
const deploymentLayoutBlockedTargets = deploymentLayout.rollups.blocked_target_ids.length
const deploymentLayoutApprovalTargets = deploymentLayout.rollups.approval_required_target_ids.length
const serviceHealthActions = serviceHealthGapMatrix.rollups.target_ids_requiring_action.length
const serviceHealthStaleGaps = serviceHealthGapMatrix.rollups.stale_endpoint_ids.length
const serviceHealthOperatorReviews = serviceHealthGapMatrix.rollups.health_gap_ids.length
@@ -771,6 +812,14 @@ export function AutomationInventoryTab() {
}
}
const deploymentLayoutValueLabel = (group: string, value: string) => {
try {
return t(`deploymentLayout.${group}.${value}` as never)
} catch {
return value
}
}
const serviceHealthValueLabel = (value: string) => {
try {
return t(`serviceHealth.values.${value}` as never)
@@ -942,6 +991,124 @@ export function AutomationInventoryTab() {
<MetricCard label={t('metrics.explicitApprovalTasks')} value={explicitApprovalTaskCount} tone={explicitApprovalTaskCount > 0 ? 'warn' : 'ok'} icon={<ShieldCheck size={16} />} />
</div>
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 13, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>
<Route size={14} style={{ color: '#d97757' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('deploymentLayout.title')}
</span>
</div>
<Chip
value={t('deploymentLayout.source', {
generated: formatDateTime(deploymentLayout.generated_at),
current: deploymentLayout.program_status.current_task_id,
next: deploymentLayout.program_status.next_task_id,
})}
muted
/>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, minmax(0, 1fr))', gap: 12 }} className="automation-inventory-deployment-layout-kpi-grid">
<MetricCard label={t('deploymentLayout.metrics.targets')} value={deploymentLayoutTargets} icon={<Boxes size={16} />} />
<MetricCard label={t('deploymentLayout.metrics.openclaw')} value={deploymentLayoutOpenClawTargets} icon={<ShieldCheck size={16} />} />
<MetricCard label={t('deploymentLayout.metrics.hermes')} value={deploymentLayoutHermesTargets} icon={<FileText size={16} />} />
<MetricCard label={t('deploymentLayout.metrics.nemotron')} value={deploymentLayoutNemotronTargets} tone="warn" icon={<Target size={16} />} />
<MetricCard label={t('deploymentLayout.metrics.approval')} value={deploymentLayoutApprovalTargets} tone={deploymentLayoutApprovalTargets > 0 ? 'warn' : 'ok'} icon={<Lock size={16} />} />
<MetricCard label={t('deploymentLayout.metrics.blocked')} value={deploymentLayoutBlockedTargets} tone={deploymentLayoutBlockedTargets > 0 ? 'danger' : 'ok'} icon={<AlertTriangle size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.25fr) minmax(0, 0.75fr)', gap: 12 }} className="automation-inventory-deployment-layout-grid">
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('deploymentLayout.targetsTitle')}
</span>
<Chip value={t('deploymentLayout.targetsShown', { count: visibleDeploymentTargets.length, total: deploymentLayoutTargets })} muted />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-deployment-layout-target-grid">
{visibleDeploymentTargets.map(target => (
<div key={target.target_id} style={{ padding: 11, border: '0.5px solid #e0ddd4', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, minWidth: 0 }}>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, fontWeight: 700, color: '#141413' }}>
{target.target_id}
</span>
<Chip value={deploymentLayoutValueLabel('deploymentStates', target.deployment_state)} muted={target.deployment_state === 'active_governed'} />
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, minWidth: 0 }}>
<Chip value={`${t('deploymentLayout.labels.domain')}: ${deploymentLayoutValueLabel('domains', target.domain_id)}`} muted />
<Chip value={`${t('deploymentLayout.labels.primary')}: ${deploymentLayoutValueLabel('agents', target.primary_agent)}`} />
<Chip value={`${t('deploymentLayout.labels.telegram')}: ${deploymentLayoutValueLabel('telegramPolicies', target.telegram_policy)}`} muted={target.telegram_policy !== 'approval_required'} />
<Chip value={`${t('deploymentLayout.labels.automation')}: ${deploymentLayoutValueLabel('automationLevels', target.automation_level)}`} muted={target.automation_level === 'observe_only'} />
</div>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{target.next_action}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, minWidth: 0 }}>
{target.supporting_agents.length > 0 ? (
<Chip value={`${t('deploymentLayout.labels.support')}: ${target.supporting_agents.map(agent => deploymentLayoutValueLabel('agents', agent)).join(' / ')}`} muted />
) : null}
<Chip value={`${t('deploymentLayout.labels.evidence')}: ${target.evidence_refs.length}`} muted />
</div>
</div>
))}
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ padding: 12, border: '0.5px solid #e0ddd4', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 9, minWidth: 0 }}>
<SmallLabel>{t('deploymentLayout.agentContractTitle')}</SmallLabel>
{deploymentLayout.agent_contracts.map(contract => (
<div key={contract.agent_id} style={{ display: 'flex', flexDirection: 'column', gap: 5, paddingBottom: 7, borderBottom: '0.5px solid #eee9dd' }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413' }}>
{deploymentLayoutValueLabel('agents', contract.agent_id)}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45 }}>
{contract.primary_specialty}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{contract.allowed_autonomy.slice(0, 2).map(item => (
<Chip key={`${contract.agent_id}-${item}`} value={item} muted />
))}
</div>
</div>
))}
</div>
<div style={{ padding: 12, border: '0.5px solid #e0ddd4', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('deploymentLayout.telegramTitle')}</SmallLabel>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{t('deploymentLayout.telegramDetail', {
gateway: deploymentLayout.telegram_contract.primary_gateway,
classes: deploymentLayout.telegram_contract.notification_classes.length,
})}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('deploymentLayout.redactionLocked')} />
<Chip value={t('deploymentLayout.directSendBlocked')} muted />
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #e0ddd4', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('deploymentLayout.learningTitle')}</SmallLabel>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{t('deploymentLayout.learningDetail', {
sources: deploymentLayout.learning_contract.event_sources.length,
loops: deploymentLayout.learning_contract.feedback_loops.length,
metrics: deploymentLayout.learning_contract.growth_metrics.length,
})}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={`${t('deploymentLayout.labels.bus')}: ${deploymentLayout.collaboration_contract.message_bus}`} muted />
<Chip value={t('deploymentLayout.frontendSafe')} />
</div>
</div>
</div>
</div>
</div>
</GlassCard>
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 13, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
@@ -2389,6 +2556,9 @@ export function AutomationInventoryTab() {
.automation-inventory-provider-map-grid,
.automation-inventory-provider-grid,
.automation-inventory-provider-route-grid,
.automation-inventory-deployment-layout-kpi-grid,
.automation-inventory-deployment-layout-grid,
.automation-inventory-deployment-layout-target-grid,
.automation-inventory-service-health-kpi-grid,
.automation-inventory-service-health-map-grid,
.automation-inventory-service-health-evidence-grid,

View File

@@ -262,6 +262,11 @@ export const apiClient = {
return handleResponse<AiAgentAutomationBacklogSnapshot>(res)
},
async getAiAgentDeploymentLayout() {
const res = await fetch(`${API_BASE_URL}/agents/agent-deployment-layout`)
return handleResponse<AiAgentDeploymentLayoutSnapshot>(res)
},
async getRuntimeSurfaceInventory() {
const res = await fetch(`${API_BASE_URL}/agents/runtime-surface-inventory`)
return handleResponse<RuntimeSurfaceInventorySnapshot>(res)
@@ -819,6 +824,109 @@ export interface AiAgentAutomationBacklogSnapshot {
>
}
export interface AiAgentDeploymentLayoutSnapshot {
schema_version: 'ai_agent_deployment_layout_v1'
generated_at: string
program_status: {
overall_completion_percent: number
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
current_task_id: string
next_task_id: string
read_only_mode: true
deployment_authority: 'layout_only_no_runtime_deploy'
}
agent_contracts: Array<{
agent_id: string
display_name: string
primary_specialty: string
deployment_lane: string
allowed_autonomy: string[]
must_delegate_to: string[]
blocked_actions: string[]
learning_scope: string[]
}>
domains: Array<{
domain_id: string
display_name: string
description: string
}>
deployment_targets: Array<{
target_id: string
domain_id: string
display_name: string
target_type: string
primary_agent: string
supporting_agents: string[]
deployment_state:
| 'active_governed'
| 'read_only_layout'
| 'blocked_by_gate'
| 'planned'
| 'candidate_only'
automation_level:
| 'observe_only'
| 'prepare_only'
| 'dry_run_only'
| 'hitl_execute_after_approval'
| 'blocked'
capabilities: string[]
telegram_policy:
| 'failure_only'
| 'action_required'
| 'approval_required'
| 'daily_summary_only'
| 'no_direct_notify'
learning_inputs: string[]
communication_channels: string[]
approval_gate: string
evidence_refs: string[]
next_action: string
}>
collaboration_contract: {
message_bus: string
audit_trail: string
handoff_rules: string[]
frontend_redaction: {
operator_conversation_display_allowed: false
agent_private_reasoning_display_allowed: false
display_policy: string
}
}
learning_contract: {
event_sources: string[]
feedback_loops: string[]
growth_metrics: string[]
retention_policy: string
}
telegram_contract: {
primary_gateway: string
bot_roles: string[]
notification_classes: string[]
redaction_policy: string
e2e_validation: string
}
rollups: {
total_targets: number
by_domain: Record<string, number>
by_primary_agent: Record<string, number>
by_deployment_state: Record<string, number>
by_telegram_policy: Record<string, number>
blocked_target_ids: string[]
approval_required_target_ids: string[]
}
approval_boundaries: Record<
| 'sdk_installation_allowed'
| 'paid_api_call_allowed'
| 'shadow_or_canary_allowed'
| 'production_routing_allowed'
| 'destructive_operation_allowed'
| 'secret_plaintext_allowed'
| 'autonomous_host_mutation_allowed'
| 'telegram_direct_send_allowed',
false
>
}
export interface RuntimeSurfaceInventorySnapshot {
schema_version: 'runtime_surface_inventory_v1'
generated_at: string

View File

@@ -1,3 +1,27 @@
## 2026-06-11OpenClaw / Hermes / NemoTron 佈建布局第一波
**背景**:統帥要求將 OpenClaw、Hermes、NemoTron 依各自專長安排到所有主機、套件、工具、服務、專案、網站前後台,並納入主動學習、互相溝通、持續成長與 Telegram Bot 告警鏈路。本波先建立可驗證的只讀佈建布局,避免把尚未批准的 runtime deploy、SDK/API、Telegram 發送或主機操作誤當已授權。
**完成內容:**
- 新增 `docs/schemas/ai_agent_deployment_layout_v1.schema.json`,定義三 Agent 佈建布局、target、Telegram policy、learning contract、collaboration contract 與 approval boundaries。
- 新增 `docs/evaluations/ai_agent_deployment_layout_2026-06-11.json`,覆蓋 42 個 target6 台主機、3 組套件、8 個工具、8 個服務、8 個專案、5 個網站前後台、4 個學習/協作面。
- 新增 `apps/api/src/services/ai_agent_deployment_layout.py`,只讀載入最新 committed layout並驗證 `production_routing_allowed``telegram_direct_send_allowed``autonomous_host_mutation_allowed` 等旗標必須維持 false。
- 新增 `GET /api/v1/agents/agent-deployment-layout`,只讀回傳佈建布局;端點不部署 Agent、不呼叫外部模型、不送 Telegram、不碰 DB/Redis、不讀 Secret payload。
- 新增 `docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md`,記錄 OpenClaw / Hermes / NemoTron 分工、Telegram 策略、主動學習與優先順序。
- 更新 `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`,新增 P1-401 完成狀態與 P1-402 / P1-403 / P2-401 / P3-401 下一步。
- 更新 `apps/web/src/lib/api-client.ts`,加入 `getAiAgentDeploymentLayout()``AiAgentDeploymentLayoutSnapshot` 型別,供治理後台後續接 UI 使用。
- 更新 `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 與 i18n將三 Agent 佈建布局接入治理頁自動化盤點只顯示狀態、證據、Agent 主責、Telegram policy 與批准邊界,不顯示工作對話內容。
- 新增 `apps/api/tests/test_ai_agent_deployment_layout.py``apps/api/tests/test_ai_agent_deployment_layout_api.py`
**完成度與邊界:**
- 三 Agent 佈建布局合約:`100%`
- 三 Agent 佈建布局整體:`45%`,因目前已完成 schema / snapshot / API / 測試 / 報告 / 治理頁 UI尚未做 runtime deploy、Telegram E2E 發送或 AgentSession worker。
- NemoTron 仍維持離線評估 / smoke / replay 候選,不得直接進 shadow/canary/production route。
- 所有 Telegram 通知必須走 Gateway / ADR-035本波不直接送 Telegram、不讀 token、不新增 Bot secret。
- 120 host 仍維持 blocked不安排自動修復。
- 前端只能顯示任務狀態、證據摘要、批准邊界與產物連結,不得顯示操作對話原文或 Agent 私有推理。
- 本地驗證JSON parse、API pytest、ruff、web typecheck 通過in-app Browser 對本機 URL 被安全 policy 阻擋,未取得畫面截圖。
## 2026-06-11IwoooS 即時資安危害優先序與 Wave 0 修補
**背景**:使用者調整 IwoooS 推進方向:不再把「完全不影響現有服務」放在所有工作之前,而是優先處理有即時性資訊安全危害的部分;處理過程仍需主動同步其他專案並避免未協調的服務中斷。本階段先確認全域資安範圍,並處理 source-control 層級可立即收斂的 P0。

View File

@@ -11,10 +11,13 @@
| Agent 市場治理 | 72% | 進行中 | `agent_market_governance_snapshot_v1`、API、UI 分頁、每週觀察流程 |
| Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` |
| 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 |
| OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json``GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` |
| 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 |
AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本身完成度:**100%**。
三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。
完成度計算模型:
```text
@@ -54,6 +57,19 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本
| Claude Agent SDK 候選 | DevOps / 程式修復專家 | 離線修復評分、patch plan 批判 | SDK/API 使用、未經 OpenClaw/HITL 的執行 |
| CrewAI / ADK / Microsoft 候選 | 次級或平台候選 | 觀察 / 回放準備度、能力評分表 | 生產執行 |
### 3.1 OpenClaw / Hermes / NemoTron 佈建布局P1-401
| 項目 | 狀態 |
|---|---|
| 佈建布局 schema | 已完成:`docs/schemas/ai_agent_deployment_layout_v1.schema.json` |
| 佈建布局 snapshot | 已完成:`docs/evaluations/ai_agent_deployment_layout_2026-06-11.json` |
| 只讀 API | 已完成:`GET /api/v1/agents/agent-deployment-layout` |
| 細化報告 | 已完成:`docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` |
| 前端治理 UI | 已完成P1-402治理頁自動化盤點 tab 已顯示佈建布局、主責 Agent、Telegram policy、批准/阻擋狀態 |
| Telegram 三 Agent lane E2E | 待辦P1-403必須沿用 Gateway / ADR-035不允許 Agent 直接發送 |
| AgentSession / Redis Streams runtime loop | 待辦P2-401需 migration / worker gate |
| NemoTron 3 Ultra smoke | 待辦P3-401需 source refresh + cost/data approval |
## 4. 工作流總覽
| ID | 工作流 | 目標 | 目前狀態 | 目標狀態 |

View File

@@ -0,0 +1,116 @@
# OpenClaw / Hermes / NemoTron 佈建布局與協作報告
> 日期2026-06-11台北時間
> 狀態只讀布局與治理頁接線完成runtime 佈署尚未授權。
> 權威資料:`docs/evaluations/ai_agent_deployment_layout_2026-06-11.json`
> Schema`docs/schemas/ai_agent_deployment_layout_v1.schema.json`
> API`GET /api/v1/agents/agent-deployment-layout`
## 1. 結論
目前已把 OpenClaw、Hermes、NemoTron 依各自專長安排到主機、套件、工具、服務、專案、網站前後台、學習協作與 Telegram 告警布局中。
本波完成的是「可驗證佈建布局」,不是直接把 Agent 遠端安裝到所有主機;因此不會碰 Secret、不送 Telegram、不改主機、不裝 SDK、不呼叫付費 API、不進 shadow/canary、不改 production route。
## 2. 完成度
| 範圍 | 完成度 | 狀態 |
|---|---:|---|
| 三 Agent 佈建布局合約 | 100% | Schema、snapshot、loader、API、測試已建立 |
| 佈建布局資料覆蓋 | 100% | 42 個 target 已納入 |
| 治理頁只讀顯示 | 100% | 自動化盤點 tab 已接 API 與繁中顯示 |
| Runtime 實際部署 | 0% | 需要逐項 gate / approval / deploy |
| Telegram Gateway 整合布局 | 70% | 既有 Gateway / ADR-035 可承接;本波不直接送訊息 |
| 主動學習與協作布局 | 45% | 已定義 AgentSession / Redis Streams / KM / replay 回饋runtime loop 需後續落地 |
## 3. Agent 分工
| Agent | 專長 | 主要佈建 lane | 可主動做 | 必須交給誰 |
|---|---|---|---|---|
| OpenClaw | 生產仲裁、HITL、執行風險、Telegram action-required | production decision core | 只讀診斷、風險分級、批准包審查、批准後執行仲裁 | Hermes 彙整治理NemoTron 做離線模型評估 |
| Hermes | 治理、知識、文件、套件/供應鏈、降噪、跨專案盤點 | governance / knowledge / reporting | 盤點、報告、runbook、依賴漂移、降噪提案、批准包草稿 | OpenClaw 仲裁生產與 TelegramNemoTron 評估模型能力 |
| NemoTron | 離線專家、模型比較、工具呼叫、NIM / NVIDIA replay | offline evaluator | sanitized smoke、replay 評分、輸出合約檢查、模型能力矩陣 | OpenClaw 仲裁 shadow/canary/productionHermes 彙整來源與報告 |
## 4. 覆蓋範圍
| Domain | Target 數 | 主要 owner |
|---|---:|---|
| 主機 | 6 | OpenClaw |
| 套件與建置 | 3 | Hermes |
| 工具 | 8 | Hermes / OpenClaw |
| 服務 | 8 | OpenClaw / Hermes |
| 專案 | 8 | Hermes / OpenClaw |
| 網站前後台 | 5 | Hermes / OpenClaw |
| 學習與協作 | 4 | OpenClaw / Hermes / NemoTron |
合計42 個 target。
## 5. 主機佈建策略
| 主機 | 角色 | 主 Agent | 狀態 | Telegram |
|---|---|---|---|---|
| 110 | DevOps / runner / monitoring | OpenClaw | active_governed | action_required |
| 188 | AI / Web / DB / Redis / SigNoz | OpenClaw | active_governed | action_required |
| 111 | Ollama fallback | OpenClaw | read_only_layout | failure_only |
| 120 | K3s blocked recovery | OpenClaw | blocked_by_gate | action_required |
| 121 | K3s peer | OpenClaw | active_governed | failure_only |
| 112 | Kali / security evidence | Hermes | read_only_layout | no_direct_notify |
120 仍維持 blocked不安排自動修復必須等 console / SSH / K3s evidence 回來。
## 6. Telegram 策略
所有 Agent 都不得直接持有 token 或直接送訊息;統一經 `apps/api/src/services/telegram_gateway.py` 與 ADR-035 的部署後 E2E 驗證鏈路。
通知等級:
| 類型 | 策略 |
|---|---|
| critical failure | 立即告警 |
| operator action required | 立即告警 |
| approval required | Telegram approval card |
| success / healthy | 壓低或每日摘要 |
| watch-only no-op | 靜默 |
訊息不得包含 Secret、token、cookie、private key、原始 payload、操作對話原文或 Agent 私有推理。
## 7. 主動學習與互相溝通
協作路徑:
1. Prometheus / Sentry / SigNoz / backup / runtime snapshot 產生事件。
2. Hermes 先彙整只讀證據與 runbook / dependency / market context。
3. OpenClaw 仲裁風險、HITL、Telegram action-required 與是否能進 dry-run。
4. NemoTron 只處理 sanitized smoke / replay / model comparison。
5. 結果回寫 AgentSession、Timeline、KM、Playbook trust 與 market scorecard。
學習來源:
- incident timeline
- approval outcome
- alert operation log
- service health snapshot
- backup / DR evidence
- dependency drift snapshot
- agent market watch
- NemoTron smoke / replay result
## 8. 優先順序
| 優先級 | 任務 | 狀態 | 下一步 |
|---|---|---|---|
| P1-401 | 建立三 Agent 佈建布局 schema / snapshot / API | 完成 | 進入 Telegram / 協作 runtime gate |
| P1-402 | 治理後台顯示佈建布局 | 完成 | 已顯示 target、owner、Telegram policy、批准/阻擋狀態 |
| P1-403 | Telegram Gateway 對三 Agent lane 做 E2E smoke 包 | 待辦 | 不直接發送,先做批准包 |
| P2-401 | AgentSession / Redis Streams 協作資料面落地 | 待辦 | 需要 migration / worker gate |
| P2-402 | Playbook trust / KM / operator feedback loop 補強 | 待辦 | 先做只讀報表 |
| P3-401 | NemoTron 3 Ultra source refresh + 5-record smoke | 待辦 | 需要 cost/data approval 後才能跑 |
## 9. 紅線
- 不把布局當成 runtime 授權。
- 不把 AwoooP work item 當成生產批准。
- 不把 IwoooS UI 可見當成安全授權。
- 不讓 NemoTron 在 smoke/replay gate 前進 shadow/canary。
- 不讓 Hermes 直接發 Telegram 或修改生產。
- 不讓任何 Agent 顯示操作對話原文或私有推理到前端。

View File

@@ -0,0 +1,976 @@
{
"schema_version": "ai_agent_deployment_layout_v1",
"generated_at": "2026-06-11T18:20:00+08:00",
"program_status": {
"overall_completion_percent": 45,
"current_priority": "P1",
"current_task_id": "P1-402",
"next_task_id": "P1-403",
"read_only_mode": true,
"deployment_authority": "layout_only_no_runtime_deploy"
},
"agent_contracts": [
{
"agent_id": "openclaw",
"display_name": "OpenClaw",
"primary_specialty": "生產仲裁、風險判斷、HITL 關卡與執行前後驗證",
"deployment_lane": "production_decision_core",
"allowed_autonomy": [
"只讀診斷",
"風險分級",
"批准包審查",
"Telegram action-required 分流",
"批准後的 dry-run / 執行仲裁"
],
"must_delegate_to": [
"Hermes 負責治理、文件、降噪與盤點彙整",
"NemoTron 負責離線模型能力比較與 replay 評分"
],
"blocked_actions": [
"未批准的生產寫入",
"未批准的 destructive operation",
"未批准的 provider route 切換",
"未批准的 Telegram 直接發送",
"未通過 replacement gate 前降級或取代自身生產角色"
],
"learning_scope": [
"incident lifecycle",
"approval outcome",
"post-execution verification",
"playbook trust score",
"alert grouping quality"
]
},
{
"agent_id": "hermes",
"display_name": "Hermes",
"primary_specialty": "治理、知識管理、文件、套件/供應鏈、降噪與跨專案盤點",
"deployment_lane": "governance_knowledge_and_reporting",
"allowed_autonomy": [
"只讀盤點",
"Runbook / KM 草稿",
"市場與依賴漂移摘要",
"告警降噪提案",
"批准包起草"
],
"must_delegate_to": [
"OpenClaw 仲裁任何生產、Telegram、host mutation 或 rollback",
"NemoTron 評估模型/工具能力與 replay 結果"
],
"blocked_actions": [
"直接改生產環境",
"直接發送 Telegram 通知",
"直接修改 Secret",
"自行升級套件或 SDK",
"自行切換 AI provider"
],
"learning_scope": [
"runbook freshness",
"docs drift",
"dependency drift",
"service health evidence gap",
"operator review feedback"
]
},
{
"agent_id": "nemotron",
"display_name": "NemoTron / Nemotron",
"primary_specialty": "離線專家評估、模型工具能力比較、NIM/NVIDIA replay 與長任務 Agent 能力驗證",
"deployment_lane": "offline_evaluator_and_specialist_candidate",
"allowed_autonomy": [
"sanitized request pack 分析",
"5-record smoke 評分",
"50-record replay 結果比較",
"工具呼叫輸出合約檢查",
"候選模型能力矩陣更新"
],
"must_delegate_to": [
"OpenClaw 仲裁生產風險與是否可進 shadow/canary",
"Hermes 彙整市場來源、文件與 operator 報告"
],
"blocked_actions": [
"直接讀取 production secret",
"未批准的 paid API / NIM 呼叫",
"未批准的 SDK 安裝",
"未通過 smoke gate 前進 full replay",
"自行進 shadow/canary 或生產路由"
],
"learning_scope": [
"smoke gate failures",
"output contract completeness",
"latency budget",
"tool calling reliability",
"OpenClaw same-run baseline delta"
]
}
],
"domains": [
{
"domain_id": "hosts",
"display_name": "主機",
"description": "110 / 111 / 112 / 120 / 121 / 188 的只讀監控、診斷、備份與批准後修復佈局。"
},
{
"domain_id": "packages",
"display_name": "套件與建置",
"description": "Python、pnpm/npm、Docker base image、CVE、license、digest 與 drift。"
},
{
"domain_id": "tools",
"display_name": "工具",
"description": "Gitea、Harbor、Prometheus、Alertmanager、SigNoz、Sentry、Open-WebUI、Telegram、Ansible。"
},
{
"domain_id": "services",
"display_name": "服務",
"description": "API、Web、AwoooP、IwoooS、PostgreSQL、Redis、K8s workload 與內部控制面。"
},
{
"domain_id": "projects",
"display_name": "專案",
"description": "AWOOOI 及已納入治理視野的外部/相鄰專案。"
},
{
"domain_id": "websites",
"display_name": "網站前後台",
"description": "公開站、治理後台、告警後台、AwoooP 後台、IwoooS 後台。"
},
{
"domain_id": "learning",
"display_name": "學習與協作",
"description": "AgentSession、KM、Playbook trust、market watch、replay harness。"
}
],
"deployment_targets": [
{
"target_id": "host_110",
"domain_id": "hosts",
"display_name": "110 DevOps / Gitea runner / monitoring host",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["host health diagnosis", "runner health review", "backup freshness review", "approval package"],
"telegram_policy": "action_required",
"learning_inputs": ["Gitea workflow result", "runner evidence", "backup status", "Alertmanager route"],
"communication_channels": ["AwoooP approval", "Telegram failure/action-required", "Prometheus metrics"],
"approval_gate": "host mutation requires human approval",
"evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/evaluations/gitea_workflow_runner_health_2026-06-05.json"],
"next_action": "把 runner / backup / monitoring 狀態納入 Agent 協作節點,但只允許準備修復提案。"
},
{
"target_id": "host_188",
"domain_id": "hosts",
"display_name": "188 AI / Web / PostgreSQL / Redis / SigNoz host",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes", "nemotron"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["AI provider health review", "database/cache health review", "observability review", "NemoTron candidate evidence"],
"telegram_policy": "action_required",
"learning_inputs": ["AI route matrix", "DB backup status", "SigNoz/ClickHouse evidence", "NemoTron smoke result"],
"communication_channels": ["AwoooP approval", "Telegram failure/action-required", "Prometheus metrics"],
"approval_gate": "host mutation, service restart, provider route change require explicit approval",
"evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/evaluations/ai_provider_route_matrix_2026-06-05.json"],
"next_action": "建立 OpenClaw 主仲裁、Hermes 彙整、NemoTron 離線評估的 host-188 協作節點。"
},
{
"target_id": "host_111",
"domain_id": "hosts",
"display_name": "111 Ollama fallback host",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["fallback health observation", "route readiness review"],
"telegram_policy": "failure_only",
"learning_inputs": ["Ollama health", "provider failover evidence"],
"communication_channels": ["Prometheus metrics", "Telegram failure-only"],
"approval_gate": "SSH / service operation requires approval",
"evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/runbooks/RUNBOOK-OLLAMA-FAILOVER.md"],
"next_action": "保持只讀觀察,避免因 fallback 健康誤判而切換 provider。"
},
{
"target_id": "host_120",
"domain_id": "hosts",
"display_name": "120 K3s master / blocked recovery host",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "blocked_by_gate",
"automation_level": "blocked",
"capabilities": ["blocked-state tracking", "recovery checklist", "backup config capture blocker"],
"telegram_policy": "action_required",
"learning_inputs": ["cold-start scorecard", "backup config capture", "host reachability"],
"communication_channels": ["Telegram action-required", "LOGBOOK", "cold-start runbook"],
"approval_gate": "console/SSH recovery evidence required before any automation",
"evidence_refs": ["docs/runbooks/BACKUP-STATUS.md", "docs/runbooks/FULL-STACK-COLD-START-SOP.md"],
"next_action": "維持 blocked不安排自動修復等待 console / SSH 復原證據。"
},
{
"target_id": "host_121",
"domain_id": "hosts",
"display_name": "121 K3s peer host",
"target_type": "host",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["K3s readiness observation", "failover context", "backup/cold-start evidence"],
"telegram_policy": "failure_only",
"learning_inputs": ["K3s node readiness", "host connectivity", "cold-start scorecard"],
"communication_channels": ["Prometheus metrics", "Telegram failure-only"],
"approval_gate": "K3s mutation requires maintenance window approval",
"evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/runbooks/K3S-OPTIMIZATION-RUNBOOK.md"],
"next_action": "將 121 視為 K3s readiness 證據節點,不做未批准操作。"
},
{
"target_id": "host_112",
"domain_id": "hosts",
"display_name": "112 Kali / security evidence host",
"target_type": "host",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["security evidence catalog", "owner response package", "read-only posture projection"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["IwoooS posture projection", "owner response evidence"],
"communication_channels": ["IwoooS read-only handoff", "operator review"],
"approval_gate": "active scan / host update / credentialed scan require independent approval",
"evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json", "infra/ansible/inventory/hosts.yml"],
"next_action": "維持只讀安全證據,不啟用 active scan。"
},
{
"target_id": "pkg_python_api",
"domain_id": "packages",
"display_name": "API Python dependencies",
"target_type": "package_set",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["dependency inventory", "CVE/license drift review", "upgrade approval package"],
"telegram_policy": "daily_summary_only",
"learning_inputs": ["package inventory", "CVE policy", "dependency drift plan"],
"communication_channels": ["governance UI", "daily summary only"],
"approval_gate": "dependency upgrade requires approval package",
"evidence_refs": ["docs/evaluations/package_supply_chain_inventory_2026-06-04.json"],
"next_action": "Hermes 週期性彙整漂移OpenClaw 只仲裁高風險升級。"
},
{
"target_id": "pkg_web_pnpm",
"domain_id": "packages",
"display_name": "Web pnpm/npm dependencies",
"target_type": "package_set",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["package inventory", "lockfile drift review", "upgrade proposal"],
"telegram_policy": "daily_summary_only",
"learning_inputs": ["javascript package inventory", "dependency drift plan"],
"communication_channels": ["governance UI", "daily summary only"],
"approval_gate": "package upgrade / install requires approval",
"evidence_refs": ["docs/evaluations/javascript_package_inventory_2026-06-04.json"],
"next_action": "只產生升級批准包,不自動 npm/pnpm install。"
},
{
"target_id": "docker_build_surface",
"domain_id": "packages",
"display_name": "Docker base image and build surface",
"target_type": "container_image",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["digest drift review", "base image risk review", "build-surface proposal"],
"telegram_policy": "action_required",
"learning_inputs": ["docker build surface inventory", "CVE policy"],
"communication_channels": ["governance UI", "Telegram action-required for critical drift"],
"approval_gate": "image digest/base image change requires review and deploy gate",
"evidence_refs": ["docs/evaluations/docker_build_surface_inventory_2026-06-04.json"],
"next_action": "把 critical image drift 送 OpenClaw 仲裁,其他由 Hermes 月報。"
},
{
"target_id": "gitea_actions",
"domain_id": "tools",
"display_name": "Gitea Actions and host runner",
"target_type": "workflow_tool",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["workflow health matrix", "runner attestation", "notification hygiene"],
"telegram_policy": "action_required",
"learning_inputs": ["workflow results", "runner evidence", "CI failure classes"],
"communication_channels": ["Gitea Actions summary", "Telegram actionable/failure only"],
"approval_gate": "workflow modification requires review",
"evidence_refs": ["docs/evaluations/gitea_workflow_runner_health_2026-06-05.json", ".gitea/workflows/"],
"next_action": "Hermes 彙整 runner / workflow gapsOpenClaw 只仲裁部署或 workflow 修改。"
},
{
"target_id": "harbor_registry",
"domain_id": "tools",
"display_name": "Harbor registry",
"target_type": "registry",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["backup freshness", "image inventory", "registry health summary"],
"telegram_policy": "failure_only",
"learning_inputs": ["backup target inventory", "registry backup evidence"],
"communication_channels": ["backup policy", "Telegram failure-only"],
"approval_gate": "registry cleanup / retention change requires approval",
"evidence_refs": ["scripts/backup/backup-harbor.sh", "docs/evaluations/backup_dr_target_inventory_2026-06-04.json"],
"next_action": "只監控 backup / freshness不自動刪 image。"
},
{
"target_id": "prometheus_alertmanager",
"domain_id": "tools",
"display_name": "Prometheus / Alertmanager",
"target_type": "observability",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["alert contract review", "noise reduction proposal", "E2E chain evidence"],
"telegram_policy": "action_required",
"learning_inputs": ["alert rules", "alert chain metrics", "notification outcome"],
"communication_channels": ["Alertmanager webhook", "Telegram action-required"],
"approval_gate": "alert rule deploy / silence requires approval",
"evidence_refs": ["docs/evaluations/observability_contract_matrix_2026-06-05.json", "docs/adr/ADR-035-telegram-alert-chain-enforcement.md"],
"next_action": "保持告警必到,成功訊息降噪,規則部署另走 deploy-alerts gate。"
},
{
"target_id": "signoz_clickhouse",
"domain_id": "tools",
"display_name": "SigNoz / ClickHouse",
"target_type": "observability",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["trace evidence", "log evidence", "storage health review"],
"telegram_policy": "action_required",
"learning_inputs": ["SigNoz alert", "ClickHouse health", "trace/log evidence"],
"communication_channels": ["governance UI", "Telegram action-required"],
"approval_gate": "query-heavy or retention change requires approval",
"evidence_refs": ["docs/evaluations/observability_contract_matrix_2026-06-05.json", "ops/signoz"],
"next_action": "Hermes 做證據摘要OpenClaw 仲裁重大 storage / retention 風險。"
},
{
"target_id": "sentry",
"domain_id": "tools",
"display_name": "Sentry",
"target_type": "observability",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["issue summary", "frontend/backend error drift", "release evidence"],
"telegram_policy": "action_required",
"learning_inputs": ["Sentry issue class", "release marker", "frontend/backend route"],
"communication_channels": ["governance UI", "Telegram action-required"],
"approval_gate": "Sentry DSN / project setting change requires approval",
"evidence_refs": ["apps/web/src/instrumentation.ts", "scripts/backup/backup-sentry.sh"],
"next_action": "只讀錯誤分類,不讀 secret不直接建立/修改 Sentry 設定。"
},
{
"target_id": "open_webui",
"domain_id": "tools",
"display_name": "Open-WebUI / AI workspace",
"target_type": "ai_tool",
"primary_agent": "nemotron",
"supporting_agents": ["hermes", "openclaw"],
"deployment_state": "candidate_only",
"automation_level": "observe_only",
"capabilities": ["model evaluation evidence", "AI artifact inventory", "offline specialist review"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["AI artifact backup", "model evaluation", "operator review"],
"communication_channels": ["offline report", "governance UI"],
"approval_gate": "external model call / NIM route requires cost and data-boundary approval",
"evidence_refs": ["scripts/backup/backup-open-webui.sh", "docs/evaluations/agent_nemotron_contract_tuned_smoke_matrix_2026-06-02.json"],
"next_action": "NemoTron 只做離線評估,不接 production route。"
},
{
"target_id": "telegram_gateway",
"domain_id": "tools",
"display_name": "Telegram Gateway / Bot alert chain",
"target_type": "notification_gateway",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "hitl_execute_after_approval",
"capabilities": ["failure-only routing", "approval card", "dedup", "E2E validation"],
"telegram_policy": "approval_required",
"learning_inputs": ["notification outcome", "dedup result", "operator button action"],
"communication_channels": ["Telegram Bot", "AwoooP approval", "alert operation log"],
"approval_gate": "direct send / bot token change / chat target change requires approval and E2E smoke",
"evidence_refs": ["apps/api/src/services/telegram_gateway.py", "docs/adr/ADR-035-telegram-alert-chain-enforcement.md"],
"next_action": "把三 Agent 的通知都收斂到 Gateway不讓 Agent 直接持有 token 或直接發送。"
},
{
"target_id": "ansible_control",
"domain_id": "tools",
"display_name": "Ansible host control plane",
"target_type": "host_iac",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "read_only_layout",
"automation_level": "hitl_execute_after_approval",
"capabilities": ["check-mode proposal", "host-state drift", "rollback plan"],
"telegram_policy": "approval_required",
"learning_inputs": ["Ansible check-mode", "host textfile exporter", "operator approval outcome"],
"communication_channels": ["AwoooP approval", "Telegram approval-required"],
"approval_gate": "any ansible apply requires independent human approval",
"evidence_refs": ["docs/runbooks/ANSIBLE-OPERATING-MODEL.md", "infra/ansible/inventory/hosts.yml"],
"next_action": "先建立 check-mode 證據apply 仍必須人工批准。"
},
{
"target_id": "awoooi_api",
"domain_id": "services",
"display_name": "AWOOOI API backend",
"target_type": "api",
"primary_agent": "openclaw",
"supporting_agents": ["hermes", "nemotron"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["incident decision", "MCP context", "agent API snapshots", "post-execution verification"],
"telegram_policy": "failure_only",
"learning_inputs": ["incident", "timeline event", "approval record", "agent snapshot"],
"communication_channels": ["API", "Redis stream", "Telegram failure-only"],
"approval_gate": "runtime deploy / DB migration / provider route change requires CD gate",
"evidence_refs": ["apps/api/src/api/v1/agents.py", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"],
"next_action": "把新佈建布局暴露為只讀 API不新增執行端點。"
},
{
"target_id": "awoooi_web",
"domain_id": "services",
"display_name": "AWOOOI public web frontend",
"target_type": "web",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["frontend evidence display", "i18n review", "route health summary"],
"telegram_policy": "failure_only",
"learning_inputs": ["browser smoke", "Sentry issue", "i18n validation"],
"communication_channels": ["governance UI", "Sentry", "Telegram failure-only"],
"approval_gate": "frontend deploy requires CD validation and production smoke",
"evidence_refs": ["apps/web/src/app/[locale]/governance/page.tsx", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"],
"next_action": "Hermes 維護 UI/文案與證據卡OpenClaw 仲裁發布風險。"
},
{
"target_id": "governance_backoffice",
"domain_id": "services",
"display_name": "Governance backoffice",
"target_type": "backoffice",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["automation inventory", "agent market", "service health", "deployment layout display"],
"telegram_policy": "action_required",
"learning_inputs": ["snapshot freshness", "operator review", "blocked gate count"],
"communication_channels": ["governance UI", "AwoooP work item"],
"approval_gate": "UI display is not runtime authorization",
"evidence_refs": ["apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx"],
"next_action": "將本 layout 先接進只讀 API再接治理 UI。"
},
{
"target_id": "awooop_control_plane",
"domain_id": "services",
"display_name": "AwoooP control plane",
"target_type": "control_plane",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "hitl_execute_after_approval",
"capabilities": ["work item", "approval", "truth chain", "run state"],
"telegram_policy": "approval_required",
"learning_inputs": ["approval decision", "run state", "operator timeline"],
"communication_channels": ["AwoooP approvals", "Telegram approval card"],
"approval_gate": "AwoooP work item is not security or production approval by itself",
"evidence_refs": ["apps/web/src/app/[locale]/awooop/page.tsx", "docs/awooop/MASTER-WORKPLAN.md"],
"next_action": "OpenClaw 只把 AwoooP 當批准/證據控制面,不讓候選 Agent 直接執行。"
},
{
"target_id": "iwooos_security_surface",
"domain_id": "services",
"display_name": "IwoooS security surface",
"target_type": "security_surface",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["read-only evidence", "owner response packet", "security posture"],
"telegram_policy": "action_required",
"learning_inputs": ["owner response gate", "redacted evidence refs", "scope handoff"],
"communication_channels": ["IwoooS page", "operator review", "Telegram action-required only"],
"approval_gate": "active runtime / scan / host update remains false until independent approval",
"evidence_refs": ["apps/web/src/app/[locale]/iwooos/page.tsx", "docs/security/iwooos-posture-projection.snapshot.json"],
"next_action": "Hermes 整理資安證據OpenClaw 守住 active runtime gate。"
},
{
"target_id": "postgresql_primary",
"domain_id": "services",
"display_name": "PostgreSQL primary data layer",
"target_type": "database",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["connection health", "backup freshness", "migration risk", "slow query evidence"],
"telegram_policy": "failure_only",
"learning_inputs": ["DB alert", "backup status", "migration outcome"],
"communication_channels": ["Prometheus", "backup status", "Telegram failure-only"],
"approval_gate": "migration / restore / schema change requires approval and backup evidence",
"evidence_refs": ["apps/api/migrations/", "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json"],
"next_action": "OpenClaw 仲裁 DB 風險Hermes 產出備份與 migration 證據摘要。"
},
{
"target_id": "redis_cache",
"domain_id": "services",
"display_name": "Redis / Stream / cache layer",
"target_type": "cache",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["stream backlog review", "cache health", "agent bus readiness"],
"telegram_policy": "failure_only",
"learning_inputs": ["Redis alert", "agent stream backlog", "approval outcome"],
"communication_channels": ["Prometheus", "Agent stream", "Telegram failure-only"],
"approval_gate": "flush / restart / data mutation requires approval",
"evidence_refs": ["docs/evaluations/service_health_gap_matrix_2026-06-05.json", "docs/adr/ADR-082-multi-agent-collaboration.md"],
"next_action": "用 Redis stream 作為 Agent 協作匯流,但不允許無批准清除資料。"
},
{
"target_id": "k8s_workloads",
"domain_id": "services",
"display_name": "K8s workloads / manifests",
"target_type": "k8s",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "dry_run_only",
"capabilities": ["manifest mapping", "rollout evidence", "drift interpretation", "dry-run proposal"],
"telegram_policy": "action_required",
"learning_inputs": ["runtime surface inventory", "drift report", "post-execution verifier"],
"communication_channels": ["AwoooP approval", "Telegram action-required", "Prometheus"],
"approval_gate": "kubectl apply / rollout / scale / delete requires approval",
"evidence_refs": ["k8s/awoooi-prod/", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"],
"next_action": "OpenClaw 做乾跑仲裁Hermes 彙整 manifest 與 runbook。"
},
{
"target_id": "project_awoooi",
"domain_id": "projects",
"display_name": "AWOOOI core project",
"target_type": "project",
"primary_agent": "openclaw",
"supporting_agents": ["hermes", "nemotron"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["incident governance", "agent market governance", "runtime truth", "deployment evidence"],
"telegram_policy": "action_required",
"learning_inputs": ["LOGBOOK", "HARD_RULES", "market watch", "runtime smoke"],
"communication_channels": ["Gitea", "AwoooP", "Telegram", "governance UI"],
"approval_gate": "production deploy and provider changes require existing CD/HITL gates",
"evidence_refs": ["docs/HARD_RULES.md", "docs/LOGBOOK.md"],
"next_action": "把三 Agent 佈局做成 AWOOOI 的正式只讀控制面資料。"
},
{
"target_id": "project_awooop",
"domain_id": "projects",
"display_name": "AwoooP operations project surface",
"target_type": "project",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "hitl_execute_after_approval",
"capabilities": ["work item", "approval queue", "run monitor", "contract governance"],
"telegram_policy": "approval_required",
"learning_inputs": ["work item status", "approval outcome", "truth chain"],
"communication_channels": ["AwoooP UI", "Telegram approval card"],
"approval_gate": "AwoooP approval is necessary but not sufficient for security or host mutation",
"evidence_refs": ["docs/awooop/MASTER-WORKPLAN.md", "apps/web/src/app/[locale]/awooop/"],
"next_action": "將 Agent 佈局轉為 AwoooP work item templates但不自動簽核。"
},
{
"target_id": "project_iwooos",
"domain_id": "projects",
"display_name": "IwoooS security governance",
"target_type": "project",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["scope handoff", "owner response gate", "redacted evidence"],
"telegram_policy": "action_required",
"learning_inputs": ["IwoooS posture projection", "owner response state"],
"communication_channels": ["IwoooS UI", "operator review"],
"approval_gate": "UI-visible state must not be treated as runtime authorization",
"evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"],
"next_action": "Hermes 維持只讀證據卡OpenClaw 防止 runtime gate 被誤開。"
},
{
"target_id": "project_vibework",
"domain_id": "projects",
"display_name": "VibeWork adjacent product",
"target_type": "adjacent_project",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["boundary inventory", "release evidence summary", "alert routing expectation"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["cross-project handoff", "runtime evidence if explicitly supplied"],
"communication_channels": ["operator report only"],
"approval_gate": "separate repo/runtime approval required",
"evidence_refs": ["docs/security/vibework-iwooos-onboarding-handoff.snapshot.json"],
"next_action": "只保留治理視野,不跨 repo 自動部署。"
},
{
"target_id": "project_stockplatform",
"domain_id": "projects",
"display_name": "StockPlatform adjacent product",
"target_type": "adjacent_project",
"primary_agent": "hermes",
"supporting_agents": ["nemotron", "openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["AI research governance", "dry-run review", "human-review boundary"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["research governance note", "operator-approved evidence"],
"communication_channels": ["operator report only"],
"approval_gate": "separate project approval required before any runtime work",
"evidence_refs": ["docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md"],
"next_action": "NemoTron 只可作研究乾跑,不作投資建議或生產推薦。"
},
{
"target_id": "project_tsenyang",
"domain_id": "projects",
"display_name": "TSENYANG website adjacent project",
"target_type": "adjacent_project",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["SEO/launch evidence summary", "analytics boundary review"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["operator-approved launch evidence"],
"communication_channels": ["operator report only"],
"approval_gate": "separate repo/runtime approval required",
"evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"],
"next_action": "只保留跨產品治理視角,不直接改網站。"
},
{
"target_id": "project_bitan",
"domain_id": "projects",
"display_name": "Bitan Pharmacy adjacent project",
"target_type": "adjacent_project",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["production recovery evidence summary", "AI ops loop review"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["operator-approved recovery evidence"],
"communication_channels": ["operator report only"],
"approval_gate": "separate repo/runtime approval required",
"evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"],
"next_action": "只列入相鄰產品治理,不跨專案部署。"
},
{
"target_id": "project_agent_bounty",
"domain_id": "projects",
"display_name": "agent-bounty-protocol onboarding surface",
"target_type": "adjacent_project",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "read_only_layout",
"automation_level": "observe_only",
"capabilities": ["read-only security onboarding", "owner gate", "external agent boundary"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["agent bounty onboarding handoff", "owner response status"],
"communication_channels": ["IwoooS read-only handoff", "operator report"],
"approval_gate": "no runtime, repo, scan, cron, payout or contract action without explicit approval",
"evidence_refs": ["docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json"],
"next_action": "維持 IwoooS 只讀收件,不跨到 agent-bounty runtime。"
},
{
"target_id": "public_frontend",
"domain_id": "websites",
"display_name": "Public frontend routes",
"target_type": "website_frontend",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["browser smoke evidence", "i18n coverage", "route health"],
"telegram_policy": "failure_only",
"learning_inputs": ["browser smoke", "Sentry error", "route uptime"],
"communication_channels": ["Sentry", "Prometheus", "Telegram failure-only"],
"approval_gate": "frontend deploy requires build/typecheck/browser smoke",
"evidence_refs": ["apps/web/src/app/[locale]/page.tsx"],
"next_action": "Hermes 負責內容/顯示品質OpenClaw 仲裁發布風險。"
},
{
"target_id": "governance_ui",
"domain_id": "websites",
"display_name": "Governance UI backoffice",
"target_type": "website_backoffice",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["agent market display", "automation inventory display", "deployment layout display"],
"telegram_policy": "action_required",
"learning_inputs": ["operator review", "snapshot freshness", "blocked gate count"],
"communication_channels": ["governance UI", "AwoooP work item"],
"approval_gate": "UI display does not authorize runtime action",
"evidence_refs": ["apps/web/src/app/[locale]/governance/page.tsx"],
"next_action": "新增只讀 layout 資料源後再接 UI 呈現。"
},
{
"target_id": "alerts_backoffice",
"domain_id": "websites",
"display_name": "Alerts and operation-log backoffice",
"target_type": "website_backoffice",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "prepare_only",
"capabilities": ["incident triage", "alert operation log", "operator action request"],
"telegram_policy": "action_required",
"learning_inputs": ["alert operation log", "incident timeline", "notification outcome"],
"communication_channels": ["alert-operation-logs UI", "Telegram action-required"],
"approval_gate": "alert action execution requires approval",
"evidence_refs": ["apps/web/src/app/[locale]/alerts/page.tsx", "apps/web/src/app/[locale]/alert-operation-logs/page.tsx"],
"next_action": "OpenClaw 產生 action proposalHermes 彙整趨勢與降噪。"
},
{
"target_id": "awooop_admin",
"domain_id": "websites",
"display_name": "AwoooP admin routes",
"target_type": "website_backoffice",
"primary_agent": "openclaw",
"supporting_agents": ["hermes"],
"deployment_state": "active_governed",
"automation_level": "hitl_execute_after_approval",
"capabilities": ["approval queue", "runs", "contracts", "tenants"],
"telegram_policy": "approval_required",
"learning_inputs": ["approval decision", "run evidence", "contract state"],
"communication_channels": ["AwoooP UI", "Telegram approval card"],
"approval_gate": "AwoooP UI action requires separate policy and human approval",
"evidence_refs": ["apps/web/src/app/[locale]/awooop/"],
"next_action": "將三 Agent 提案映射成 AwoooP work item不讓候選 Agent 直接執行。"
},
{
"target_id": "iwooos_admin",
"domain_id": "websites",
"display_name": "IwoooS read-only security routes",
"target_type": "website_backoffice",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["security mirror", "scope card", "owner response gate"],
"telegram_policy": "action_required",
"learning_inputs": ["owner response", "security posture", "redacted refs"],
"communication_channels": ["IwoooS UI", "operator review"],
"approval_gate": "IwoooS visibility is not active runtime authorization",
"evidence_refs": ["apps/web/src/app/[locale]/iwooos/page.tsx"],
"next_action": "繼續維持低摩擦只讀資安治理。"
},
{
"target_id": "agent_session_bus",
"domain_id": "learning",
"display_name": "AgentSession / Redis Streams collaboration bus",
"target_type": "learning_bus",
"primary_agent": "openclaw",
"supporting_agents": ["hermes", "nemotron"],
"deployment_state": "planned",
"automation_level": "observe_only",
"capabilities": ["agent handoff", "turn audit", "replayable collaboration"],
"telegram_policy": "no_direct_notify",
"learning_inputs": ["agent session turn", "critic challenge", "coordinator decision"],
"communication_channels": ["Redis stream", "agent_sessions audit"],
"approval_gate": "new runtime worker or schema migration requires approval",
"evidence_refs": ["docs/adr/ADR-082-multi-agent-collaboration.md"],
"next_action": "先補只讀 layout下一波才評估是否需要 migration / worker。"
},
{
"target_id": "km_playbook_learning",
"domain_id": "learning",
"display_name": "KM / Playbook trust learning loop",
"target_type": "learning_loop",
"primary_agent": "hermes",
"supporting_agents": ["openclaw"],
"deployment_state": "planned",
"automation_level": "observe_only",
"capabilities": ["runbook freshness", "playbook trust evidence", "negative reinforcement"],
"telegram_policy": "daily_summary_only",
"learning_inputs": ["execution result", "post verification", "operator review"],
"communication_channels": ["KM", "LOGBOOK", "daily summary only"],
"approval_gate": "playbook promotion requires review",
"evidence_refs": ["docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md"],
"next_action": "Hermes 整理學習素材OpenClaw 仲裁是否可升為 approved playbook。"
},
{
"target_id": "nemotron_replay_pipeline",
"domain_id": "learning",
"display_name": "NemoTron smoke / replay pipeline",
"target_type": "model_replay",
"primary_agent": "nemotron",
"supporting_agents": ["openclaw", "hermes"],
"deployment_state": "blocked_by_gate",
"automation_level": "blocked",
"capabilities": ["5-record smoke", "output contract review", "latency budget", "same-run baseline delta"],
"telegram_policy": "approval_required",
"learning_inputs": ["smoke gate", "external runner report", "OpenClaw baseline"],
"communication_channels": ["offline report", "operator review", "Telegram approval-required after gate"],
"approval_gate": "refresh source evidence, cost/data approval, then 5-record smoke only",
"evidence_refs": ["docs/evaluations/agent_nemotron_contract_tuned_smoke_matrix_2026-06-02.json", "docs/runbooks/OPENCLAW-REPLACEMENT-EVALUATION.md"],
"next_action": "新增 Nemotron 3 Ultra source evidence 後,先重跑 5-record smoke。"
},
{
"target_id": "agent_market_watch",
"domain_id": "learning",
"display_name": "AI Agent market watch",
"target_type": "market_watch",
"primary_agent": "hermes",
"supporting_agents": ["openclaw", "nemotron"],
"deployment_state": "active_governed",
"automation_level": "observe_only",
"capabilities": ["primary-source scan", "integration review", "discovery intake", "candidate scoring"],
"telegram_policy": "action_required",
"learning_inputs": ["official docs", "release metadata", "scorecard", "operator review"],
"communication_channels": ["Gitea Actions", "governance UI", "Telegram actionable only"],
"approval_gate": "market watch cannot approve SDK/API/replay/shadow/canary/production",
"evidence_refs": [".gitea/workflows/agent-market-watch.yaml", "docs/evaluations/agent_market_governance_snapshot_2026-06-04.json"],
"next_action": "把 Nemotron 3 Ultra 納入下一次 source refresh 和 scorecard review。"
}
],
"collaboration_contract": {
"message_bus": "以 Redis Streams / AgentSession / timeline event 作為可回放協作總線;本快照只描述布局,不啟動新 worker。",
"audit_trail": "所有 Agent 建議必須落到可查詢事件、批准包或 committed snapshot不得只存在聊天視窗。",
"handoff_rules": [
"OpenClaw 收斂生產風險、HITL 與 Telegram action-required。",
"Hermes 收斂治理、文件、盤點、降噪與跨專案報告。",
"NemoTron 收斂 sanitized 離線評估、模型比較與 replay score。",
"任何 Agent 發現風險升級時必須轉交 OpenClaw 仲裁。",
"任何 Agent 發現市場/依賴漂移時必須轉交 Hermes 彙整。",
"任何候選模型能力聲稱必須先經 NemoTron 或 replay harness 產出同題證據。"
],
"frontend_redaction": {
"operator_conversation_display_allowed": false,
"agent_private_reasoning_display_allowed": false,
"display_policy": "前端只能顯示任務狀態、證據摘要、批准邊界與產物連結;不得顯示操作對話原文或 Agent 私有推理。"
}
},
"learning_contract": {
"event_sources": [
"incident timeline",
"approval outcome",
"alert operation log",
"service health snapshot",
"backup / DR evidence",
"dependency drift snapshot",
"agent market watch",
"NemoTron smoke / replay result"
],
"feedback_loops": [
"成功/失敗/中性結果回寫 Playbook trust",
"Critic / Reviewer 挑戰結果回寫 AgentSession",
"Telegram 按鈕與 operator review 回寫 notification outcome",
"Market watch source delta 進入 scorecard / replay gate",
"NemoTron replay 失敗模式回寫 prompt / contract 改善清單"
],
"growth_metrics": [
"playbook trust_score 有效更新數",
"general fallback alert ratio",
"agent handoff completion rate",
"notification failure rate",
"smoke gate pass rate",
"operator-approved proposal precision"
],
"retention_policy": "學習資料只保留 redacted evidence refs、hash、分類與結果Secret、token、原始對話與私有推理不得進前端或告警。"
},
"telegram_contract": {
"primary_gateway": "apps/api/src/services/telegram_gateway.py",
"bot_roles": [
"OpenClaw Bot生產告警、批准、HITL 與 action-required。",
"Hermes Bot lane治理摘要、週期性報告與降噪候選但仍經 Gateway。",
"NemoTron lane只在 smoke/replay gate 需要 operator review 時產生摘要,不能直接發送。"
],
"notification_classes": [
"critical failure: immediate",
"operator action required: immediate",
"approval required: approval card",
"success / healthy: suppressed or daily summary",
"watch-only no-op: quiet"
],
"redaction_policy": "不得在 Telegram 顯示 Secret、token、cookie、private key、原始 payload、操作對話原文或 Agent 私有推理;只顯示 evidence ref、狀態、影響範圍與下一步。",
"e2e_validation": "沿用 ADR-035部署前檢查 Secret、部署時注入 K8s Secret、部署後做 E2E 告警鏈路驗證;本布局不直接送測試通知。"
},
"rollups": {
"total_targets": 42,
"by_domain": {
"hosts": 6,
"packages": 3,
"tools": 8,
"services": 8,
"projects": 8,
"websites": 5,
"learning": 4
},
"by_primary_agent": {
"openclaw": 17,
"hermes": 23,
"nemotron": 2
},
"by_deployment_state": {
"active_governed": 28,
"read_only_layout": 9,
"blocked_by_gate": 2,
"planned": 2,
"candidate_only": 1
},
"by_telegram_policy": {
"failure_only": 8,
"action_required": 17,
"approval_required": 6,
"daily_summary_only": 3,
"no_direct_notify": 8
},
"blocked_target_ids": [
"host_120",
"nemotron_replay_pipeline"
],
"approval_required_target_ids": [
"host_120",
"telegram_gateway",
"ansible_control",
"awooop_control_plane",
"project_awooop",
"awooop_admin",
"nemotron_replay_pipeline"
]
},
"approval_boundaries": {
"sdk_installation_allowed": false,
"paid_api_call_allowed": false,
"shadow_or_canary_allowed": false,
"production_routing_allowed": false,
"destructive_operation_allowed": false,
"secret_plaintext_allowed": false,
"autonomous_host_mutation_allowed": false,
"telegram_direct_send_allowed": false
}
}

View File

@@ -0,0 +1,343 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:awoooi:ai-agent-deployment-layout-v1",
"title": "AWOOOI AI Agent 佈建布局快照 v1",
"description": "OpenClaw、Hermes、NemoTron 在主機、套件、工具、服務、專案與網站前後台的只讀佈建布局合約。此 schema 不授權任何生產寫入、Telegram 發送、SDK 安裝、付費 API、shadow/canary 或生產路由切換。",
"type": "object",
"required": [
"schema_version",
"generated_at",
"program_status",
"agent_contracts",
"domains",
"deployment_targets",
"collaboration_contract",
"learning_contract",
"telegram_contract",
"rollups",
"approval_boundaries"
],
"properties": {
"schema_version": {
"type": "string",
"const": "ai_agent_deployment_layout_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",
"deployment_authority"
],
"properties": {
"overall_completion_percent": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"current_priority": {
"type": "string",
"enum": ["P0", "P1", "P2", "P3"]
},
"current_task_id": {
"type": "string",
"minLength": 1
},
"next_task_id": {
"type": "string",
"minLength": 1
},
"read_only_mode": {
"type": "boolean",
"const": true
},
"deployment_authority": {
"type": "string",
"const": "layout_only_no_runtime_deploy"
}
},
"additionalProperties": false
},
"agent_contracts": {
"type": "array",
"minItems": 3,
"items": {
"type": "object",
"required": [
"agent_id",
"display_name",
"primary_specialty",
"deployment_lane",
"allowed_autonomy",
"must_delegate_to",
"blocked_actions",
"learning_scope"
],
"properties": {
"agent_id": {"type": "string", "minLength": 1},
"display_name": {"type": "string", "minLength": 1},
"primary_specialty": {"type": "string", "minLength": 1},
"deployment_lane": {"type": "string", "minLength": 1},
"allowed_autonomy": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"must_delegate_to": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"blocked_actions": {
"type": "array",
"minItems": 1,
"items": {"type": "string", "minLength": 1}
},
"learning_scope": {
"type": "array",
"items": {"type": "string", "minLength": 1}
}
},
"additionalProperties": false
}
},
"domains": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["domain_id", "display_name", "description"],
"properties": {
"domain_id": {"type": "string", "minLength": 1},
"display_name": {"type": "string", "minLength": 1},
"description": {"type": "string", "minLength": 1}
},
"additionalProperties": false
}
},
"deployment_targets": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"target_id",
"domain_id",
"display_name",
"target_type",
"primary_agent",
"supporting_agents",
"deployment_state",
"automation_level",
"capabilities",
"telegram_policy",
"learning_inputs",
"communication_channels",
"approval_gate",
"evidence_refs",
"next_action"
],
"properties": {
"target_id": {"type": "string", "minLength": 1},
"domain_id": {"type": "string", "minLength": 1},
"display_name": {"type": "string", "minLength": 1},
"target_type": {"type": "string", "minLength": 1},
"primary_agent": {"type": "string", "minLength": 1},
"supporting_agents": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"deployment_state": {
"type": "string",
"enum": [
"active_governed",
"read_only_layout",
"blocked_by_gate",
"planned",
"candidate_only"
]
},
"automation_level": {
"type": "string",
"enum": [
"observe_only",
"prepare_only",
"dry_run_only",
"hitl_execute_after_approval",
"blocked"
]
},
"capabilities": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"telegram_policy": {
"type": "string",
"enum": [
"failure_only",
"action_required",
"approval_required",
"daily_summary_only",
"no_direct_notify"
]
},
"learning_inputs": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"communication_channels": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"approval_gate": {"type": "string", "minLength": 1},
"evidence_refs": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"next_action": {"type": "string", "minLength": 1}
},
"additionalProperties": false
}
},
"collaboration_contract": {
"type": "object",
"required": [
"message_bus",
"audit_trail",
"handoff_rules",
"frontend_redaction"
],
"properties": {
"message_bus": {"type": "string", "minLength": 1},
"audit_trail": {"type": "string", "minLength": 1},
"handoff_rules": {
"type": "array",
"minItems": 1,
"items": {"type": "string", "minLength": 1}
},
"frontend_redaction": {
"type": "object",
"required": [
"operator_conversation_display_allowed",
"agent_private_reasoning_display_allowed",
"display_policy"
],
"properties": {
"operator_conversation_display_allowed": {"type": "boolean", "const": false},
"agent_private_reasoning_display_allowed": {"type": "boolean", "const": false},
"display_policy": {"type": "string", "minLength": 1}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"learning_contract": {
"type": "object",
"required": [
"event_sources",
"feedback_loops",
"growth_metrics",
"retention_policy"
],
"properties": {
"event_sources": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"feedback_loops": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"growth_metrics": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"retention_policy": {"type": "string", "minLength": 1}
},
"additionalProperties": false
},
"telegram_contract": {
"type": "object",
"required": [
"primary_gateway",
"bot_roles",
"notification_classes",
"redaction_policy",
"e2e_validation"
],
"properties": {
"primary_gateway": {"type": "string", "minLength": 1},
"bot_roles": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"notification_classes": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"redaction_policy": {"type": "string", "minLength": 1},
"e2e_validation": {"type": "string", "minLength": 1}
},
"additionalProperties": false
},
"rollups": {
"type": "object",
"required": [
"total_targets",
"by_domain",
"by_primary_agent",
"by_deployment_state",
"by_telegram_policy",
"blocked_target_ids",
"approval_required_target_ids"
],
"properties": {
"total_targets": {"type": "integer", "minimum": 1},
"by_domain": {"type": "object", "additionalProperties": {"type": "integer"}},
"by_primary_agent": {"type": "object", "additionalProperties": {"type": "integer"}},
"by_deployment_state": {"type": "object", "additionalProperties": {"type": "integer"}},
"by_telegram_policy": {"type": "object", "additionalProperties": {"type": "integer"}},
"blocked_target_ids": {
"type": "array",
"items": {"type": "string", "minLength": 1}
},
"approval_required_target_ids": {
"type": "array",
"items": {"type": "string", "minLength": 1}
}
},
"additionalProperties": false
},
"approval_boundaries": {
"type": "object",
"required": [
"sdk_installation_allowed",
"paid_api_call_allowed",
"shadow_or_canary_allowed",
"production_routing_allowed",
"destructive_operation_allowed",
"secret_plaintext_allowed",
"autonomous_host_mutation_allowed",
"telegram_direct_send_allowed"
],
"properties": {
"sdk_installation_allowed": {"type": "boolean", "const": false},
"paid_api_call_allowed": {"type": "boolean", "const": false},
"shadow_or_canary_allowed": {"type": "boolean", "const": false},
"production_routing_allowed": {"type": "boolean", "const": false},
"destructive_operation_allowed": {"type": "boolean", "const": false},
"secret_plaintext_allowed": {"type": "boolean", "const": false},
"autonomous_host_mutation_allowed": {"type": "boolean", "const": false},
"telegram_direct_send_allowed": {"type": "boolean", "const": false}
},
"additionalProperties": false
}
},
"additionalProperties": false
}