feat(governance): 新增 Agent host stateful 版本盤點
This commit is contained in:
@@ -58,6 +58,9 @@ from src.services.ai_agent_deployment_layout import (
|
||||
from src.services.ai_agent_gitea_pr_draft_lane import (
|
||||
load_latest_ai_agent_gitea_pr_draft_lane,
|
||||
)
|
||||
from src.services.ai_agent_host_stateful_version_inventory import (
|
||||
load_latest_ai_agent_host_stateful_version_inventory,
|
||||
)
|
||||
from src.services.ai_agent_proactive_operations_contract import (
|
||||
load_latest_ai_agent_proactive_operations_contract,
|
||||
)
|
||||
@@ -709,6 +712,35 @@ async def get_agent_gitea_pr_draft_lane() -> dict[str, Any]:
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/agent-host-stateful-version-inventory",
|
||||
response_model=dict[str, Any],
|
||||
summary="取得 AI Agent host / K3s / stateful 版本只讀盤點",
|
||||
description=(
|
||||
"讀取最新已提交的 AI Agent host OS / K3s / stateful services 版本只讀盤點與 "
|
||||
"maintenance window 批准包;此端點不 SSH、不執行 host command、不執行 kubectl、"
|
||||
"不 apt upgrade、不升級 kernel/K3s、不 drain node、不 reboot、不 restart stateful service、"
|
||||
"不做 DB migration、不刪備份、不 restore、不 pull image、不安裝套件、不查外部版本來源、"
|
||||
"不 active scan、不發 Telegram、不讀取 secret、不回傳工作視窗對話內容。"
|
||||
),
|
||||
)
|
||||
async def get_agent_host_stateful_version_inventory() -> dict[str, Any]:
|
||||
"""Return the latest read-only host / K3s / stateful version inventory."""
|
||||
try:
|
||||
return await asyncio.to_thread(load_latest_ai_agent_host_stateful_version_inventory)
|
||||
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_host_stateful_version_inventory_invalid", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="AI Agent host / K3s / stateful 版本只讀盤點無效",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/runtime-surface-inventory",
|
||||
response_model=dict[str, Any],
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
AI Agent host and stateful version inventory snapshot.
|
||||
|
||||
Loads the latest committed, read-only host OS, K3s, and stateful services
|
||||
inventory contract. This module never runs SSH, kubectl, package upgrades,
|
||||
node drains, reboots, stateful restarts, live scans, Telegram sends, or exposes
|
||||
work-window transcripts.
|
||||
"""
|
||||
|
||||
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_host_stateful_version_inventory_*.json"
|
||||
_SCHEMA_VERSION = "ai_agent_host_stateful_version_inventory_v1"
|
||||
_RUNTIME_AUTHORITY = "host_stateful_readonly_inventory_no_upgrade_or_restart"
|
||||
_TRANSCRIPT_MARKERS = {
|
||||
"# In app browser",
|
||||
"My request for Codex",
|
||||
"Current URL:",
|
||||
"AGENTS.md instructions",
|
||||
"<environment_context>",
|
||||
"批准!繼續",
|
||||
}
|
||||
|
||||
|
||||
def load_latest_ai_agent_host_stateful_version_inventory(
|
||||
evaluations_dir: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Load the newest committed host / K3s / stateful version inventory."""
|
||||
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
|
||||
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
|
||||
if not candidates:
|
||||
raise FileNotFoundError(
|
||||
f"no AI Agent host stateful version inventory snapshots found in {directory}"
|
||||
)
|
||||
|
||||
latest = candidates[-1]
|
||||
with latest.open(encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError(f"{latest}: expected JSON object")
|
||||
_require_schema(payload, _SCHEMA_VERSION, str(latest))
|
||||
_require_read_only_boundaries(payload, str(latest))
|
||||
_require_rollup_consistency(payload, str(latest))
|
||||
_require_inventory_safety(payload, str(latest))
|
||||
_require_maintenance_approval_contract(payload, str(latest))
|
||||
_require_display_redaction(payload, str(latest))
|
||||
_require_no_plaintext_secret_payload_keys(payload, str(latest))
|
||||
_require_no_conversation_transcript_content(payload, str(latest))
|
||||
return payload
|
||||
|
||||
|
||||
def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None:
|
||||
actual = payload.get("schema_version")
|
||||
if actual != expected:
|
||||
raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}")
|
||||
|
||||
|
||||
def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None:
|
||||
program_status = payload.get("program_status") or {}
|
||||
if program_status.get("read_only_mode") is not True:
|
||||
raise ValueError(f"{label}: program_status.read_only_mode must be true")
|
||||
if program_status.get("runtime_authority") != _RUNTIME_AUTHORITY:
|
||||
raise ValueError(f"{label}: runtime_authority must stay {_RUNTIME_AUTHORITY}")
|
||||
|
||||
operation_boundaries = payload.get("operation_boundaries") or {}
|
||||
if operation_boundaries.get("read_only_inventory_allowed") is not True:
|
||||
raise ValueError(f"{label}: read_only_inventory_allowed must be true")
|
||||
|
||||
blocked_operation_flags = {
|
||||
"ssh_login_allowed",
|
||||
"host_command_execution_allowed",
|
||||
"kubectl_command_execution_allowed",
|
||||
"apt_upgrade_allowed",
|
||||
"os_release_upgrade_allowed",
|
||||
"kernel_upgrade_allowed",
|
||||
"k3s_upgrade_allowed",
|
||||
"kubelet_restart_allowed",
|
||||
"node_drain_allowed",
|
||||
"reboot_allowed",
|
||||
"stateful_service_restart_allowed",
|
||||
"database_migration_allowed",
|
||||
"backup_delete_allowed",
|
||||
"restore_execution_allowed",
|
||||
"image_pull_allowed",
|
||||
"package_install_allowed",
|
||||
"external_version_lookup_allowed",
|
||||
"active_network_scan_allowed",
|
||||
"telegram_direct_send_allowed",
|
||||
"telegram_gateway_queue_write_allowed",
|
||||
"secret_plaintext_allowed",
|
||||
"conversation_transcript_allowed",
|
||||
}
|
||||
allowed_operation_flags = sorted(
|
||||
flag
|
||||
for flag in blocked_operation_flags
|
||||
if operation_boundaries.get(flag) is not False
|
||||
)
|
||||
if allowed_operation_flags:
|
||||
raise ValueError(
|
||||
f"{label}: operation boundaries must remain false: {allowed_operation_flags}"
|
||||
)
|
||||
|
||||
approval_boundaries = payload.get("approval_boundaries") or {}
|
||||
allowed_approval_flags = sorted(
|
||||
flag for flag, value in approval_boundaries.items() if value is not False
|
||||
)
|
||||
if allowed_approval_flags:
|
||||
raise ValueError(
|
||||
f"{label}: approval boundaries must remain false: {allowed_approval_flags}"
|
||||
)
|
||||
|
||||
|
||||
def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None:
|
||||
host_inventory = payload.get("host_inventory") or []
|
||||
k3s_inventory = payload.get("k3s_inventory") or {}
|
||||
stateful_services = payload.get("stateful_services") or []
|
||||
readonly_probe_plan = payload.get("readonly_probe_plan") or []
|
||||
maintenance_requirements = payload.get("maintenance_window_approval_package") or {}
|
||||
rollups = payload.get("rollups") or {}
|
||||
|
||||
expected_counts = {
|
||||
"host_count": len(host_inventory),
|
||||
"k3s_node_count": len(k3s_inventory.get("nodes") or []),
|
||||
"stateful_service_count": len(stateful_services),
|
||||
"readonly_probe_step_count": len(readonly_probe_plan),
|
||||
"maintenance_required_field_count": len(maintenance_requirements.get("required_fields") or []),
|
||||
}
|
||||
mismatched = {
|
||||
key: {"expected": expected, "actual": rollups.get(key)}
|
||||
for key, expected in expected_counts.items()
|
||||
if rollups.get(key) != expected
|
||||
}
|
||||
if mismatched:
|
||||
raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}")
|
||||
|
||||
expected_host_ids = sorted(host.get("host_id") for host in host_inventory)
|
||||
if sorted(rollups.get("host_ids") or []) != expected_host_ids:
|
||||
raise ValueError(f"{label}: rollups.host_ids mismatch")
|
||||
|
||||
expected_service_ids = sorted(service.get("service_id") for service in stateful_services)
|
||||
if sorted(rollups.get("stateful_service_ids") or []) != expected_service_ids:
|
||||
raise ValueError(f"{label}: rollups.stateful_service_ids mismatch")
|
||||
|
||||
zero_rollups = {
|
||||
"ssh_login_allowed_count",
|
||||
"kubectl_command_execution_allowed_count",
|
||||
"apt_upgrade_allowed_count",
|
||||
"k3s_upgrade_allowed_count",
|
||||
"node_drain_allowed_count",
|
||||
"reboot_allowed_count",
|
||||
"stateful_service_restart_allowed_count",
|
||||
"telegram_direct_send_allowed_count",
|
||||
"conversation_transcript_allowed_count",
|
||||
}
|
||||
nonzero = sorted(key for key in zero_rollups if rollups.get(key) != 0)
|
||||
if nonzero:
|
||||
raise ValueError(f"{label}: safety counters must remain 0: {nonzero}")
|
||||
|
||||
|
||||
def _require_inventory_safety(payload: dict[str, Any], label: str) -> None:
|
||||
unsafe_hosts = [
|
||||
host.get("host_id")
|
||||
for host in payload.get("host_inventory") or []
|
||||
if host.get("readonly_only") is not True
|
||||
or host.get("host_update_authorized") is not False
|
||||
or host.get("reboot_authorized") is not False
|
||||
or host.get("maintenance_window_required") is not True
|
||||
or not host.get("version_observation_status")
|
||||
]
|
||||
if unsafe_hosts:
|
||||
raise ValueError(f"{label}: host inventory must remain read-only and gated: {unsafe_hosts}")
|
||||
|
||||
k3s = payload.get("k3s_inventory") or {}
|
||||
if k3s.get("skew_policy_required") is not True:
|
||||
raise ValueError(f"{label}: K3s skew policy must be required")
|
||||
if k3s.get("upgrade_authorized") is not False:
|
||||
raise ValueError(f"{label}: K3s upgrade must remain unauthorized")
|
||||
unsafe_nodes = [
|
||||
node.get("node_id")
|
||||
for node in k3s.get("nodes") or []
|
||||
if node.get("drain_authorized") is not False
|
||||
or node.get("kubelet_restart_authorized") is not False
|
||||
or node.get("readonly_only") is not True
|
||||
]
|
||||
if unsafe_nodes:
|
||||
raise ValueError(f"{label}: K3s nodes must remain read-only: {unsafe_nodes}")
|
||||
|
||||
unsafe_services = [
|
||||
service.get("service_id")
|
||||
for service in payload.get("stateful_services") or []
|
||||
if service.get("readonly_only") is not True
|
||||
or service.get("restart_authorized") is not False
|
||||
or service.get("upgrade_authorized") is not False
|
||||
or service.get("backup_required_before_change") is not True
|
||||
or not service.get("version_observation_status")
|
||||
]
|
||||
if unsafe_services:
|
||||
raise ValueError(
|
||||
f"{label}: stateful services must remain read-only and backup-gated: {unsafe_services}"
|
||||
)
|
||||
|
||||
unsafe_probe_steps = [
|
||||
step.get("step_id")
|
||||
for step in payload.get("readonly_probe_plan") or []
|
||||
if step.get("run_now_allowed") is not False
|
||||
or step.get("mutation_allowed") is not False
|
||||
or not step.get("planned_output")
|
||||
]
|
||||
if unsafe_probe_steps:
|
||||
raise ValueError(f"{label}: readonly probe steps must stay planned-only: {unsafe_probe_steps}")
|
||||
|
||||
|
||||
def _require_maintenance_approval_contract(payload: dict[str, Any], label: str) -> None:
|
||||
required_fields = {
|
||||
"owner",
|
||||
"decision",
|
||||
"maintenance_window",
|
||||
"affected_hosts",
|
||||
"affected_services",
|
||||
"backup_snapshot_ref",
|
||||
"rollback_owner",
|
||||
"rollback_plan",
|
||||
"smoke_plan",
|
||||
"communication_plan",
|
||||
"risk_acceptance",
|
||||
}
|
||||
package = payload.get("maintenance_window_approval_package") or {}
|
||||
actual_fields = set(package.get("required_fields") or [])
|
||||
if not required_fields.issubset(actual_fields):
|
||||
raise ValueError(f"{label}: maintenance window approval package missing required fields")
|
||||
if package.get("approval_required_before_probe") is not True:
|
||||
raise ValueError(f"{label}: approval must be required before live probe")
|
||||
if package.get("approval_required_before_change") is not True:
|
||||
raise ValueError(f"{label}: approval must be required before changes")
|
||||
if package.get("break_glass_record_required") is not True:
|
||||
raise ValueError(f"{label}: break-glass record must be required")
|
||||
|
||||
|
||||
def _require_display_redaction(payload: dict[str, Any], label: str) -> None:
|
||||
display = payload.get("display_redaction_contract") or {}
|
||||
if display.get("conversation_transcript_display_allowed") is not False:
|
||||
raise ValueError(f"{label}: conversation transcript display must remain false")
|
||||
if display.get("redaction_required") is not True:
|
||||
raise ValueError(f"{label}: display redaction must be required")
|
||||
|
||||
|
||||
def _require_no_plaintext_secret_payload_keys(value: Any, label: str, path: str = "$") -> None:
|
||||
if isinstance(value, dict):
|
||||
forbidden_key_fragments = {
|
||||
"secret_value",
|
||||
"token_plaintext",
|
||||
"authorization_header",
|
||||
"private_key",
|
||||
"credential_value",
|
||||
}
|
||||
for key, nested in value.items():
|
||||
normalized_key = str(key).lower()
|
||||
if any(fragment in normalized_key for fragment in forbidden_key_fragments):
|
||||
raise ValueError(f"{label}: forbidden plaintext secret key at {path}.{key}")
|
||||
_require_no_plaintext_secret_payload_keys(nested, label, f"{path}.{key}")
|
||||
elif isinstance(value, list):
|
||||
for index, nested in enumerate(value):
|
||||
_require_no_plaintext_secret_payload_keys(nested, label, f"{path}[{index}]")
|
||||
|
||||
|
||||
def _require_no_conversation_transcript_content(value: Any, label: str, path: str = "$") -> None:
|
||||
if isinstance(value, str):
|
||||
for marker in _TRANSCRIPT_MARKERS:
|
||||
if marker in value:
|
||||
raise ValueError(
|
||||
f"{label}: forbidden work-window conversation content at {path}: {marker}"
|
||||
)
|
||||
elif isinstance(value, dict):
|
||||
for key, nested in value.items():
|
||||
_require_no_conversation_transcript_content(nested, label, f"{path}.{key}")
|
||||
elif isinstance(value, list):
|
||||
for index, nested in enumerate(value):
|
||||
_require_no_conversation_transcript_content(nested, label, f"{path}[{index}]")
|
||||
122
apps/api/tests/test_ai_agent_host_stateful_version_inventory.py
Normal file
122
apps/api/tests/test_ai_agent_host_stateful_version_inventory.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from src.services.ai_agent_host_stateful_version_inventory import (
|
||||
load_latest_ai_agent_host_stateful_version_inventory,
|
||||
)
|
||||
|
||||
|
||||
def test_load_latest_ai_agent_host_stateful_version_inventory_reads_committed_snapshot():
|
||||
data = load_latest_ai_agent_host_stateful_version_inventory()
|
||||
|
||||
assert data["schema_version"] == "ai_agent_host_stateful_version_inventory_v1"
|
||||
assert data["program_status"]["overall_completion_percent"] == 86
|
||||
assert data["program_status"]["current_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402G"
|
||||
assert data["program_status"]["read_only_mode"] is True
|
||||
assert data["program_status"]["runtime_authority"] == (
|
||||
"host_stateful_readonly_inventory_no_upgrade_or_restart"
|
||||
)
|
||||
assert data["operation_boundaries"]["read_only_inventory_allowed"] is True
|
||||
assert data["operation_boundaries"]["ssh_login_allowed"] is False
|
||||
assert data["operation_boundaries"]["kubectl_command_execution_allowed"] is False
|
||||
assert data["operation_boundaries"]["apt_upgrade_allowed"] is False
|
||||
assert data["operation_boundaries"]["k3s_upgrade_allowed"] is False
|
||||
assert data["operation_boundaries"]["node_drain_allowed"] is False
|
||||
assert data["operation_boundaries"]["reboot_allowed"] is False
|
||||
assert data["operation_boundaries"]["stateful_service_restart_allowed"] is False
|
||||
assert data["operation_boundaries"]["telegram_direct_send_allowed"] is False
|
||||
assert data["operation_boundaries"]["conversation_transcript_allowed"] is False
|
||||
assert data["rollups"]["host_count"] == len(data["host_inventory"]) == 5
|
||||
assert data["rollups"]["k3s_node_count"] == len(data["k3s_inventory"]["nodes"]) == 2
|
||||
assert data["rollups"]["stateful_service_count"] == len(data["stateful_services"]) == 12
|
||||
assert data["rollups"]["readonly_probe_step_count"] == len(data["readonly_probe_plan"]) == 6
|
||||
assert data["rollups"]["ssh_login_allowed_count"] == 0
|
||||
assert data["rollups"]["kubectl_command_execution_allowed_count"] == 0
|
||||
assert data["rollups"]["apt_upgrade_allowed_count"] == 0
|
||||
assert data["rollups"]["k3s_upgrade_allowed_count"] == 0
|
||||
assert data["rollups"]["node_drain_allowed_count"] == 0
|
||||
assert data["rollups"]["reboot_allowed_count"] == 0
|
||||
assert data["rollups"]["stateful_service_restart_allowed_count"] == 0
|
||||
assert data["rollups"]["telegram_direct_send_allowed_count"] == 0
|
||||
assert data["rollups"]["conversation_transcript_allowed_count"] == 0
|
||||
assert all(host["readonly_only"] is True for host in data["host_inventory"])
|
||||
assert all(service["restart_authorized"] is False for service in data["stateful_services"])
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_blocks_ssh(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["operation_boundaries"]["ssh_login_allowed"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="operation boundaries"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_rejects_rollup_mismatch(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["rollups"]["host_count"] = 99
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="rollup counts"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_rejects_host_reboot(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["host_inventory"][0]["reboot_authorized"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="host inventory"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_rejects_k3s_drain(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["k3s_inventory"]["nodes"][0]["drain_authorized"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="K3s nodes"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_rejects_stateful_restart(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["stateful_services"][0]["restart_authorized"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="stateful services"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_requires_maintenance_fields(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["maintenance_window_approval_package"]["required_fields"] = ["owner"]
|
||||
snapshot["rollups"]["maintenance_required_field_count"] = 1
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="maintenance window approval package"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_host_stateful_version_inventory_rejects_transcript_markers(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["program_status"]["status_note"] = "禁止放入 My request for Codex"
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="forbidden work-window conversation content"):
|
||||
load_latest_ai_agent_host_stateful_version_inventory(tmp_path)
|
||||
|
||||
|
||||
def _write_snapshot(tmp_path, snapshot: dict) -> None:
|
||||
(tmp_path / "ai_agent_host_stateful_version_inventory_2026-06-11.json").write_text(
|
||||
json.dumps(snapshot),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _snapshot() -> dict:
|
||||
return json.loads(json.dumps(load_latest_ai_agent_host_stateful_version_inventory()))
|
||||
@@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.api.v1.agents import router
|
||||
|
||||
|
||||
def test_agent_host_stateful_version_inventory_endpoint_returns_committed_snapshot():
|
||||
app = FastAPI()
|
||||
app.include_router(router, prefix="/api/v1")
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/api/v1/agents/agent-host-stateful-version-inventory")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["schema_version"] == "ai_agent_host_stateful_version_inventory_v1"
|
||||
assert data["program_status"]["overall_completion_percent"] == 86
|
||||
assert data["program_status"]["current_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402G"
|
||||
assert data["program_status"]["read_only_mode"] is True
|
||||
assert data["operation_boundaries"]["read_only_inventory_allowed"] is True
|
||||
assert data["operation_boundaries"]["ssh_login_allowed"] is False
|
||||
assert data["operation_boundaries"]["kubectl_command_execution_allowed"] is False
|
||||
assert data["operation_boundaries"]["apt_upgrade_allowed"] is False
|
||||
assert data["operation_boundaries"]["k3s_upgrade_allowed"] is False
|
||||
assert data["operation_boundaries"]["node_drain_allowed"] is False
|
||||
assert data["operation_boundaries"]["reboot_allowed"] is False
|
||||
assert data["operation_boundaries"]["stateful_service_restart_allowed"] is False
|
||||
assert data["operation_boundaries"]["telegram_direct_send_allowed"] is False
|
||||
assert data["operation_boundaries"]["conversation_transcript_allowed"] is False
|
||||
assert data["rollups"]["host_count"] == 5
|
||||
assert data["rollups"]["k3s_node_count"] == 2
|
||||
assert data["rollups"]["stateful_service_count"] == 12
|
||||
assert data["rollups"]["readonly_probe_step_count"] == 6
|
||||
assert data["rollups"]["maintenance_required_field_count"] == 11
|
||||
assert data["rollups"]["ssh_login_allowed_count"] == 0
|
||||
assert data["rollups"]["kubectl_command_execution_allowed_count"] == 0
|
||||
assert data["rollups"]["apt_upgrade_allowed_count"] == 0
|
||||
assert data["rollups"]["k3s_upgrade_allowed_count"] == 0
|
||||
assert data["rollups"]["node_drain_allowed_count"] == 0
|
||||
assert data["rollups"]["reboot_allowed_count"] == 0
|
||||
assert data["rollups"]["stateful_service_restart_allowed_count"] == 0
|
||||
assert data["rollups"]["telegram_direct_send_allowed_count"] == 0
|
||||
assert data["rollups"]["conversation_transcript_allowed_count"] == 0
|
||||
|
||||
serialized = json.dumps(data, ensure_ascii=False)
|
||||
for marker in [
|
||||
"批准!繼續",
|
||||
"My request for Codex",
|
||||
"Current URL:",
|
||||
"# In app browser",
|
||||
"AGENTS.md instructions",
|
||||
]:
|
||||
assert marker not in serialized
|
||||
@@ -13,9 +13,9 @@ def test_load_latest_ai_agent_proactive_operations_contract_reads_committed_snap
|
||||
data = load_latest_ai_agent_proactive_operations_contract()
|
||||
|
||||
assert data["schema_version"] == "ai_agent_proactive_operations_contract_v1"
|
||||
assert data["program_status"]["overall_completion_percent"] == 78
|
||||
assert data["program_status"]["current_task_id"] == "P2-402E"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["overall_completion_percent"] == 86
|
||||
assert data["program_status"]["current_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402G"
|
||||
assert data["program_status"]["read_only_mode"] is True
|
||||
assert data["program_status"]["runtime_authority"] == "contract_only_no_version_or_runtime_update"
|
||||
assert data["approval_boundaries"]["runtime_version_update_allowed"] is False
|
||||
|
||||
@@ -16,9 +16,9 @@ def test_ai_agent_proactive_operations_contract_endpoint_returns_committed_snaps
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["schema_version"] == "ai_agent_proactive_operations_contract_v1"
|
||||
assert data["program_status"]["overall_completion_percent"] == 78
|
||||
assert data["program_status"]["current_task_id"] == "P2-402E"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["overall_completion_percent"] == 86
|
||||
assert data["program_status"]["current_task_id"] == "P2-402F"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402G"
|
||||
assert data["program_status"]["read_only_mode"] is True
|
||||
assert data["approval_boundaries"]["runtime_version_update_allowed"] is False
|
||||
assert data["approval_boundaries"]["package_upgrade_allowed"] is False
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
## 2026-06-11|P2-402F AI Agent host / K3s / stateful 版本只讀盤點
|
||||
|
||||
**背景**:P2-402E 已建立 Gitea PR 草案 lane,但 host OS、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 與監控 / DevOps stateful 服務仍缺少統一版本盤點與 maintenance window 批准包。本階段先建立 committed read-only inventory,讓 OpenClaw / Hermes / NemoTron 可以整理版本與維護證據,但不得執行 SSH、kubectl、升級、重啟或 Telegram 實發。
|
||||
|
||||
**本輪完成**:
|
||||
|
||||
- 新增 `docs/schemas/ai_agent_host_stateful_version_inventory_v1.schema.json`。
|
||||
- 新增 `docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json`:5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務、6 個只讀 probe step、11 個 maintenance window 必填欄位。
|
||||
- 新增 `apps/api/src/services/ai_agent_host_stateful_version_inventory.py`,強制驗證 SSH / kubectl / apt upgrade / K3s upgrade / node drain / reboot / stateful restart / Telegram direct send / conversation transcript 全部 false。
|
||||
- 新增 `GET /api/v1/agents/agent-host-stateful-version-inventory`。
|
||||
- 新增 loader / API 測試,覆蓋 SSH boundary、rollup consistency、host reboot、K3s drain、stateful restart、maintenance package 欄位與工作視窗內容 redaction。
|
||||
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `86%`,current task `P2-402F`,next task `P2-402G`。
|
||||
- 同步 `AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`、`AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md`、P2-402C/D/E 報告與 MASTER §3.2.1c / §5 / changelog。
|
||||
|
||||
**完成度同步**:
|
||||
|
||||
- P2-402F:`100%`。
|
||||
- AI Agent 主動營運委派與版本生命週期:`86%`。
|
||||
- Current task:`P2-402F`。
|
||||
- Next task:`P2-402G` governance UI 顯示可委派能力。
|
||||
|
||||
**邊界**:
|
||||
|
||||
- 本波仍不 SSH、不執行 host command、不執行 kubectl、不 apt / kernel / K3s upgrade、不 node drain、不 kubelet restart、不 reboot、不 stateful restart、不 DB migration、不 restore、不 pull image、不發 Telegram、不讀取或輸出 secret、不回傳工作視窗對話內容。
|
||||
|
||||
## 2026-06-11|P0 Telegram no-action 人工處置包
|
||||
|
||||
**背景**:使用者再次指出 Telegram 告警即使已改成 `NO_ACTION - REPAIR_CANDIDATE_MISSING`,卡片仍只說「AI 選擇不執行修復,需要人工判斷」,但沒有產出可操作的人工處置選項、證據補齊清單、修復候選建立方式或後續驗證說明。這代表前一輪只止住 fake execution,尚未把 no-action / 需人工路徑變成可接手的處置包。
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
| 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 |
|
||||
| OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` |
|
||||
| OpenClaw / Hermes / NemoTron 主動溝通與學習契約 | 35% | P2-401A 已完成只讀 contract;runtime worker、DB migration、Telegram 實發、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1` schema、`ai_agent_communication_learning_contract_2026-06-11.json`、`GET /api/v1/agents/agent-communication-learning-contract`、MASTER §3.2.1b / §3.4.3 |
|
||||
| AI Agent 主動營運委派與版本生命週期 | 78% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane 與 API。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、MASTER §3.2.1c |
|
||||
| AI Agent 主動營運委派與版本生命週期 | 86% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點與 API。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、MASTER §3.2.1c |
|
||||
| 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 |
|
||||
|
||||
AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本身完成度:**100%**。
|
||||
@@ -22,7 +22,7 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本
|
||||
|
||||
三 Agent 主動溝通與學習契約目前完成度:**35%**。已完成只讀 schema / snapshot / API / 測試與 MASTER 同步;下一步依優先順序推 `P2-401B` AgentSession / Redis Streams migration 與 worker gate,但在批准前仍不得啟動 runtime loop。
|
||||
|
||||
AI Agent 主動營運委派與版本生命週期目前完成度:**78%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy,以及 `P2-402E` Gitea PR 草案 lane;下一步是 `P2-402F` host OS / K3s / stateful services 版本只讀盤點,外部 registry / package source / host probe / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發仍需 gate。
|
||||
AI Agent 主動營運委派與版本生命週期目前完成度:**86%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane,以及 `P2-402F` host OS / K3s / stateful services 版本只讀盤點;下一步是 `P2-402G` governance UI 顯示可委派能力,外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發仍需 gate。
|
||||
|
||||
完成度計算模型:
|
||||
|
||||
@@ -956,7 +956,7 @@ UI:
|
||||
| P2-402C | 完成 | 100 | OpenClaw | 建立 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包 | `ai_agent_tool_adoption_approval_package_v1` / snapshot / 只讀 API / 測試;5 工具、5 官方來源、4 採用 lane、6 批准欄位 | 只讀;tool install、CI change、external lookup、vulnerability DB、PR、Telegram 全部未啟用 |
|
||||
| P2-402D | 完成 | 100 | OpenClaw | 建立 Telegram action-required digest policy | `ai_agent_telegram_action_required_digest_policy_v1` / snapshot / 只讀 API / 測試;8 條 digest rule、3 個 channel 草案、5 類 trigger category、成功降噪與 redaction policy | 只讀;Telegram send、Gateway queue write、route / receiver change、workflow、runtime 全部未啟用 |
|
||||
| P2-402E | 完成 | 100 | Hermes | 設計 Gitea PR 草案 lane | `ai_agent_gitea_pr_draft_lane_v1` / snapshot / 只讀 API / 測試;6 條 grouping rule、7 個 lane step、9 個 required check、6 條 rollback requirement、4 個 draft template | 只讀;branch push、PR creation、workflow trigger、lockfile、package upgrade、auto merge、Telegram 全部未啟用 |
|
||||
| P2-402F | 待辦 | 0 | OpenClaw | 建立 host OS / K3s / stateful services 版本只讀盤點 | host / K3s / DB / Redis / MinIO / Gitea 版本矩陣 | host readonly probe + maintenance window approval |
|
||||
| P2-402F | 完成 | 100 | OpenClaw | 建立 host OS / K3s / stateful services 版本只讀盤點 | `ai_agent_host_stateful_version_inventory_v1` / snapshot / 只讀 API / 測試;5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務、6 個只讀 probe step、maintenance window approval package | 只讀;SSH、kubectl、apt / K3s upgrade、node drain、reboot、stateful restart、Telegram 全部未啟用 |
|
||||
| P2-402G | 待辦 | 0 | Hermes | 接入 governance UI 顯示可委派能力 | 自主等級、gate、owner、Telegram policy | frontend UI change approval |
|
||||
| P2-101 | 待辦 | 0 | OpenClaw | 定義操作類別權限模型 | 操作政策 schema | HITL 關卡 |
|
||||
| P2-102 | 待辦 | 0 | OpenClaw | 所有候選操作都要有 dry-run 證據 | dry-run 合約 | 不直接 apply |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# AI Agent Gitea PR 草案 Lane 工作報告
|
||||
|
||||
> 日期:2026-06-11(台北時間)
|
||||
> 狀態:P2-402E 已完成;整體 AI Agent 主動營運與版本生命週期完成度 78%。
|
||||
> 狀態:P2-402E 已完成;P2-402F 已接續完成;整體 AI Agent 主動營運與版本生命週期完成度 86%。
|
||||
> 邊界:本文件與對應 API 只提供 Gitea PR 草案 lane,不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不 build / pull image、不發 Telegram、不顯示工作視窗對話內容。
|
||||
|
||||
## 1. 本波完成項目
|
||||
@@ -13,7 +13,7 @@
|
||||
| API loader | 完成 | `apps/api/src/services/ai_agent_gitea_pr_draft_lane.py` |
|
||||
| 只讀 API | 完成 | `GET /api/v1/agents/agent-gitea-pr-draft-lane` |
|
||||
| 測試 | 完成 | loader / API / PR creation boundary / automerge boundary / blocking checks / owner response / transcript redaction |
|
||||
| 主契約同步 | 完成 | `ai_agent_proactive_operations_contract_v1` 完成度 78%,current `P2-402E`,next `P2-402F` |
|
||||
| 主契約同步 | 完成 | `ai_agent_proactive_operations_contract_v1` 已由 P2-402F 推進到完成度 86%,current `P2-402F`,next `P2-402G` |
|
||||
|
||||
## 2. Grouping 規則摘要
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
| 優先 | ID | 工作 | 完成標準 |
|
||||
|---:|---|---|---|
|
||||
| 1 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 版本矩陣;只讀 probe / maintenance approval |
|
||||
| 1 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 已完成;只讀 schema / snapshot / API / 測試,SSH / kubectl / upgrade / reboot 未啟用 |
|
||||
| 2 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 |
|
||||
| 3 | 候選池 | Gitea bot / branch policy approval package | bot account、branch naming、PR permission、rollback、audit trail |
|
||||
| 4 | 候選池 | Draft PR packet renderer | 把 grouping / test / rollback / owner response 轉為可審核 PR 草案,不呼叫 Gitea API |
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# AI Agent Host / K3s / Stateful 版本只讀盤點
|
||||
|
||||
> 日期:2026-06-11(台北時間)
|
||||
> 狀態:P2-402F 已完成;整體 AI Agent 主動營運與版本生命週期完成度 86%。
|
||||
> 事實來源:`ai_agent_host_stateful_version_inventory_v1`、`ai_agent_proactive_operations_contract_v1`、MASTER §3.2.1c。
|
||||
|
||||
## 1. 本波完成
|
||||
|
||||
| 項目 | 狀態 | 說明 |
|
||||
|---|---|---|
|
||||
| Schema | 完成 | `docs/schemas/ai_agent_host_stateful_version_inventory_v1.schema.json` |
|
||||
| Snapshot | 完成 | `docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json` |
|
||||
| 只讀 loader | 完成 | 強制 SSH / kubectl / upgrade / drain / reboot / restart / Telegram / transcript gate 全為 false |
|
||||
| API | 完成 | `GET /api/v1/agents/agent-host-stateful-version-inventory` |
|
||||
| 測試 | 完成 | loader / API / rollup / reboot / drain / stateful restart / redaction boundary |
|
||||
| 主契約同步 | 完成 | `ai_agent_proactive_operations_contract_v1` 完成度 86%,current `P2-402F`,next `P2-402G` |
|
||||
|
||||
## 2. 盤點範圍
|
||||
|
||||
| 類別 | 數量 | 內容 |
|
||||
|---|---:|---|
|
||||
| Host | 5 | 188 AI / DB / monitoring / backup;110 DevOps / registry;112 Kali read-only;120 K3s blocked;121 K3s peer / VIP evidence |
|
||||
| K3s node | 2 | 120 server、121 server;需要 skew policy、node readiness、ArgoCD sync / health 與 rollback ref |
|
||||
| Stateful / ops service | 12 | PostgreSQL、Redis、MinIO、ClickHouse、SignOz、Prometheus、Alertmanager、Gitea、Harbor、Sentry、Langfuse、ArgoCD |
|
||||
| Readonly probe step | 6 | endpoint map、CD banner evidence、host probe packet、K3s skew packet、stateful packet、maintenance packet |
|
||||
| Maintenance required fields | 11 | owner、decision、maintenance window、affected scope、backup snapshot、rollback、smoke、communication、risk acceptance |
|
||||
|
||||
## 3. 仍維持 false
|
||||
|
||||
| Gate | 值 |
|
||||
|---|---|
|
||||
| SSH login | false |
|
||||
| Host command execution | false |
|
||||
| Kubectl command execution | false |
|
||||
| Apt / kernel / K3s upgrade | false |
|
||||
| Node drain / kubelet restart | false |
|
||||
| Reboot | false |
|
||||
| Stateful service restart / upgrade | false |
|
||||
| DB migration / restore / backup delete | false |
|
||||
| Image pull / package install | false |
|
||||
| External version lookup / active scan | false |
|
||||
| Telegram direct send / queue write | false |
|
||||
| Secret plaintext / 工作視窗對話內容 | false |
|
||||
|
||||
## 4. Maintenance Window 批准包
|
||||
|
||||
任何從「只讀盤點」進到「live probe」或「變更」前,都必須補齊:
|
||||
|
||||
| 欄位 | 用途 |
|
||||
|---|---|
|
||||
| owner / decision | 明確責任人與是否接受風險 |
|
||||
| maintenance_window | 維護時間窗,不可由 Agent 自行假設 |
|
||||
| affected_hosts / affected_services | 爆炸半徑與服務清單 |
|
||||
| backup_snapshot_ref | 變更前備份證據 |
|
||||
| rollback_owner / rollback_plan | 回滾責任人與步驟 |
|
||||
| smoke_plan | public / NodePort / stateful / backup / rollback readback |
|
||||
| communication_plan | Telegram / AwoooP / owner 溝通草案 |
|
||||
| risk_acceptance | 人工接受風險,不可由 AI 自動批准 |
|
||||
|
||||
## 5. 下一步
|
||||
|
||||
| 優先 | 任務 | 關卡 |
|
||||
|---:|---|---|
|
||||
| 1 | P2-402G Governance UI 顯示可委派能力 | frontend UI change approval |
|
||||
| 2 | Host readonly probe owner request | 需 owner approval,不得直接 SSH / kubectl |
|
||||
| 3 | Stateful backup freshness packet | 需備份 evidence,不得 restore / prune |
|
||||
@@ -1,7 +1,7 @@
|
||||
# AI Agent 主動營運委派與版本生命週期分析報告
|
||||
|
||||
> 日期:2026-06-11(台北時間)
|
||||
> 文件定位:P2-402A / P2-402B / P2-402C / P2-402D / P2-402E 只讀契約摘要。權威細節以 MASTER §3.2.1c、`ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1` 與 `ai_agent_gitea_pr_draft_lane_v1` 為準。
|
||||
> 文件定位:P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F 只讀契約摘要。權威細節以 MASTER §3.2.1c、`ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1` 與 `ai_agent_host_stateful_version_inventory_v1` 為準。
|
||||
|
||||
## 1. 本波完成度
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
| 工具採用批准包 | 100% | P2-402C 已完成 Renovate / OSV-Scanner / Trivy / Syft / Grype schema / committed snapshot / API / 測試 |
|
||||
| Telegram action-required digest policy | 100% | P2-402D 已完成 critical / action-required / failure-only digest schema / committed snapshot / API / 測試 |
|
||||
| Gitea PR 草案 lane | 100% | P2-402E 已完成 grouping、automerge=false、測試證據、rollback、owner response schema / committed snapshot / API / 測試 |
|
||||
| 整體主動營運與版本生命週期 | 78% | 已完成架構、repo-only freshness、工具採用批准包、Telegram digest policy 與 Gitea PR 草案 lane;runtime 排程、工具安裝、CI 變更、實際 PR 建立與更新仍未開 gate |
|
||||
| Host / K3s / stateful 版本只讀盤點 | 100% | P2-402F 已完成 5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務與 maintenance window 批准包 schema / committed snapshot / API / 測試 |
|
||||
| 整體主動營運與版本生命週期 | 86% | 已完成架構、repo-only freshness、工具採用批准包、Telegram digest policy、Gitea PR 草案 lane 與 host / K3s / stateful 版本只讀盤點;runtime 排程、工具安裝、CI 變更、實際 PR 建立與更新、host probe、升級、重啟仍未開 gate |
|
||||
|
||||
## 2. 可交給 AI Agent 的工作分類
|
||||
|
||||
@@ -46,6 +47,9 @@
|
||||
| `docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json` | Gitea PR 草案 lane schema;branch push、PR creation、workflow trigger、lockfile、automerge、Telegram 全部預設 false |
|
||||
| `docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json` | 6 條 grouping rule、7 個 lane step、9 個 required check、6 條 rollback requirement、4 個 draft template |
|
||||
| `GET /api/v1/agents/agent-gitea-pr-draft-lane` | 只讀 API;不 push branch、不建立 PR、不觸發 workflow、不改 CI、不升級套件、不發 Telegram |
|
||||
| `docs/schemas/ai_agent_host_stateful_version_inventory_v1.schema.json` | Host / K3s / stateful 版本只讀盤點 schema;SSH、kubectl、upgrade、drain、reboot、restart、Telegram 全部預設 false |
|
||||
| `docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json` | 5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務、6 個只讀 probe 步驟、maintenance window approval package |
|
||||
| `GET /api/v1/agents/agent-host-stateful-version-inventory` | 只讀 API;不 SSH、不 kubectl、不升級、不 drain、不 reboot、不重啟 stateful、不發 Telegram |
|
||||
|
||||
## 4. P2-402B Repo-only 版本新鮮度快照
|
||||
|
||||
@@ -94,16 +98,28 @@ P2-402C 的重點是把「哪些工具可被採用、採用前要填哪些批准
|
||||
|
||||
P2-402E 的重點是先把「PR 草案要長什麼樣、要有哪些證據、誰要回覆、怎麼 rollback」固定成契約。它不是 Gitea bot 啟用;bot account、branch policy、實際 PR creation、workflow trigger、lockfile write、package upgrade、Telegram 實發全部維持 blocked。
|
||||
|
||||
## 8. 下一步優先順序
|
||||
## 8. P2-402F Host / K3s / Stateful 版本只讀盤點
|
||||
|
||||
| 類別 | 本波定義 | 仍禁止 |
|
||||
|---|---|---|
|
||||
| Host inventory | 5 台主機:188 AI / DB / monitoring / backup、110 DevOps / registry、112 Kali read-only、120 K3s blocked、121 K3s peer / VIP evidence | 不 SSH、不跑 host command、不 apt upgrade、不 reboot |
|
||||
| K3s inventory | 2 個 K3s server 節點與 VIP API endpoint;要求 version skew policy、node readiness、ArgoCD sync / health、rollback ref | 不 kubectl、不 K3s upgrade、不 node drain、不 kubelet restart |
|
||||
| Stateful services | 12 個服務:PostgreSQL、Redis、MinIO、ClickHouse、SignOz、Prometheus、Alertmanager、Gitea、Harbor、Sentry、Langfuse、ArgoCD | 不 restart、不 upgrade、不 migration、不 restore、不刪 backup |
|
||||
| Readonly probe packet | 6 個 planned-only step:repo endpoint map、CD banner evidence、host probe packet、K3s skew packet、stateful packet、maintenance window packet | 不把 planned probe 當已執行,不開 runtime gate |
|
||||
| Maintenance window | owner、decision、maintenance window、affected scope、backup snapshot、rollback owner、smoke plan、communication plan、risk acceptance | 不用 Agent 自我批准取代 owner gate |
|
||||
|
||||
P2-402F 的重點是把「要盤哪些主機與 stateful 服務、盤點前要誰批准、升級前要哪些備份與 rollback 證據」固定成資料契約。它不是主機探測或升級;SSH、kubectl、apt upgrade、K3s upgrade、node drain、reboot、stateful restart、Telegram 實發全部維持 blocked。
|
||||
|
||||
## 9. 下一步優先順序
|
||||
|
||||
| ID | 優先 | 任務 | 關卡 |
|
||||
|---|---|---|---|
|
||||
| P2-402D | 0 | Telegram action-required digest policy | 已完成,只讀;Telegram send / queue write 未啟用 |
|
||||
| P2-402E | 1 | Gitea PR 草案 lane | 已完成,只讀;branch push / PR creation / workflow trigger 未啟用 |
|
||||
| P2-402F | 2 | host OS / K3s / stateful services 版本只讀盤點 | host probe / maintenance approval |
|
||||
| P2-402F | 2 | host OS / K3s / stateful services 版本只讀盤點 | 已完成,只讀;SSH / kubectl / upgrade / reboot 未啟用 |
|
||||
| P2-402G | 3 | governance UI 顯示可委派能力 | frontend UI approval |
|
||||
|
||||
## 8. 仍維持 false 的安全邊界
|
||||
## 10. 仍維持 false 的安全邊界
|
||||
|
||||
- `runtime_version_update_allowed=false`
|
||||
- `package_upgrade_allowed=false`
|
||||
@@ -119,3 +135,10 @@ P2-402E 的重點是先把「PR 草案要長什麼樣、要有哪些證據、誰
|
||||
- `external_registry_lookup_allowed=false`
|
||||
- `daily_schedule_enabled=false`
|
||||
- `gitea_pr_creation_allowed=false`
|
||||
- `ssh_login_allowed=false`
|
||||
- `kubectl_command_execution_allowed=false`
|
||||
- `apt_upgrade_allowed=false`
|
||||
- `k3s_upgrade_allowed=false`
|
||||
- `node_drain_allowed=false`
|
||||
- `reboot_allowed=false`
|
||||
- `stateful_service_restart_allowed=false`
|
||||
|
||||
@@ -50,6 +50,6 @@
|
||||
| 優先 | ID | 工作 | 完成標準 |
|
||||
|---:|---|---|---|
|
||||
| 1 | P2-402E | Gitea PR 草案 lane | 已完成:Renovate grouping、automerge=false、測試要求、rollback、owner response;bot / branch policy 仍未批准 |
|
||||
| 2 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 |
|
||||
| 2 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 已完成;主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 |
|
||||
| 3 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 |
|
||||
| 4 | 候選池 | Telegram Gateway E2E 批准包 | queue write / send / callback / fallback path 的測試與 rollout gate |
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|---:|---|---|---|---|
|
||||
| 1 | P2-402D | Telegram action-required digest policy | 定義 critical / action-required / failure-only,禁止成功洗版,接 Telegram Gateway E2E gate | 完成 |
|
||||
| 2 | P2-402E | Gitea PR 草案 lane | Renovate grouping、automerge=false、測試要求、rollback、owner response | 完成 |
|
||||
| 3 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 | 待辦 |
|
||||
| 3 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 | 完成 |
|
||||
| 4 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 | 待辦 |
|
||||
| 5 | 候選池 | SBOM artifact policy | Syft / Trivy artifact 格式、保存、redaction、消費者規則 | 未排正式編號 |
|
||||
| 6 | 候選池 | Scanner false-positive policy | OSV / Trivy / Grype 差異、suppression、severity mapping、owner SLA | 未排正式編號 |
|
||||
|
||||
@@ -0,0 +1,586 @@
|
||||
{
|
||||
"schema_version": "ai_agent_host_stateful_version_inventory_v1",
|
||||
"generated_at": "2026-06-11T15:31:00+08:00",
|
||||
"program_status": {
|
||||
"overall_completion_percent": 86,
|
||||
"current_task_id": "P2-402F",
|
||||
"next_task_id": "P2-402G",
|
||||
"read_only_mode": true,
|
||||
"runtime_authority": "host_stateful_readonly_inventory_no_upgrade_or_restart",
|
||||
"status_note": "已建立 host OS / K3s / stateful services 版本只讀盤點與 maintenance window 批准包;仍不執行 SSH、kubectl、升級、重啟、drain、restore、Telegram 實發或外部版本查詢。"
|
||||
},
|
||||
"source_refs": [
|
||||
{
|
||||
"ref_id": "service_endpoints",
|
||||
"path": "docs/reference/SERVICE-ENDPOINTS.md",
|
||||
"usage": "主機、K3s VIP、AI、資料庫、監控、DevOps、備份與安全端點來源。"
|
||||
},
|
||||
{
|
||||
"ref_id": "deployment_layout",
|
||||
"path": "docs/evaluations/ai_agent_deployment_layout_2026-06-11.json",
|
||||
"usage": "AI Agent 在 188 / 110 / 112 / 120 / 121 的只讀佈建與 gate 來源。"
|
||||
},
|
||||
{
|
||||
"ref_id": "version_freshness",
|
||||
"path": "docs/evaluations/ai_agent_version_freshness_snapshot_2026-06-11.json",
|
||||
"usage": "P2-402B 已明確把 host / K3s live version probe 留給 P2-402F gate。"
|
||||
},
|
||||
{
|
||||
"ref_id": "latest_cd_evidence",
|
||||
"path": "Gitea cd.yaml run 2667 log",
|
||||
"usage": "部署過程只讀看到 121/125 login banner 與 rollout evidence;未使用 SSH 手動命令,未收 secret value。"
|
||||
},
|
||||
{
|
||||
"ref_id": "hard_rules_high_value_config",
|
||||
"path": "docs/HARD_RULES.md#high-value-config-control",
|
||||
"usage": "主機、K3s、workflow、secret、backup、AI provider 與 runtime config 變更必須 source-of-truth、owner gate、diff、rollback 與驗證。"
|
||||
}
|
||||
],
|
||||
"operation_boundaries": {
|
||||
"read_only_inventory_allowed": true,
|
||||
"ssh_login_allowed": false,
|
||||
"host_command_execution_allowed": false,
|
||||
"kubectl_command_execution_allowed": false,
|
||||
"apt_upgrade_allowed": false,
|
||||
"os_release_upgrade_allowed": false,
|
||||
"kernel_upgrade_allowed": false,
|
||||
"k3s_upgrade_allowed": false,
|
||||
"kubelet_restart_allowed": false,
|
||||
"node_drain_allowed": false,
|
||||
"reboot_allowed": false,
|
||||
"stateful_service_restart_allowed": false,
|
||||
"database_migration_allowed": false,
|
||||
"backup_delete_allowed": false,
|
||||
"restore_execution_allowed": false,
|
||||
"image_pull_allowed": false,
|
||||
"package_install_allowed": false,
|
||||
"external_version_lookup_allowed": false,
|
||||
"active_network_scan_allowed": false,
|
||||
"telegram_direct_send_allowed": false,
|
||||
"telegram_gateway_queue_write_allowed": false,
|
||||
"secret_plaintext_allowed": false,
|
||||
"conversation_transcript_allowed": false
|
||||
},
|
||||
"approval_boundaries": {
|
||||
"host_readonly_probe_approved": false,
|
||||
"ssh_probe_approved": false,
|
||||
"kubectl_probe_approved": false,
|
||||
"maintenance_window_approved": false,
|
||||
"apt_upgrade_approved": false,
|
||||
"kernel_upgrade_approved": false,
|
||||
"k3s_upgrade_approved": false,
|
||||
"node_drain_approved": false,
|
||||
"reboot_approved": false,
|
||||
"stateful_restart_approved": false,
|
||||
"stateful_upgrade_approved": false,
|
||||
"backup_restore_drill_approved": false,
|
||||
"telegram_send_approved": false
|
||||
},
|
||||
"agent_roles": [
|
||||
{
|
||||
"agent": "OpenClaw",
|
||||
"role": "仲裁者",
|
||||
"responsibility": "判斷 host / K3s / stateful 版本盤點是否可進 owner gate,禁止在證據不足時推升級或重啟。"
|
||||
},
|
||||
{
|
||||
"agent": "Hermes",
|
||||
"role": "執行規劃者",
|
||||
"responsibility": "整理 repo evidence、probe 草案、maintenance window packet 與 rollback checklist,不執行命令。"
|
||||
},
|
||||
{
|
||||
"agent": "NemoTron",
|
||||
"role": "研究助理",
|
||||
"responsibility": "在 dry-run / human-review 邊界內協助摘要版本 skew 與相依性風險,不做生產建議最終裁決。"
|
||||
}
|
||||
],
|
||||
"host_inventory": [
|
||||
{
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"display_name": "188 AI / DB / monitoring / backup host",
|
||||
"known_addresses": [
|
||||
"192.168.0.188"
|
||||
],
|
||||
"primary_surfaces": [
|
||||
"Ollama",
|
||||
"OpenClaw",
|
||||
"PostgreSQL",
|
||||
"Redis",
|
||||
"SignOz",
|
||||
"ClickHouse",
|
||||
"Prometheus",
|
||||
"Alertmanager",
|
||||
"MinIO"
|
||||
],
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"host_update_authorized": false,
|
||||
"reboot_authorized": false,
|
||||
"maintenance_window_required": true,
|
||||
"next_evidence_needed": [
|
||||
"os-release",
|
||||
"kernel version",
|
||||
"docker / container runtime version",
|
||||
"stateful service versions",
|
||||
"backup freshness"
|
||||
],
|
||||
"blocked_actions": [
|
||||
"apt upgrade",
|
||||
"kernel upgrade",
|
||||
"reboot",
|
||||
"stateful restart",
|
||||
"backup delete",
|
||||
"restore execution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host_id": "host_110_devops_registry",
|
||||
"display_name": "110 DevOps / registry host",
|
||||
"known_addresses": [
|
||||
"192.168.0.110"
|
||||
],
|
||||
"primary_surfaces": [
|
||||
"Gitea",
|
||||
"Harbor registry",
|
||||
"Sentry",
|
||||
"Langfuse",
|
||||
"Prometheus blackbox exporter"
|
||||
],
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"host_update_authorized": false,
|
||||
"reboot_authorized": false,
|
||||
"maintenance_window_required": true,
|
||||
"next_evidence_needed": [
|
||||
"os-release",
|
||||
"Gitea version",
|
||||
"registry version",
|
||||
"Sentry / Langfuse version",
|
||||
"runner health"
|
||||
],
|
||||
"blocked_actions": [
|
||||
"workflow change",
|
||||
"runner restart",
|
||||
"registry image delete",
|
||||
"host package upgrade",
|
||||
"reboot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host_id": "host_112_kali_readonly",
|
||||
"display_name": "112 Kali scanner read-only host",
|
||||
"known_addresses": [
|
||||
"192.168.0.112"
|
||||
],
|
||||
"primary_surfaces": [
|
||||
"Kali scanner API",
|
||||
"security evidence collector"
|
||||
],
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"host_update_authorized": false,
|
||||
"reboot_authorized": false,
|
||||
"maintenance_window_required": true,
|
||||
"next_evidence_needed": [
|
||||
"scanner service version",
|
||||
"tool package baseline",
|
||||
"active scan gate state"
|
||||
],
|
||||
"blocked_actions": [
|
||||
"active scan",
|
||||
"credentialed scan",
|
||||
"tool upgrade",
|
||||
"reboot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host_id": "host_120_k3s_master_blocked",
|
||||
"display_name": "120 K3s master / blocked recovery host",
|
||||
"known_addresses": [
|
||||
"192.168.0.120"
|
||||
],
|
||||
"primary_surfaces": [
|
||||
"K3s server",
|
||||
"control-plane recovery context"
|
||||
],
|
||||
"version_observation_status": "blocked_host_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"host_update_authorized": false,
|
||||
"reboot_authorized": false,
|
||||
"maintenance_window_required": true,
|
||||
"next_evidence_needed": [
|
||||
"reachability evidence",
|
||||
"K3s version",
|
||||
"node readiness",
|
||||
"kubelet status"
|
||||
],
|
||||
"blocked_actions": [
|
||||
"ssh probe",
|
||||
"node drain",
|
||||
"K3s upgrade",
|
||||
"kubelet restart",
|
||||
"reboot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host_id": "host_121_k3s_peer",
|
||||
"display_name": "121 K3s peer host",
|
||||
"known_addresses": [
|
||||
"192.168.0.121",
|
||||
"192.168.0.125"
|
||||
],
|
||||
"primary_surfaces": [
|
||||
"K3s server",
|
||||
"VIP endpoint",
|
||||
"AWOOOI API/Web/Worker rollout evidence"
|
||||
],
|
||||
"version_observation_status": "cd_login_banner_observed_os_only",
|
||||
"observed_evidence_summary": "Gitea CD run 2667 login banner observed Ubuntu 22.04.5 LTS and kernel 5.15.0-179-generic on the deploy target; this is deployment evidence only, not a full host version inventory.",
|
||||
"readonly_only": true,
|
||||
"host_update_authorized": false,
|
||||
"reboot_authorized": false,
|
||||
"maintenance_window_required": true,
|
||||
"next_evidence_needed": [
|
||||
"K3s version",
|
||||
"kubectl version",
|
||||
"container runtime version",
|
||||
"node readiness",
|
||||
"pending package updates with owner gate"
|
||||
],
|
||||
"blocked_actions": [
|
||||
"apt upgrade",
|
||||
"do-release-upgrade",
|
||||
"node drain",
|
||||
"K3s upgrade",
|
||||
"reboot"
|
||||
]
|
||||
}
|
||||
],
|
||||
"k3s_inventory": {
|
||||
"cluster_id": "awoooi_prod_k3s",
|
||||
"api_endpoint": "192.168.0.125:6443",
|
||||
"version_observation_status": "repo_reference_only_kubectl_probe_not_run",
|
||||
"skew_policy_required": true,
|
||||
"upgrade_authorized": false,
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": "k3s_120",
|
||||
"host_id": "host_120_k3s_master_blocked",
|
||||
"role": "server",
|
||||
"readonly_only": true,
|
||||
"drain_authorized": false,
|
||||
"kubelet_restart_authorized": false,
|
||||
"version_observation_status": "blocked_host_live_probe_not_run"
|
||||
},
|
||||
{
|
||||
"node_id": "k3s_121",
|
||||
"host_id": "host_121_k3s_peer",
|
||||
"role": "server",
|
||||
"readonly_only": true,
|
||||
"drain_authorized": false,
|
||||
"kubelet_restart_authorized": false,
|
||||
"version_observation_status": "rollout_evidence_only_version_probe_not_run"
|
||||
}
|
||||
],
|
||||
"required_pre_change_evidence": [
|
||||
"kubectl version --client/output redacted",
|
||||
"K3s server version",
|
||||
"node readiness",
|
||||
"current ArgoCD sync / health",
|
||||
"version skew policy assessment",
|
||||
"rollback ref"
|
||||
]
|
||||
},
|
||||
"stateful_services": [
|
||||
{
|
||||
"service_id": "postgresql",
|
||||
"display_name": "PostgreSQL",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#資料庫-1921680188",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "redis",
|
||||
"display_name": "Redis",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#資料庫-1921680188",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "minio",
|
||||
"display_name": "MinIO",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#備份-1921680188",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "clickhouse",
|
||||
"display_name": "ClickHouse",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "signoz",
|
||||
"display_name": "SignOz",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "prometheus",
|
||||
"display_name": "Prometheus",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "alertmanager",
|
||||
"display_name": "Alertmanager",
|
||||
"host_id": "host_188_ai_db_monitoring_backup",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "gitea",
|
||||
"display_name": "Gitea",
|
||||
"host_id": "host_110_devops_registry",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#devops-1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "harbor_registry",
|
||||
"display_name": "Harbor / registry",
|
||||
"host_id": "host_110_devops_registry",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#devops-1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "sentry",
|
||||
"display_name": "Sentry",
|
||||
"host_id": "host_110_devops_registry",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "langfuse",
|
||||
"display_name": "Langfuse",
|
||||
"host_id": "host_110_devops_registry",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#監控-1921680188--1921680110",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
},
|
||||
{
|
||||
"service_id": "argocd",
|
||||
"display_name": "ArgoCD",
|
||||
"host_id": "host_121_k3s_peer",
|
||||
"endpoint_ref": "docs/reference/SERVICE-ENDPOINTS.md#k3s-叢集管理",
|
||||
"version_observation_status": "repo_reference_only_live_probe_not_run",
|
||||
"readonly_only": true,
|
||||
"restart_authorized": false,
|
||||
"upgrade_authorized": false,
|
||||
"backup_required_before_change": true
|
||||
}
|
||||
],
|
||||
"readonly_probe_plan": [
|
||||
{
|
||||
"step_id": "repo_endpoint_map",
|
||||
"display_name": "Repo endpoint map 對齊",
|
||||
"planned_output": "整理 SERVICE-ENDPOINTS、runtime surface、deployment layout 的主機與服務 mapping。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
},
|
||||
{
|
||||
"step_id": "cd_banner_evidence_extract",
|
||||
"display_name": "CD banner evidence 摘要",
|
||||
"planned_output": "只讀引用最新 CD login banner / rollout log,標註其侷限,不當成完整版本盤點。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
},
|
||||
{
|
||||
"step_id": "host_readonly_probe_packet",
|
||||
"display_name": "Host readonly probe packet",
|
||||
"planned_output": "準備 os-release、kernel、container runtime、package pending summary 的 owner approval request。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
},
|
||||
{
|
||||
"step_id": "k3s_version_skew_packet",
|
||||
"display_name": "K3s version skew packet",
|
||||
"planned_output": "準備 K3s / kubectl / node readiness / skew policy 的只讀證據欄位。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
},
|
||||
{
|
||||
"step_id": "stateful_service_version_packet",
|
||||
"display_name": "Stateful service version packet",
|
||||
"planned_output": "準備 PostgreSQL、Redis、MinIO、Gitea、Harbor、監控服務的版本與備份前置欄位。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
},
|
||||
{
|
||||
"step_id": "maintenance_window_packet",
|
||||
"display_name": "Maintenance window approval packet",
|
||||
"planned_output": "產出 owner、affected scope、backup、rollback、smoke、communication 與 risk acceptance 欄位。",
|
||||
"run_now_allowed": false,
|
||||
"mutation_allowed": false
|
||||
}
|
||||
],
|
||||
"maintenance_window_approval_package": {
|
||||
"package_id": "host_stateful_maintenance_window_packet_v1",
|
||||
"approval_required_before_probe": true,
|
||||
"approval_required_before_change": true,
|
||||
"break_glass_record_required": true,
|
||||
"required_fields": [
|
||||
"owner",
|
||||
"decision",
|
||||
"maintenance_window",
|
||||
"affected_hosts",
|
||||
"affected_services",
|
||||
"backup_snapshot_ref",
|
||||
"rollback_owner",
|
||||
"rollback_plan",
|
||||
"smoke_plan",
|
||||
"communication_plan",
|
||||
"risk_acceptance"
|
||||
],
|
||||
"forbidden_fields": [
|
||||
"secret value",
|
||||
"private key",
|
||||
"authorization header",
|
||||
"cookie",
|
||||
"session payload",
|
||||
"work-window transcript",
|
||||
"chain-of-thought"
|
||||
],
|
||||
"minimum_smoke_plan": [
|
||||
"public health",
|
||||
"internal NodePort health when applicable",
|
||||
"stateful service ready check",
|
||||
"backup freshness readback",
|
||||
"rollback ref readback"
|
||||
]
|
||||
},
|
||||
"telegram_policy": {
|
||||
"status": "draft_only",
|
||||
"direct_send_allowed": false,
|
||||
"gateway_queue_write_allowed": false,
|
||||
"allowed_digest_types_after_gate": [
|
||||
"action_required_missing_evidence",
|
||||
"critical_version_skew",
|
||||
"maintenance_window_owner_response_needed",
|
||||
"rollout_failure"
|
||||
],
|
||||
"success_noise_suppression": true
|
||||
},
|
||||
"display_redaction_contract": {
|
||||
"conversation_transcript_display_allowed": false,
|
||||
"redaction_required": true,
|
||||
"allowed_frontend_fields": [
|
||||
"整體完成度",
|
||||
"目前任務",
|
||||
"下一任務",
|
||||
"主機版本盤點摘要",
|
||||
"K3s skew gate",
|
||||
"stateful services 摘要",
|
||||
"maintenance window approval package"
|
||||
],
|
||||
"forbidden_frontend_content": [
|
||||
"工作視窗對話內容",
|
||||
"secret 明文",
|
||||
"private key",
|
||||
"authorization header",
|
||||
"cookie",
|
||||
"prompt 全文",
|
||||
"chain-of-thought",
|
||||
"browser session context"
|
||||
]
|
||||
},
|
||||
"rollups": {
|
||||
"host_count": 5,
|
||||
"k3s_node_count": 2,
|
||||
"stateful_service_count": 12,
|
||||
"readonly_probe_step_count": 6,
|
||||
"maintenance_required_field_count": 11,
|
||||
"host_ids": [
|
||||
"host_110_devops_registry",
|
||||
"host_112_kali_readonly",
|
||||
"host_120_k3s_master_blocked",
|
||||
"host_121_k3s_peer",
|
||||
"host_188_ai_db_monitoring_backup"
|
||||
],
|
||||
"stateful_service_ids": [
|
||||
"alertmanager",
|
||||
"argocd",
|
||||
"clickhouse",
|
||||
"gitea",
|
||||
"harbor_registry",
|
||||
"langfuse",
|
||||
"minio",
|
||||
"postgresql",
|
||||
"prometheus",
|
||||
"redis",
|
||||
"sentry",
|
||||
"signoz"
|
||||
],
|
||||
"ssh_login_allowed_count": 0,
|
||||
"kubectl_command_execution_allowed_count": 0,
|
||||
"apt_upgrade_allowed_count": 0,
|
||||
"k3s_upgrade_allowed_count": 0,
|
||||
"node_drain_allowed_count": 0,
|
||||
"reboot_allowed_count": 0,
|
||||
"stateful_service_restart_allowed_count": 0,
|
||||
"telegram_direct_send_allowed_count": 0,
|
||||
"conversation_transcript_allowed_count": 0
|
||||
},
|
||||
"next_actions": [
|
||||
{
|
||||
"task_id": "P2-402G",
|
||||
"priority": "P2",
|
||||
"summary": "把可委派能力接入 governance UI,顯示 autonomy level、gate、owner、Telegram policy。",
|
||||
"gate": "frontend_ui_change_approval_required"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
"schema_version": "ai_agent_proactive_operations_contract_v1",
|
||||
"generated_at": "2026-06-11T21:30:00+08:00",
|
||||
"program_status": {
|
||||
"overall_completion_percent": 78,
|
||||
"overall_completion_percent": 86,
|
||||
"current_priority": "P2",
|
||||
"current_task_id": "P2-402E",
|
||||
"next_task_id": "P2-402F",
|
||||
"current_task_id": "P2-402F",
|
||||
"next_task_id": "P2-402G",
|
||||
"read_only_mode": true,
|
||||
"runtime_authority": "contract_only_no_version_or_runtime_update",
|
||||
"status_note": "P2-402E 已建立 Gitea PR 草案 lane、schema、snapshot、只讀 API 與測試;本波仍不 push branch、不建立或更新 Gitea PR、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不發 Telegram。"
|
||||
"status_note": "P2-402F 已建立 host OS / K3s / stateful services 版本只讀盤點與 maintenance window 批准包;本波仍不 SSH、不執行 kubectl、不升級主機 / K3s / stateful service、不 drain、不 reboot、不發 Telegram。"
|
||||
},
|
||||
"external_source_evidence": [
|
||||
{
|
||||
@@ -646,16 +646,16 @@
|
||||
"completion_percent": 100,
|
||||
"owner_agent": "Hermes",
|
||||
"summary": "建立 Gitea PR 草案 lane、schema、snapshot、只讀 API 與測試;定義 grouping、automerge=false、測試證據、rollback、owner response 與 redaction policy。",
|
||||
"next_gate": "P2-402F_host_stateful_version_inventory"
|
||||
"next_gate": "P2-402F_completed"
|
||||
},
|
||||
{
|
||||
"task_id": "P2-402F",
|
||||
"priority": "P2",
|
||||
"status": "planned",
|
||||
"completion_percent": 0,
|
||||
"status": "done",
|
||||
"completion_percent": 100,
|
||||
"owner_agent": "OpenClaw",
|
||||
"summary": "建立 host OS / K3s / stateful services 版本只讀盤點與 maintenance window 批准包。",
|
||||
"next_gate": "host_readonly_probe_and_maintenance_window_approval_required"
|
||||
"summary": "建立 host OS / K3s / stateful services 版本只讀盤點、maintenance window 批准包、schema、snapshot、只讀 API 與測試;所有 SSH / kubectl / upgrade / drain / reboot / restart gate 維持 false。",
|
||||
"next_gate": "P2-402G_governance_ui_capability_display"
|
||||
},
|
||||
{
|
||||
"task_id": "P2-402G",
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://awoooi.wooo.work/schemas/ai_agent_host_stateful_version_inventory_v1.schema.json",
|
||||
"title": "AI Agent Host Stateful Version Inventory v1",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema_version",
|
||||
"generated_at",
|
||||
"program_status",
|
||||
"operation_boundaries",
|
||||
"approval_boundaries",
|
||||
"host_inventory",
|
||||
"k3s_inventory",
|
||||
"stateful_services",
|
||||
"readonly_probe_plan",
|
||||
"maintenance_window_approval_package",
|
||||
"display_redaction_contract",
|
||||
"rollups"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": "ai_agent_host_stateful_version_inventory_v1"
|
||||
},
|
||||
"generated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"program_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"overall_completion_percent",
|
||||
"current_task_id",
|
||||
"next_task_id",
|
||||
"read_only_mode",
|
||||
"runtime_authority"
|
||||
],
|
||||
"properties": {
|
||||
"overall_completion_percent": {
|
||||
"const": 86
|
||||
},
|
||||
"current_task_id": {
|
||||
"const": "P2-402F"
|
||||
},
|
||||
"next_task_id": {
|
||||
"const": "P2-402G"
|
||||
},
|
||||
"read_only_mode": {
|
||||
"const": true
|
||||
},
|
||||
"runtime_authority": {
|
||||
"const": "host_stateful_readonly_inventory_no_upgrade_or_restart"
|
||||
}
|
||||
}
|
||||
},
|
||||
"operation_boundaries": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"read_only_inventory_allowed",
|
||||
"ssh_login_allowed",
|
||||
"host_command_execution_allowed",
|
||||
"kubectl_command_execution_allowed",
|
||||
"apt_upgrade_allowed",
|
||||
"k3s_upgrade_allowed",
|
||||
"node_drain_allowed",
|
||||
"reboot_allowed",
|
||||
"stateful_service_restart_allowed",
|
||||
"telegram_direct_send_allowed",
|
||||
"secret_plaintext_allowed",
|
||||
"conversation_transcript_allowed"
|
||||
],
|
||||
"properties": {
|
||||
"read_only_inventory_allowed": {
|
||||
"const": true
|
||||
},
|
||||
"ssh_login_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"host_command_execution_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"kubectl_command_execution_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"apt_upgrade_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"k3s_upgrade_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"node_drain_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"reboot_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"stateful_service_restart_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"telegram_direct_send_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"secret_plaintext_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"conversation_transcript_allowed": {
|
||||
"const": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"host_inventory": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"host_id",
|
||||
"readonly_only",
|
||||
"version_observation_status",
|
||||
"host_update_authorized",
|
||||
"reboot_authorized",
|
||||
"maintenance_window_required"
|
||||
],
|
||||
"properties": {
|
||||
"readonly_only": {
|
||||
"const": true
|
||||
},
|
||||
"host_update_authorized": {
|
||||
"const": false
|
||||
},
|
||||
"reboot_authorized": {
|
||||
"const": false
|
||||
},
|
||||
"maintenance_window_required": {
|
||||
"const": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"stateful_services": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"service_id",
|
||||
"readonly_only",
|
||||
"version_observation_status",
|
||||
"restart_authorized",
|
||||
"upgrade_authorized",
|
||||
"backup_required_before_change"
|
||||
],
|
||||
"properties": {
|
||||
"readonly_only": {
|
||||
"const": true
|
||||
},
|
||||
"restart_authorized": {
|
||||
"const": false
|
||||
},
|
||||
"upgrade_authorized": {
|
||||
"const": false
|
||||
},
|
||||
"backup_required_before_change": {
|
||||
"const": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"display_redaction_contract": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"conversation_transcript_display_allowed",
|
||||
"redaction_required"
|
||||
],
|
||||
"properties": {
|
||||
"conversation_transcript_display_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"redaction_required": {
|
||||
"const": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,7 +670,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
|
||||
| 檔案 / API | 用途 |
|
||||
|---|---|
|
||||
| `docs/schemas/ai_agent_proactive_operations_contract_v1.schema.json` | 主動營運委派、版本生命週期、MCP、RAG、Telegram policy、approval boundary 契約 |
|
||||
| `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` | 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory;完成度 `78%` |
|
||||
| `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` | 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory;完成度 `86%` |
|
||||
| `apps/api/src/services/ai_agent_proactive_operations_contract.py` | 只讀 loader;強制 runtime update / package upgrade / host upgrade / workflow schedule / auto merge / Telegram direct send 全部 false |
|
||||
| `GET /api/v1/agents/agent-proactive-operations-contract` | 治理 API;只回傳 committed snapshot,不啟用排程、不升級、不呼叫付費服務 |
|
||||
| `docs/schemas/ai_agent_version_freshness_snapshot_v1.schema.json` | P2-402B repo-only 版本新鮮度 schema;鎖定 schedule / external lookup / upgrade / Telegram / PR / host probe 全部 false |
|
||||
@@ -685,6 +685,9 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
|
||||
| `docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json` | P2-402E Gitea PR 草案 lane schema;鎖定 branch push / PR creation / workflow trigger / lockfile / automerge / Telegram 全部 false |
|
||||
| `docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json` | P2-402E committed snapshot:6 條 grouping rule、7 個 lane step、9 個 required check、6 條 rollback requirement、4 個 draft template |
|
||||
| `GET /api/v1/agents/agent-gitea-pr-draft-lane` | 只讀 API;只回傳 PR 草案 lane,不 push branch、不建立 PR、不觸發 workflow、不改 CI、不升級套件、不發 Telegram |
|
||||
| `docs/schemas/ai_agent_host_stateful_version_inventory_v1.schema.json` | P2-402F host / K3s / stateful 版本只讀盤點 schema;鎖定 SSH / kubectl / upgrade / drain / reboot / restart / Telegram 全部 false |
|
||||
| `docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json` | P2-402F committed snapshot:5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務、6 個只讀 probe step、maintenance window approval package |
|
||||
| `GET /api/v1/agents/agent-host-stateful-version-inventory` | 只讀 API;只回傳 host / K3s / stateful 版本盤點,不 SSH、不 kubectl、不升級、不重啟、不發 Telegram |
|
||||
|
||||
**採用順序:**
|
||||
|
||||
@@ -692,7 +695,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
|
||||
2. 再評估 external primary source weekly watch:Renovate、OSV-Scanner、Trivy、Syft、Grype、Kubernetes skew policy、Docker Scout。✅ P2-402C 已建立工具採用批准包;工具安裝、CI 變更、外部查詢仍未授權。
|
||||
3. 建立 Telegram action-required digest policy,先定義 critical / action-required / failure-only 通知門檻,禁止成功洗版。✅ P2-402D 已建立 policy snapshot/API;Telegram 實發與 queue write 仍未授權。
|
||||
4. 再進 Gitea PR 草案 lane:grouping、automerge=false、tests、rollback、owner response。✅ P2-402E 已建立 draft lane snapshot/API;branch push、PR creation、workflow trigger、lockfile、package upgrade、auto merge 仍未授權。
|
||||
5. 最後才進人工批准後的 dry-run / smoke / canary / production rollout。下一步 P2-402F host OS / K3s / stateful services 版本只讀盤點。
|
||||
5. 建立 host OS / K3s / stateful services 版本只讀盤點,先固定維護窗口、備份、rollback 與 smoke gate。✅ P2-402F 已建立 snapshot/API;SSH、kubectl、upgrade、drain、reboot、stateful restart 仍未授權。
|
||||
6. 最後才進人工批准後的 dry-run / smoke / canary / production rollout。下一步 P2-402G governance UI 顯示可委派能力。
|
||||
|
||||
#### 3.2.2 核心缺口與災難場景
|
||||
|
||||
@@ -1726,6 +1730,12 @@ Phase 6 完成後
|
||||
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `78%`,current task `P2-402E`,next task `P2-402F`。
|
||||
- 本波仍不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不 build / pull image、不發 Telegram、不讀取或輸出 secret、不回傳工作視窗對話內容;P2-402F 才建立 host OS / K3s / stateful services 版本只讀盤點。
|
||||
|
||||
### 2026-06-11 23:59 (台北) — §3.2 / §5 — 完成 P2-402F host / K3s / stateful 版本只讀盤點 — 回應統帥要求讓 Agent 定期管理主機、套件、服務與維護窗口
|
||||
|
||||
- 新增 `ai_agent_host_stateful_version_inventory_v1` schema / committed snapshot / loader / API / 測試,定義 5 台主機、2 個 K3s 節點、12 個 stateful / ops 服務、6 個只讀 probe step、maintenance window approval package 與 redaction policy。
|
||||
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `86%`,current task `P2-402F`,next task `P2-402G`。
|
||||
- 本波仍不 SSH、不執行 host command、不執行 kubectl、不 apt / kernel / K3s upgrade、不 node drain、不 kubelet restart、不 reboot、不 stateful restart、不 DB migration、不 restore、不 pull image、不發 Telegram、不讀取或輸出 secret、不回傳工作視窗對話內容;P2-402G 才接治理 UI 顯示可委派能力。
|
||||
|
||||
### 2026-04-15 (台北) — 全檔 — 建立 v2 骨架,§0/§1 完成 — 統帥批准「單 MASTER + 4 道閘門」結構
|
||||
|
||||
- 從 v1(plans/2026-04-15-MASTER-ai-autonomous-flywheel.md)繼承核心發現
|
||||
|
||||
Reference in New Issue
Block a user