feat(governance): 新增 Agent Gitea PR 草案 lane
This commit is contained in:
@@ -55,6 +55,9 @@ from src.services.ai_agent_communication_learning_contract import (
|
||||
from src.services.ai_agent_deployment_layout import (
|
||||
load_latest_ai_agent_deployment_layout,
|
||||
)
|
||||
from src.services.ai_agent_gitea_pr_draft_lane import (
|
||||
load_latest_ai_agent_gitea_pr_draft_lane,
|
||||
)
|
||||
from src.services.ai_agent_proactive_operations_contract import (
|
||||
load_latest_ai_agent_proactive_operations_contract,
|
||||
)
|
||||
@@ -677,6 +680,35 @@ async def get_agent_telegram_action_required_digest_policy() -> dict[str, Any]:
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/agent-gitea-pr-draft-lane",
|
||||
response_model=dict[str, Any],
|
||||
summary="取得 AI Agent Gitea PR 草案 lane",
|
||||
description=(
|
||||
"讀取最新已提交的 AI Agent Gitea PR 草案 lane;"
|
||||
"此端點只回傳 grouping、automerge=false、測試證據、rollback、owner response 與 redaction 邊界,"
|
||||
"不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、"
|
||||
"不寫 lockfile、不升級套件、不 build/pull image、不改 production route、不發 Telegram、"
|
||||
"不讀取 secret、不回傳工作視窗對話內容。"
|
||||
),
|
||||
)
|
||||
async def get_agent_gitea_pr_draft_lane() -> dict[str, Any]:
|
||||
"""Return the latest read-only AI Agent Gitea PR draft lane policy."""
|
||||
try:
|
||||
return await asyncio.to_thread(load_latest_ai_agent_gitea_pr_draft_lane)
|
||||
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_gitea_pr_draft_lane_invalid", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="AI Agent Gitea PR 草案 lane 無效",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/runtime-surface-inventory",
|
||||
response_model=dict[str, Any],
|
||||
|
||||
300
apps/api/src/services/ai_agent_gitea_pr_draft_lane.py
Normal file
300
apps/api/src/services/ai_agent_gitea_pr_draft_lane.py
Normal file
@@ -0,0 +1,300 @@
|
||||
"""
|
||||
AI Agent Gitea PR draft lane snapshot.
|
||||
|
||||
Loads the latest committed, read-only policy for AI Agent generated Gitea PR
|
||||
draft plans. This module never pushes branches, creates PRs, edits workflows,
|
||||
writes lockfiles, upgrades packages, triggers CI, sends Telegram messages, 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_gitea_pr_draft_lane_*.json"
|
||||
_SCHEMA_VERSION = "ai_agent_gitea_pr_draft_lane_v1"
|
||||
_RUNTIME_AUTHORITY = "draft_lane_only_no_pr_creation_or_branch_push"
|
||||
_TRANSCRIPT_MARKERS = {
|
||||
"# In app browser",
|
||||
"My request for Codex",
|
||||
"Current URL:",
|
||||
"AGENTS.md instructions",
|
||||
"<environment_context>",
|
||||
"批准!繼續",
|
||||
}
|
||||
|
||||
|
||||
def load_latest_ai_agent_gitea_pr_draft_lane(
|
||||
evaluations_dir: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Load the newest committed AI Agent Gitea PR draft lane policy."""
|
||||
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
|
||||
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
|
||||
if not candidates:
|
||||
raise FileNotFoundError(f"no AI Agent Gitea PR draft lane 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_grouping_and_checks(payload, str(latest))
|
||||
_require_owner_and_rollback_contracts(payload, str(latest))
|
||||
_require_template_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_lane_allowed") is not True:
|
||||
raise ValueError(f"{label}: read_only_lane_allowed must be true")
|
||||
|
||||
blocked_operation_flags = {
|
||||
"gitea_branch_push_allowed",
|
||||
"gitea_pr_creation_allowed",
|
||||
"gitea_pr_update_allowed",
|
||||
"gitea_pr_comment_allowed",
|
||||
"auto_merge_allowed",
|
||||
"workflow_trigger_allowed",
|
||||
"ci_workflow_change_allowed",
|
||||
"lockfile_write_allowed",
|
||||
"package_upgrade_allowed",
|
||||
"file_mutation_allowed",
|
||||
"external_registry_lookup_allowed",
|
||||
"vulnerability_database_download_allowed",
|
||||
"docker_build_allowed",
|
||||
"image_pull_allowed",
|
||||
"production_route_change_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:
|
||||
grouping_rules = payload.get("grouping_rules") or []
|
||||
lane_steps = payload.get("lane_steps") or []
|
||||
required_checks = payload.get("required_checks") or []
|
||||
owner_requirements = payload.get("owner_response_requirements") or []
|
||||
rollback_requirements = payload.get("rollback_requirements") or []
|
||||
templates = payload.get("draft_templates") or []
|
||||
rollups = payload.get("rollups") or {}
|
||||
|
||||
expected_counts = {
|
||||
"grouping_rule_count": len(grouping_rules),
|
||||
"lane_step_count": len(lane_steps),
|
||||
"required_check_count": len(required_checks),
|
||||
"owner_response_requirement_count": len(owner_requirements),
|
||||
"rollback_requirement_count": len(rollback_requirements),
|
||||
"draft_template_count": len(templates),
|
||||
}
|
||||
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_group_ids = sorted(rule.get("group_id") for rule in grouping_rules)
|
||||
if sorted(rollups.get("draft_group_ids") or []) != expected_group_ids:
|
||||
raise ValueError(f"{label}: rollups.draft_group_ids mismatch")
|
||||
|
||||
expected_owner_ids = sorted(
|
||||
requirement.get("requirement_id") for requirement in owner_requirements
|
||||
)
|
||||
if sorted(rollups.get("owner_response_requirement_ids") or []) != expected_owner_ids:
|
||||
raise ValueError(f"{label}: rollups.owner_response_requirement_ids mismatch")
|
||||
|
||||
zero_rollups = {
|
||||
"gitea_branch_push_allowed_count",
|
||||
"gitea_pr_creation_allowed_count",
|
||||
"auto_merge_allowed_count",
|
||||
"workflow_trigger_allowed_count",
|
||||
"lockfile_write_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}: draft lane safety counters must remain 0: {nonzero}")
|
||||
|
||||
|
||||
def _require_grouping_and_checks(payload: dict[str, Any], label: str) -> None:
|
||||
unsafe_groups = [
|
||||
rule.get("group_id")
|
||||
for rule in payload.get("grouping_rules") or []
|
||||
if rule.get("draft_only") is not True
|
||||
or rule.get("automerge") is not False
|
||||
or rule.get("requires_openclaw_review") is not True
|
||||
or rule.get("rollback_required") is not True
|
||||
or not rule.get("required_check_ids")
|
||||
or not isinstance(rule.get("max_batch_size"), int)
|
||||
or rule.get("max_batch_size", 0) < 1
|
||||
]
|
||||
if unsafe_groups:
|
||||
raise ValueError(f"{label}: grouping rules must stay draft-only and gated: {unsafe_groups}")
|
||||
|
||||
check_ids = {check.get("check_id") for check in payload.get("required_checks") or []}
|
||||
unknown_check_refs = sorted(
|
||||
{
|
||||
check_id
|
||||
for rule in payload.get("grouping_rules") or []
|
||||
for check_id in rule.get("required_check_ids") or []
|
||||
if check_id not in check_ids
|
||||
}
|
||||
)
|
||||
if unknown_check_refs:
|
||||
raise ValueError(f"{label}: grouping rules reference unknown checks: {unknown_check_refs}")
|
||||
|
||||
unsafe_checks = [
|
||||
check.get("check_id")
|
||||
for check in payload.get("required_checks") or []
|
||||
if check.get("blocking") is not True
|
||||
or check.get("evidence_required") is not True
|
||||
or check.get("run_now_allowed") is not False
|
||||
]
|
||||
if unsafe_checks:
|
||||
raise ValueError(f"{label}: required checks must be blocking evidence-only: {unsafe_checks}")
|
||||
|
||||
unsafe_steps = [
|
||||
step.get("step_id")
|
||||
for step in payload.get("lane_steps") or []
|
||||
if step.get("runtime_execution_allowed") is not False
|
||||
or step.get("repo_write_allowed") is not False
|
||||
or not step.get("planned_output")
|
||||
]
|
||||
if unsafe_steps:
|
||||
raise ValueError(f"{label}: lane steps must remain read-only plans: {unsafe_steps}")
|
||||
|
||||
|
||||
def _require_owner_and_rollback_contracts(payload: dict[str, Any], label: str) -> None:
|
||||
required_owner_fields = {
|
||||
"owner",
|
||||
"decision",
|
||||
"business_impact",
|
||||
"risk_acceptance",
|
||||
"rollback_acceptance",
|
||||
"maintenance_window",
|
||||
"evidence_ref",
|
||||
}
|
||||
actual_owner_fields = {
|
||||
field
|
||||
for requirement in payload.get("owner_response_requirements") or []
|
||||
for field in requirement.get("required_fields") or []
|
||||
}
|
||||
if not required_owner_fields.issubset(actual_owner_fields):
|
||||
raise ValueError(f"{label}: owner response requirements missing required fields")
|
||||
|
||||
unsafe_rollback = [
|
||||
item.get("requirement_id")
|
||||
for item in payload.get("rollback_requirements") or []
|
||||
if item.get("required") is not True
|
||||
or item.get("must_be_attached_before_pr_creation") is not True
|
||||
]
|
||||
if unsafe_rollback:
|
||||
raise ValueError(f"{label}: rollback requirements must be attached before PR: {unsafe_rollback}")
|
||||
|
||||
|
||||
def _require_template_redaction(payload: dict[str, Any], label: str) -> None:
|
||||
forbidden_fields = {
|
||||
"secret_value",
|
||||
"token",
|
||||
"authorization_header",
|
||||
"work_window_transcript",
|
||||
"codex_user_message",
|
||||
"prompt_text",
|
||||
"chain_of_thought",
|
||||
"session_id",
|
||||
"browser_context",
|
||||
}
|
||||
for template in payload.get("draft_templates") or []:
|
||||
template_id = template.get("template_id")
|
||||
if template.get("automerge") is not False:
|
||||
raise ValueError(f"{label}: draft template must keep automerge=false: {template_id}")
|
||||
if template.get("branch_push_allowed") is not False:
|
||||
raise ValueError(f"{label}: draft template must not allow branch push: {template_id}")
|
||||
if not forbidden_fields.issubset(set(template.get("forbidden_fields") or [])):
|
||||
raise ValueError(f"{label}: draft template missing redaction fields: {template_id}")
|
||||
|
||||
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}]")
|
||||
113
apps/api/tests/test_ai_agent_gitea_pr_draft_lane.py
Normal file
113
apps/api/tests/test_ai_agent_gitea_pr_draft_lane.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from src.services.ai_agent_gitea_pr_draft_lane import (
|
||||
load_latest_ai_agent_gitea_pr_draft_lane,
|
||||
)
|
||||
|
||||
|
||||
def test_load_latest_ai_agent_gitea_pr_draft_lane_reads_committed_snapshot():
|
||||
data = load_latest_ai_agent_gitea_pr_draft_lane()
|
||||
|
||||
assert data["schema_version"] == "ai_agent_gitea_pr_draft_lane_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"]["read_only_mode"] is True
|
||||
assert data["program_status"]["runtime_authority"] == (
|
||||
"draft_lane_only_no_pr_creation_or_branch_push"
|
||||
)
|
||||
assert data["branch_strategy"]["branch_push_allowed"] is False
|
||||
assert data["branch_strategy"]["pr_creation_allowed"] is False
|
||||
assert data["branch_strategy"]["automerge"] is False
|
||||
assert data["operation_boundaries"]["gitea_branch_push_allowed"] is False
|
||||
assert data["operation_boundaries"]["gitea_pr_creation_allowed"] is False
|
||||
assert data["operation_boundaries"]["workflow_trigger_allowed"] is False
|
||||
assert data["operation_boundaries"]["lockfile_write_allowed"] is False
|
||||
assert data["operation_boundaries"]["telegram_direct_send_allowed"] is False
|
||||
assert data["operation_boundaries"]["conversation_transcript_allowed"] is False
|
||||
assert data["approval_boundaries"]["gitea_pr_creation_approved"] is False
|
||||
assert data["rollups"]["grouping_rule_count"] == len(data["grouping_rules"]) == 6
|
||||
assert data["rollups"]["lane_step_count"] == len(data["lane_steps"]) == 7
|
||||
assert data["rollups"]["required_check_count"] == len(data["required_checks"]) == 9
|
||||
assert data["rollups"]["draft_template_count"] == len(data["draft_templates"]) == 4
|
||||
assert data["rollups"]["gitea_branch_push_allowed_count"] == 0
|
||||
assert data["rollups"]["gitea_pr_creation_allowed_count"] == 0
|
||||
assert data["rollups"]["auto_merge_allowed_count"] == 0
|
||||
assert data["rollups"]["workflow_trigger_allowed_count"] == 0
|
||||
assert data["rollups"]["lockfile_write_allowed_count"] == 0
|
||||
assert data["rollups"]["telegram_direct_send_allowed_count"] == 0
|
||||
assert data["rollups"]["conversation_transcript_allowed_count"] == 0
|
||||
assert all(rule["automerge"] is False for rule in data["grouping_rules"])
|
||||
assert all(template["branch_push_allowed"] is False for template in data["draft_templates"])
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_blocks_pr_creation(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["operation_boundaries"]["gitea_pr_creation_allowed"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="operation boundaries"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_rejects_rollup_mismatch(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["rollups"]["grouping_rule_count"] = 99
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="rollup counts"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_rejects_automerge(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["grouping_rules"][0]["automerge"] = True
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="draft-only"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_rejects_non_blocking_check(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["required_checks"][0]["blocking"] = False
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="blocking evidence-only"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_requires_owner_response_fields(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["owner_response_requirements"][0]["required_fields"] = ["owner"]
|
||||
snapshot["owner_response_requirements"][1]["required_fields"] = ["owner"]
|
||||
snapshot["owner_response_requirements"][2]["required_fields"] = ["owner"]
|
||||
snapshot["owner_response_requirements"][3]["required_fields"] = ["owner"]
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="owner response requirements"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def test_ai_agent_gitea_pr_draft_lane_rejects_transcript_markers(tmp_path):
|
||||
snapshot = _snapshot()
|
||||
snapshot["lane_intent"]["purpose"] = "禁止放入 My request for Codex"
|
||||
_write_snapshot(tmp_path, snapshot)
|
||||
|
||||
with pytest.raises(ValueError, match="forbidden work-window conversation content"):
|
||||
load_latest_ai_agent_gitea_pr_draft_lane(tmp_path)
|
||||
|
||||
|
||||
def _write_snapshot(tmp_path, snapshot: dict) -> None:
|
||||
(tmp_path / "ai_agent_gitea_pr_draft_lane_2026-06-11.json").write_text(
|
||||
json.dumps(snapshot),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _snapshot() -> dict:
|
||||
return json.loads(json.dumps(load_latest_ai_agent_gitea_pr_draft_lane()))
|
||||
51
apps/api/tests/test_ai_agent_gitea_pr_draft_lane_api.py
Normal file
51
apps/api/tests/test_ai_agent_gitea_pr_draft_lane_api.py
Normal file
@@ -0,0 +1,51 @@
|
||||
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_gitea_pr_draft_lane_endpoint_returns_committed_snapshot():
|
||||
app = FastAPI()
|
||||
app.include_router(router, prefix="/api/v1")
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/api/v1/agents/agent-gitea-pr-draft-lane")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["schema_version"] == "ai_agent_gitea_pr_draft_lane_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"]["read_only_mode"] is True
|
||||
assert data["branch_strategy"]["branch_push_allowed"] is False
|
||||
assert data["branch_strategy"]["pr_creation_allowed"] is False
|
||||
assert data["branch_strategy"]["automerge"] is False
|
||||
assert data["operation_boundaries"]["gitea_branch_push_allowed"] is False
|
||||
assert data["operation_boundaries"]["gitea_pr_creation_allowed"] is False
|
||||
assert data["operation_boundaries"]["workflow_trigger_allowed"] is False
|
||||
assert data["operation_boundaries"]["lockfile_write_allowed"] is False
|
||||
assert data["operation_boundaries"]["telegram_direct_send_allowed"] is False
|
||||
assert data["operation_boundaries"]["conversation_transcript_allowed"] is False
|
||||
assert data["approval_boundaries"]["gitea_pr_creation_approved"] is False
|
||||
assert data["rollups"]["grouping_rule_count"] == 6
|
||||
assert data["rollups"]["lane_step_count"] == 7
|
||||
assert data["rollups"]["required_check_count"] == 9
|
||||
assert data["rollups"]["draft_template_count"] == 4
|
||||
assert data["rollups"]["gitea_pr_creation_allowed_count"] == 0
|
||||
assert data["rollups"]["auto_merge_allowed_count"] == 0
|
||||
assert data["rollups"]["workflow_trigger_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"] == 68
|
||||
assert data["program_status"]["current_task_id"] == "P2-402D"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402E"
|
||||
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"]["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"] == 68
|
||||
assert data["program_status"]["current_task_id"] == "P2-402D"
|
||||
assert data["program_status"]["next_task_id"] == "P2-402E"
|
||||
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"]["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-402E AI Agent Gitea PR 草案 lane
|
||||
|
||||
**背景**:P2-402D 已把 Telegram action-required digest policy 固定成只讀資料契約;下一步需要讓 AI Agent 可以主動整理版本更新、工具採用、CVE / OSV、container image、AI Agent contract、host / K3s / stateful maintenance 與 docs / KM 的 Gitea PR 草案,但仍不可直接 push branch、建立 PR 或觸發 workflow。
|
||||
|
||||
**本輪完成**:
|
||||
|
||||
- 新增 `docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json`。
|
||||
- 新增 `docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json`:6 條 grouping rule、7 個 lane step、9 個 required check、4 個 owner response requirement、6 條 rollback requirement、4 個 draft template。
|
||||
- 新增 `apps/api/src/services/ai_agent_gitea_pr_draft_lane.py`,強制驗證 read-only mode、branch push / PR creation / workflow trigger / lockfile / package upgrade / auto merge / Telegram direct send 全部 false。
|
||||
- 新增 `GET /api/v1/agents/agent-gitea-pr-draft-lane`。
|
||||
- 新增 loader / API 測試,覆蓋 PR creation boundary、automerge boundary、required check、owner response、rollup consistency、工作視窗內容 redaction。
|
||||
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `78%`,current task `P2-402E`,next task `P2-402F`。
|
||||
- 同步 `AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`、`AI_AGENT_PROACTIVE_OPERATIONS_2026-06-11.md`、`AI_AGENT_TOOL_ADOPTION_APPROVAL_PACKAGE_2026-06-11.md`、`AI_AGENT_TELEGRAM_ACTION_REQUIRED_DIGEST_POLICY_2026-06-11.md` 與 MASTER §3.2.1c / §5 / changelog。
|
||||
|
||||
**完成度同步**:
|
||||
|
||||
- P2-402E:`100%`。
|
||||
- AI Agent 主動營運委派與版本生命週期:`78%`。
|
||||
- Current task:`P2-402E`。
|
||||
- Next task:`P2-402F` host OS / K3s / stateful services 版本只讀盤點。
|
||||
|
||||
**邊界**:
|
||||
|
||||
- 本波仍不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不 build / pull image、不改 production route、不發 Telegram、不讀取或輸出 secret、不回傳工作視窗對話內容。
|
||||
|
||||
## 2026-06-11|IwoooS 高價值配置 Owner Packet 接入 AwoooP 首頁
|
||||
|
||||
**背景**:P0.4 已把高價值配置 owner packet 草案接到 `/zh-TW/iwooos`。本階段把同一個只讀狀態鏡像到 `/zh-TW/awooop` 首頁,讓 AwoooP 工作視窗能看到 IwoooS 產生的配置 owner packet 草案,但仍不得把「可見」解讀成 request 已送出、owner 已回覆、owner 已接受或 runtime 已開。
|
||||
|
||||
@@ -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 主動營運委派與版本生命週期 | 68% | P2-402A / P2-402B / P2-402C / P2-402D 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy 與 API。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、auto merge、Telegram 實發仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_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`、MASTER §3.2.1c |
|
||||
| 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 |
|
||||
| 本工作清單與分析報告 | 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 主動營運委派與版本生命週期目前完成度:**68%**。已完成 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,外部 registry / package source / host probe / 工具安裝 / CI 變更 / Telegram 實發仍需 gate。
|
||||
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。
|
||||
|
||||
完成度計算模型:
|
||||
|
||||
@@ -955,7 +955,7 @@ UI:
|
||||
| P2-402B | 完成 | 100 | Hermes | 建立 repo-only daily version freshness snapshot | `ai_agent_version_freshness_snapshot_v1` / snapshot / 只讀 API / 測試;12 類 repo-only source、5 個 daily step、3 Agent role | 只讀;workflow schedule 未啟用、外部 lookup 未開、不得升級或發 Telegram |
|
||||
| P2-402C | 完成 | 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 | 待辦 | 0 | Hermes | 設計 Gitea PR 草案 lane | grouping、automerge=false、tests、rollback、owner response | bot / branch policy approval |
|
||||
| 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-402G | 待辦 | 0 | Hermes | 接入 governance UI 顯示可委派能力 | 自主等級、gate、owner、Telegram policy | frontend UI change approval |
|
||||
| P2-101 | 待辦 | 0 | OpenClaw | 定義操作類別權限模型 | 操作政策 schema | HITL 關卡 |
|
||||
|
||||
72
docs/ai/AI_AGENT_GITEA_PR_DRAFT_LANE_2026-06-11.md
Normal file
72
docs/ai/AI_AGENT_GITEA_PR_DRAFT_LANE_2026-06-11.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# AI Agent Gitea PR 草案 Lane 工作報告
|
||||
|
||||
> 日期:2026-06-11(台北時間)
|
||||
> 狀態:P2-402E 已完成;整體 AI Agent 主動營運與版本生命週期完成度 78%。
|
||||
> 邊界:本文件與對應 API 只提供 Gitea PR 草案 lane,不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不 build / pull image、不發 Telegram、不顯示工作視窗對話內容。
|
||||
|
||||
## 1. 本波完成項目
|
||||
|
||||
| 項目 | 狀態 | 產出 |
|
||||
|---|---|---|
|
||||
| Gitea PR 草案 lane schema | 完成 | `docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json` |
|
||||
| Gitea PR 草案 lane snapshot | 完成 | `docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json` |
|
||||
| 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` |
|
||||
|
||||
## 2. Grouping 規則摘要
|
||||
|
||||
| Group | 主責 Agent | 風險 | Max batch | 邊界 |
|
||||
|---|---|---:|---:|---|
|
||||
| 低風險依賴 patch 草案 | Hermes | medium | 5 | draft-only、automerge=false、需 OpenClaw review |
|
||||
| CVE / OSV 修補草案 | OpenClaw | high | 2 | 不查外部 DB、不升級套件、不 hot patch |
|
||||
| Container image digest / base image 草案 | Hermes | high | 2 | 不 build、不 pull、不改 kustomization image tag |
|
||||
| AI Agent / model / tool contract 草案 | NemoTron + OpenClaw | critical | 1 | 需 sanitized replay gate,不開 shadow / canary |
|
||||
| Host / K3s / stateful maintenance 草案 | OpenClaw | critical | 1 | 不 host upgrade、不 reboot、不 restart stateful service |
|
||||
| 文件 / runbook / KM 草案 | Hermes | low | 8 | 不發布 canonical KM、不放 secret 或工作視窗內容 |
|
||||
|
||||
## 3. PR 前必備證據
|
||||
|
||||
| 類別 | 必備內容 |
|
||||
|---|---|
|
||||
| 測試 | schema / JSON、targeted tests、secret 與 transcript redaction |
|
||||
| 風險 | blast radius、OpenClaw review、是否拆分 PR |
|
||||
| Rollback | git revert、config recovery、runtime disable switch、smoke 失敗回退 |
|
||||
| Owner response | owner、decision、business impact、risk acceptance、rollback acceptance、maintenance window、evidence ref |
|
||||
| AI / model | sanitized replay gate、hidden-label grading、不得自行批准上線 |
|
||||
|
||||
## 4. Agent 分工
|
||||
|
||||
| Agent | 本波可做 | 本波不可做 |
|
||||
|---|---|---|
|
||||
| Hermes | 彙整 committed evidence、分組、release note、test plan、draft PR packet | push branch、建立 PR、觸發 workflow、寫 lockfile |
|
||||
| OpenClaw | 仲裁風險、rollback、owner response、HITL、是否拆 PR | 自行批准、merge PR、runtime execution、production route change |
|
||||
| NemoTron | 對 AI Agent / model / prompt / tool contract 變更提出 sanitized replay gate | 呼叫付費 API、外部 replay、shadow / canary、生產路由 |
|
||||
|
||||
## 5. 仍維持 false 的邊界
|
||||
|
||||
- `gitea_branch_push_allowed=false`
|
||||
- `gitea_pr_creation_allowed=false`
|
||||
- `gitea_pr_update_allowed=false`
|
||||
- `gitea_pr_comment_allowed=false`
|
||||
- `auto_merge_allowed=false`
|
||||
- `workflow_trigger_allowed=false`
|
||||
- `ci_workflow_change_allowed=false`
|
||||
- `lockfile_write_allowed=false`
|
||||
- `package_upgrade_allowed=false`
|
||||
- `docker_build_allowed=false`
|
||||
- `image_pull_allowed=false`
|
||||
- `telegram_direct_send_allowed=false`
|
||||
- `telegram_gateway_queue_write_allowed=false`
|
||||
- `secret_plaintext_allowed=false`
|
||||
- `conversation_transcript_allowed=false`
|
||||
|
||||
## 6. 下一步代辦
|
||||
|
||||
| 優先 | ID | 工作 | 完成標準 |
|
||||
|---:|---|---|---|
|
||||
| 1 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 版本矩陣;只讀 probe / maintenance approval |
|
||||
| 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 |
|
||||
@@ -1,7 +1,7 @@
|
||||
# AI Agent 主動營運委派與版本生命週期分析報告
|
||||
|
||||
> 日期:2026-06-11(台北時間)
|
||||
> 文件定位:P2-402A / P2-402B / P2-402C / P2-402D 只讀契約摘要。權威細節以 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` 為準。
|
||||
> 文件定位: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` 為準。
|
||||
|
||||
## 1. 本波完成度
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
| Repo-only 版本新鮮度快照 | 100% | P2-402B 已完成 schema / committed snapshot / API / 測試 |
|
||||
| 工具採用批准包 | 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 / 測試 |
|
||||
| 整體主動營運與版本生命週期 | 68% | 已完成架構、repo-only freshness、工具採用批准包與 Telegram digest policy;runtime 排程、工具安裝、CI 變更與更新仍未開 gate |
|
||||
| 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 |
|
||||
|
||||
## 2. 可交給 AI Agent 的工作分類
|
||||
|
||||
@@ -42,6 +43,9 @@
|
||||
| `docs/schemas/ai_agent_telegram_action_required_digest_policy_v1.schema.json` | Telegram action-required digest policy schema;direct send、queue write、route / receiver change、workflow、transcript 全部預設 false |
|
||||
| `docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-11.json` | 8 條 digest rule、3 個 channel 草案、5 類 trigger category、成功降噪、redaction、fallback policy |
|
||||
| `GET /api/v1/agents/agent-telegram-action-required-digest-policy` | 只讀 API;不送 Telegram、不寫 queue、不改 route / receiver、不觸發 workflow |
|
||||
| `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 |
|
||||
|
||||
## 4. P2-402B Repo-only 版本新鮮度快照
|
||||
|
||||
@@ -78,12 +82,24 @@ P2-402C 的重點是把「哪些工具可被採用、採用前要填哪些批准
|
||||
|
||||
本波把「哪些事情值得通知」和「哪些成功訊息必須壓掉」固定成資料契約。Telegram Gateway queue write、direct send、route / receiver change、AwoooP event write、workflow trigger、runtime execution 仍全部為 false。
|
||||
|
||||
## 7. 下一步優先順序
|
||||
## 7. P2-402E Gitea PR 草案 Lane
|
||||
|
||||
| 類別 | 本波定義 | 仍禁止 |
|
||||
|---|---|---|
|
||||
| Grouping | 6 條:低風險依賴、CVE / OSV、container image、AI Agent contract、host / K3s / stateful、docs / KM | 不混入無關風險,不把 critical 變更和低風險 patch 同 PR |
|
||||
| PR policy | `automerge=false`、draft-only、需 OpenClaw review、需 rollback | 不 push branch、不建立 / 更新 PR、不留言、不 merge |
|
||||
| Tests | schema / JSON、targeted tests、secret / transcript redaction、vulnerability / image / replay / deployment evidence | 不觸發 workflow、不改 CI、不 build / pull image |
|
||||
| Owner response | owner、decision、business impact、risk acceptance、rollback acceptance、maintenance window、evidence ref | 不用 Agent 自我批准取代 owner response |
|
||||
| Redaction | 不顯示 secret、prompt、chain-of-thought、browser context、工作視窗對話內容 | 不把工作視窗對話放到前端或文件 |
|
||||
|
||||
P2-402E 的重點是先把「PR 草案要長什麼樣、要有哪些證據、誰要回覆、怎麼 rollback」固定成契約。它不是 Gitea bot 啟用;bot account、branch policy、實際 PR creation、workflow trigger、lockfile write、package upgrade、Telegram 實發全部維持 blocked。
|
||||
|
||||
## 8. 下一步優先順序
|
||||
|
||||
| ID | 優先 | 任務 | 關卡 |
|
||||
|---|---|---|---|
|
||||
| P2-402D | 0 | Telegram action-required digest policy | 已完成,只讀;Telegram send / queue write 未啟用 |
|
||||
| P2-402E | 1 | Gitea PR 草案 lane | bot / branch policy approval |
|
||||
| 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-402G | 3 | governance UI 顯示可委派能力 | frontend UI approval |
|
||||
|
||||
@@ -95,6 +111,7 @@ P2-402C 的重點是把「哪些工具可被採用、採用前要填哪些批准
|
||||
- `container_pull_allowed=false`
|
||||
- `workflow_schedule_enabled=false`
|
||||
- `auto_merge_allowed=false`
|
||||
- `gitea_pr_creation_allowed=false`
|
||||
- `telegram_direct_send_allowed=false`
|
||||
- `secret_plaintext_allowed=false`
|
||||
- `paid_external_service_allowed=false`
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
| 優先 | ID | 工作 | 完成標準 |
|
||||
|---:|---|---|---|
|
||||
| 1 | P2-402E | Gitea PR 草案 lane | Renovate grouping、automerge=false、測試要求、rollback、owner response、bot / branch policy approval |
|
||||
| 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 只讀版本矩陣 |
|
||||
| 3 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 |
|
||||
| 4 | 候選池 | Telegram Gateway E2E 批准包 | queue write / send / callback / fallback path 的測試與 rollout gate |
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
| 優先 | ID | 工作 | 完成標準 | 目前狀態 |
|
||||
|---:|---|---|---|---|
|
||||
| 1 | P2-402D | Telegram action-required digest policy | 定義 critical / action-required / failure-only,禁止成功洗版,接 Telegram Gateway E2E gate | 完成 |
|
||||
| 2 | P2-402E | Gitea PR 草案 lane | Renovate grouping、automerge=false、測試要求、rollback、owner response | 待辦 |
|
||||
| 2 | P2-402E | Gitea PR 草案 lane | Renovate grouping、automerge=false、測試要求、rollback、owner response | 完成 |
|
||||
| 3 | P2-402F | host OS / K3s / stateful services 版本只讀盤點 | 主機、K3s、PostgreSQL、Redis、MinIO、Harbor、Gitea 只讀版本矩陣 | 待辦 |
|
||||
| 4 | P2-402G | Governance UI 顯示可委派能力 | 前端顯示 autonomy level、gate、owner、Telegram policy,無執行按鈕 | 待辦 |
|
||||
| 5 | 候選池 | SBOM artifact policy | Syft / Trivy artifact 格式、保存、redaction、消費者規則 | 未排正式編號 |
|
||||
|
||||
694
docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json
Normal file
694
docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json
Normal file
@@ -0,0 +1,694 @@
|
||||
{
|
||||
"schema_version": "ai_agent_gitea_pr_draft_lane_v1",
|
||||
"generated_at": "2026-06-11T23:59:40+08:00",
|
||||
"program_status": {
|
||||
"overall_completion_percent": 78,
|
||||
"current_priority": "P2",
|
||||
"current_task_id": "P2-402E",
|
||||
"next_task_id": "P2-402F",
|
||||
"read_only_mode": true,
|
||||
"runtime_authority": "draft_lane_only_no_pr_creation_or_branch_push",
|
||||
"status_note": "P2-402E 已建立 Gitea PR 草案 lane;本波只定義 grouping、automerge=false、測試證據、rollback、owner response,不 push branch、不建立 PR、不觸發 workflow、不升級套件、不發 Telegram。"
|
||||
},
|
||||
"source_refs": [
|
||||
"docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json",
|
||||
"docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json",
|
||||
"docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-11.json",
|
||||
"docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#321c-2026-06-11-ai-agent-主動營運委派與版本生命週期契約",
|
||||
"docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md"
|
||||
],
|
||||
"lane_intent": {
|
||||
"purpose": "讓 Hermes / OpenClaw / NemoTron 先產生可審核的 Gitea PR 草案計畫,包含分組、風險、測試、rollback 與 owner response;真正 branch push / PR creation 必須等 bot、branch policy 與人工批准。",
|
||||
"non_goals": [
|
||||
"不建立 Gitea branch",
|
||||
"不建立或更新 Gitea PR",
|
||||
"不修改 workflow",
|
||||
"不升級 package 或 lockfile",
|
||||
"不 build / pull container image",
|
||||
"不觸發 CI workflow",
|
||||
"不 auto merge",
|
||||
"不送 Telegram"
|
||||
],
|
||||
"human_approval_gate": "gitea_bot_and_branch_policy_approval_required"
|
||||
},
|
||||
"branch_strategy": {
|
||||
"draft_branch_prefix": "agents/proposal/",
|
||||
"branch_name_pattern": "agents/proposal/{lane_id}/{yyyyMMdd}-{short_target}",
|
||||
"branch_push_allowed": false,
|
||||
"pr_creation_allowed": false,
|
||||
"automerge": false,
|
||||
"required_prefix_after_approval": "codex/"
|
||||
},
|
||||
"grouping_rules": [
|
||||
{
|
||||
"group_id": "dependency_patch_low_risk",
|
||||
"display_name": "低風險依賴 patch 草案",
|
||||
"owner_agent": "Hermes",
|
||||
"risk_tier": "medium",
|
||||
"max_batch_size": 5,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"schema_and_json_validation",
|
||||
"targeted_tests_plan",
|
||||
"secret_and_transcript_redaction"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"manifest proposal",
|
||||
"lockfile proposal after approval",
|
||||
"release note summary"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"major version bump",
|
||||
"security critical package",
|
||||
"runtime config mutation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_id": "security_cve_advisory",
|
||||
"display_name": "CVE / OSV 修補草案",
|
||||
"owner_agent": "OpenClaw",
|
||||
"risk_tier": "high",
|
||||
"max_batch_size": 2,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"vulnerability_evidence_ref",
|
||||
"targeted_tests_plan",
|
||||
"rollback_plan_attached",
|
||||
"owner_response_required"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"security advisory summary",
|
||||
"patch proposal",
|
||||
"blast radius matrix"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"external vulnerability DB download",
|
||||
"package upgrade without owner approval",
|
||||
"production hot patch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_id": "container_image_digest_proposal",
|
||||
"display_name": "Container image digest / base image 草案",
|
||||
"owner_agent": "Hermes",
|
||||
"risk_tier": "high",
|
||||
"max_batch_size": 2,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"image_digest_evidence",
|
||||
"targeted_tests_plan",
|
||||
"rollback_plan_attached",
|
||||
"maintenance_window_required"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"Dockerfile proposal",
|
||||
"image digest pin proposal",
|
||||
"smoke plan"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"docker build",
|
||||
"image pull",
|
||||
"kustomization image tag mutation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_id": "ai_agent_model_tool_contract_change",
|
||||
"display_name": "AI Agent / model / tool contract 草案",
|
||||
"owner_agent": "NemoTron + OpenClaw",
|
||||
"risk_tier": "critical",
|
||||
"max_batch_size": 1,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"sanitized_replay_gate",
|
||||
"targeted_tests_plan",
|
||||
"owner_response_required",
|
||||
"rollback_plan_attached"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"offline replay plan",
|
||||
"model route proposal",
|
||||
"tool contract proposal"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"production route change",
|
||||
"paid API enablement",
|
||||
"shadow or canary without gate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_id": "host_k3s_stateful_maintenance_plan",
|
||||
"display_name": "Host / K3s / stateful maintenance 草案",
|
||||
"owner_agent": "OpenClaw",
|
||||
"risk_tier": "critical",
|
||||
"max_batch_size": 1,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"maintenance_window_required",
|
||||
"rollback_plan_attached",
|
||||
"owner_response_required",
|
||||
"targeted_tests_plan"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"maintenance window proposal",
|
||||
"version skew report",
|
||||
"stateful backup readiness note"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"host upgrade",
|
||||
"reboot",
|
||||
"stateful service restart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_id": "docs_runbook_km_update",
|
||||
"display_name": "文件 / runbook / KM 草案",
|
||||
"owner_agent": "Hermes",
|
||||
"risk_tier": "low",
|
||||
"max_batch_size": 8,
|
||||
"draft_only": true,
|
||||
"automerge": false,
|
||||
"requires_openclaw_review": true,
|
||||
"rollback_required": true,
|
||||
"required_check_ids": [
|
||||
"schema_and_json_validation",
|
||||
"secret_and_transcript_redaction",
|
||||
"owner_response_required"
|
||||
],
|
||||
"allowed_change_kinds": [
|
||||
"runbook proposal",
|
||||
"LOGBOOK summary",
|
||||
"KM draft"
|
||||
],
|
||||
"blocked_change_kinds": [
|
||||
"canonical KM publish without owner response",
|
||||
"secret-bearing evidence",
|
||||
"work-window transcript"
|
||||
]
|
||||
}
|
||||
],
|
||||
"lane_steps": [
|
||||
{
|
||||
"step_id": "collect_committed_evidence",
|
||||
"owner_agent": "Hermes",
|
||||
"purpose": "讀取已提交 snapshot、LOGBOOK 與批准包,整理 PR 草案候選來源。",
|
||||
"planned_output": "evidence_ref_list",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "read_only_allowed"
|
||||
},
|
||||
{
|
||||
"step_id": "group_and_dedupe_candidates",
|
||||
"owner_agent": "Hermes",
|
||||
"purpose": "依變更類型、風險與 owner 分組,避免單 PR 混入無關風險。",
|
||||
"planned_output": "grouping_decision",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "draft_lane_policy_only"
|
||||
},
|
||||
{
|
||||
"step_id": "openclaw_risk_arbitration",
|
||||
"owner_agent": "OpenClaw",
|
||||
"purpose": "審核 blast radius、rollback、HITL 與是否需拆 PR。",
|
||||
"planned_output": "risk_verdict",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "openclaw_review_required"
|
||||
},
|
||||
{
|
||||
"step_id": "nemotron_replay_requirement_check",
|
||||
"owner_agent": "NemoTron",
|
||||
"purpose": "只對 AI Agent / model / prompt / tool contract 類變更提出 sanitized replay 需求。",
|
||||
"planned_output": "replay_gate_requirement",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "offline_replay_approval_required"
|
||||
},
|
||||
{
|
||||
"step_id": "attach_test_and_smoke_plan",
|
||||
"owner_agent": "Hermes",
|
||||
"purpose": "列出 PR 前必備測試、schema、secret/redaction 與 production smoke 證據。",
|
||||
"planned_output": "test_plan",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "test_plan_required"
|
||||
},
|
||||
{
|
||||
"step_id": "attach_rollback_and_owner_response",
|
||||
"owner_agent": "OpenClaw",
|
||||
"purpose": "要求 rollback、owner response、maintenance window 與風險接受欄位齊全。",
|
||||
"planned_output": "rollback_owner_packet",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "owner_response_required"
|
||||
},
|
||||
{
|
||||
"step_id": "emit_draft_pr_packet",
|
||||
"owner_agent": "Hermes",
|
||||
"purpose": "只產出 PR 草案封包;不 push branch、不呼叫 Gitea API、不觸發 workflow。",
|
||||
"planned_output": "draft_pr_packet",
|
||||
"runtime_execution_allowed": false,
|
||||
"repo_write_allowed": false,
|
||||
"approval_gate": "gitea_bot_and_branch_policy_approval_required"
|
||||
}
|
||||
],
|
||||
"required_checks": [
|
||||
{
|
||||
"check_id": "schema_and_json_validation",
|
||||
"display_name": "Schema / JSON 驗證",
|
||||
"owner_agent": "Hermes",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "python3 -m json.tool 與 targeted schema validation evidence"
|
||||
},
|
||||
{
|
||||
"check_id": "targeted_tests_plan",
|
||||
"display_name": "Targeted tests plan",
|
||||
"owner_agent": "Hermes",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "pytest / ruff / frontend smoke plan,依變更範圍列出"
|
||||
},
|
||||
{
|
||||
"check_id": "secret_and_transcript_redaction",
|
||||
"display_name": "Secret 與工作視窗內容遮蔽",
|
||||
"owner_agent": "OpenClaw",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "doc secrets sanity check + forbidden transcript marker scan"
|
||||
},
|
||||
{
|
||||
"check_id": "rollback_plan_attached",
|
||||
"display_name": "Rollback plan attached",
|
||||
"owner_agent": "OpenClaw",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "回滾 commit / config revert / deployment rollback plan"
|
||||
},
|
||||
{
|
||||
"check_id": "owner_response_required",
|
||||
"display_name": "Owner response required",
|
||||
"owner_agent": "OpenClaw",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "owner 接受、拒絕或要求拆分的結構化回覆"
|
||||
},
|
||||
{
|
||||
"check_id": "vulnerability_evidence_ref",
|
||||
"display_name": "Vulnerability evidence ref",
|
||||
"owner_agent": "OpenClaw",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "OSV / CVE / scanner report ref;本波不查外部 DB"
|
||||
},
|
||||
{
|
||||
"check_id": "image_digest_evidence",
|
||||
"display_name": "Image digest evidence",
|
||||
"owner_agent": "Hermes",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "image digest / base image release evidence;本波不 pull image"
|
||||
},
|
||||
{
|
||||
"check_id": "sanitized_replay_gate",
|
||||
"display_name": "Sanitized replay gate",
|
||||
"owner_agent": "NemoTron",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "AI Agent / model / prompt 變更需 sanitized replay plan 與 hidden-label gate"
|
||||
},
|
||||
{
|
||||
"check_id": "maintenance_window_required",
|
||||
"display_name": "Deployment / maintenance window required",
|
||||
"owner_agent": "OpenClaw",
|
||||
"blocking": true,
|
||||
"evidence_required": true,
|
||||
"run_now_allowed": false,
|
||||
"planned_command_or_evidence": "變更需列 deployment 或 maintenance window;本波不部署"
|
||||
}
|
||||
],
|
||||
"owner_response_requirements": [
|
||||
{
|
||||
"requirement_id": "business_owner_verdict",
|
||||
"owner_agent": "OpenClaw",
|
||||
"required_fields": [
|
||||
"owner",
|
||||
"decision",
|
||||
"business_impact",
|
||||
"risk_acceptance",
|
||||
"evidence_ref"
|
||||
],
|
||||
"required_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "sre_owner_readiness",
|
||||
"owner_agent": "OpenClaw",
|
||||
"required_fields": [
|
||||
"owner",
|
||||
"decision",
|
||||
"rollback_acceptance",
|
||||
"maintenance_window",
|
||||
"evidence_ref"
|
||||
],
|
||||
"required_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "security_owner_review",
|
||||
"owner_agent": "OpenClaw",
|
||||
"required_fields": [
|
||||
"owner",
|
||||
"decision",
|
||||
"risk_acceptance",
|
||||
"business_impact",
|
||||
"evidence_ref"
|
||||
],
|
||||
"required_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "product_owner_scope_response",
|
||||
"owner_agent": "Hermes",
|
||||
"required_fields": [
|
||||
"owner",
|
||||
"decision",
|
||||
"business_impact",
|
||||
"maintenance_window",
|
||||
"evidence_ref"
|
||||
],
|
||||
"required_before_pr_creation": true
|
||||
}
|
||||
],
|
||||
"rollback_requirements": [
|
||||
{
|
||||
"requirement_id": "git_revert_plan",
|
||||
"description": "必須列出可回退 commit 或檔案層 revert 計畫。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "config_recovery_plan",
|
||||
"description": "涉及 config / K8s / workflow 時必須列出原值與回復步驟。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "data_migration_rollback",
|
||||
"description": "涉及 schema / migration 時必須列出 forward-only 或 rollback policy。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "runtime_disable_switch",
|
||||
"description": "涉及 runtime gate 時必須列出 disable switch 與監控證據。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "telegram_noise_rollback",
|
||||
"description": "涉及告警或通知時必須列出降噪、停發與 fallback 策略。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
},
|
||||
{
|
||||
"requirement_id": "production_smoke_revert_gate",
|
||||
"description": "正式部署前必須定義 smoke 失敗時的停止與回退條件。",
|
||||
"required": true,
|
||||
"must_be_attached_before_pr_creation": true
|
||||
}
|
||||
],
|
||||
"draft_templates": [
|
||||
{
|
||||
"template_id": "dependency_update_draft",
|
||||
"display_name": "Dependency update draft PR template",
|
||||
"applies_to_group_ids": [
|
||||
"dependency_patch_low_risk",
|
||||
"security_cve_advisory"
|
||||
],
|
||||
"required_sections": [
|
||||
"變更摘要",
|
||||
"版本差異",
|
||||
"測試計畫",
|
||||
"Rollback",
|
||||
"Owner response"
|
||||
],
|
||||
"forbidden_fields": [
|
||||
"secret_value",
|
||||
"token",
|
||||
"authorization_header",
|
||||
"work_window_transcript",
|
||||
"codex_user_message",
|
||||
"prompt_text",
|
||||
"chain_of_thought",
|
||||
"session_id",
|
||||
"browser_context"
|
||||
],
|
||||
"automerge": false,
|
||||
"branch_push_allowed": false
|
||||
},
|
||||
{
|
||||
"template_id": "runtime_infra_draft",
|
||||
"display_name": "Runtime / infrastructure draft PR template",
|
||||
"applies_to_group_ids": [
|
||||
"container_image_digest_proposal",
|
||||
"host_k3s_stateful_maintenance_plan"
|
||||
],
|
||||
"required_sections": [
|
||||
"影響範圍",
|
||||
"部署或維護窗口",
|
||||
"監控與 smoke",
|
||||
"Rollback",
|
||||
"Owner response"
|
||||
],
|
||||
"forbidden_fields": [
|
||||
"secret_value",
|
||||
"token",
|
||||
"authorization_header",
|
||||
"work_window_transcript",
|
||||
"codex_user_message",
|
||||
"prompt_text",
|
||||
"chain_of_thought",
|
||||
"session_id",
|
||||
"browser_context"
|
||||
],
|
||||
"automerge": false,
|
||||
"branch_push_allowed": false
|
||||
},
|
||||
{
|
||||
"template_id": "ai_agent_contract_draft",
|
||||
"display_name": "AI Agent / model / tool contract draft template",
|
||||
"applies_to_group_ids": [
|
||||
"ai_agent_model_tool_contract_change"
|
||||
],
|
||||
"required_sections": [
|
||||
"候選 Agent 或模型",
|
||||
"Replay / shadow gate",
|
||||
"安全邊界",
|
||||
"Rollback",
|
||||
"Owner response"
|
||||
],
|
||||
"forbidden_fields": [
|
||||
"secret_value",
|
||||
"token",
|
||||
"authorization_header",
|
||||
"work_window_transcript",
|
||||
"codex_user_message",
|
||||
"prompt_text",
|
||||
"chain_of_thought",
|
||||
"session_id",
|
||||
"browser_context"
|
||||
],
|
||||
"automerge": false,
|
||||
"branch_push_allowed": false
|
||||
},
|
||||
{
|
||||
"template_id": "docs_runbook_km_draft",
|
||||
"display_name": "Docs / runbook / KM draft template",
|
||||
"applies_to_group_ids": [
|
||||
"docs_runbook_km_update"
|
||||
],
|
||||
"required_sections": [
|
||||
"文件目的",
|
||||
"證據來源",
|
||||
"使用者影響",
|
||||
"Rollback",
|
||||
"Owner response"
|
||||
],
|
||||
"forbidden_fields": [
|
||||
"secret_value",
|
||||
"token",
|
||||
"authorization_header",
|
||||
"work_window_transcript",
|
||||
"codex_user_message",
|
||||
"prompt_text",
|
||||
"chain_of_thought",
|
||||
"session_id",
|
||||
"browser_context"
|
||||
],
|
||||
"automerge": false,
|
||||
"branch_push_allowed": false
|
||||
}
|
||||
],
|
||||
"agent_roles": [
|
||||
{
|
||||
"agent_id": "hermes",
|
||||
"role": "建立 PR 草案封包、分組、release note、test plan 與 docs / KM 草稿。",
|
||||
"allowed_now": [
|
||||
"讀取 committed evidence",
|
||||
"產出 draft_pr_packet",
|
||||
"產出 grouping_decision"
|
||||
],
|
||||
"blocked_until_approval": [
|
||||
"push branch",
|
||||
"create Gitea PR",
|
||||
"trigger workflow",
|
||||
"modify lockfile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent_id": "openclaw",
|
||||
"role": "仲裁風險、rollback、owner response、HITL 與是否拆分 PR。",
|
||||
"allowed_now": [
|
||||
"審核 draft packet",
|
||||
"標註 blocker",
|
||||
"要求 owner response"
|
||||
],
|
||||
"blocked_until_approval": [
|
||||
"approve itself",
|
||||
"merge PR",
|
||||
"runtime execution",
|
||||
"production route change"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent_id": "nemotron",
|
||||
"role": "針對 AI Agent / model / prompt / tool contract 類變更提出 sanitized replay gate。",
|
||||
"allowed_now": [
|
||||
"定義 replay requirement",
|
||||
"檢查 output contract",
|
||||
"標記 model/tool risk"
|
||||
],
|
||||
"blocked_until_approval": [
|
||||
"call paid API",
|
||||
"run external replay",
|
||||
"shadow/canary",
|
||||
"production route"
|
||||
]
|
||||
}
|
||||
],
|
||||
"display_redaction_contract": {
|
||||
"conversation_transcript_display_allowed": false,
|
||||
"redaction_required": true,
|
||||
"allowed_frontend_fields": [
|
||||
"整體完成度",
|
||||
"目前任務",
|
||||
"下一任務",
|
||||
"grouping rule 摘要",
|
||||
"required checks 摘要",
|
||||
"rollback / owner response 邊界"
|
||||
],
|
||||
"forbidden_frontend_content": [
|
||||
"工作視窗對話內容",
|
||||
"secret 明文",
|
||||
"prompt 全文",
|
||||
"chain-of-thought",
|
||||
"browser session context"
|
||||
]
|
||||
},
|
||||
"operation_boundaries": {
|
||||
"read_only_lane_allowed": true,
|
||||
"gitea_branch_push_allowed": false,
|
||||
"gitea_pr_creation_allowed": false,
|
||||
"gitea_pr_update_allowed": false,
|
||||
"gitea_pr_comment_allowed": false,
|
||||
"auto_merge_allowed": false,
|
||||
"workflow_trigger_allowed": false,
|
||||
"ci_workflow_change_allowed": false,
|
||||
"lockfile_write_allowed": false,
|
||||
"package_upgrade_allowed": false,
|
||||
"file_mutation_allowed": false,
|
||||
"external_registry_lookup_allowed": false,
|
||||
"vulnerability_database_download_allowed": false,
|
||||
"docker_build_allowed": false,
|
||||
"image_pull_allowed": false,
|
||||
"production_route_change_allowed": false,
|
||||
"telegram_direct_send_allowed": false,
|
||||
"telegram_gateway_queue_write_allowed": false,
|
||||
"secret_plaintext_allowed": false,
|
||||
"conversation_transcript_allowed": false
|
||||
},
|
||||
"approval_boundaries": {
|
||||
"gitea_bot_account_approved": false,
|
||||
"gitea_branch_policy_approved": false,
|
||||
"gitea_pr_creation_approved": false,
|
||||
"gitea_pr_update_approved": false,
|
||||
"workflow_trigger_approved": false,
|
||||
"ci_workflow_change_approved": false,
|
||||
"lockfile_write_approved": false,
|
||||
"package_upgrade_approved": false,
|
||||
"docker_build_approved": false,
|
||||
"image_pull_approved": false,
|
||||
"auto_merge_approved": false,
|
||||
"telegram_digest_send_approved": false,
|
||||
"runtime_execution_approved": false,
|
||||
"production_route_change_approved": false,
|
||||
"secret_plaintext_approved": false
|
||||
},
|
||||
"rollups": {
|
||||
"grouping_rule_count": 6,
|
||||
"lane_step_count": 7,
|
||||
"required_check_count": 9,
|
||||
"owner_response_requirement_count": 4,
|
||||
"rollback_requirement_count": 6,
|
||||
"draft_template_count": 4,
|
||||
"draft_group_ids": [
|
||||
"dependency_patch_low_risk",
|
||||
"security_cve_advisory",
|
||||
"container_image_digest_proposal",
|
||||
"ai_agent_model_tool_contract_change",
|
||||
"host_k3s_stateful_maintenance_plan",
|
||||
"docs_runbook_km_update"
|
||||
],
|
||||
"owner_response_requirement_ids": [
|
||||
"business_owner_verdict",
|
||||
"sre_owner_readiness",
|
||||
"security_owner_review",
|
||||
"product_owner_scope_response"
|
||||
],
|
||||
"critical_risk_group_ids": [
|
||||
"ai_agent_model_tool_contract_change",
|
||||
"host_k3s_stateful_maintenance_plan"
|
||||
],
|
||||
"gitea_branch_push_allowed_count": 0,
|
||||
"gitea_pr_creation_allowed_count": 0,
|
||||
"auto_merge_allowed_count": 0,
|
||||
"workflow_trigger_allowed_count": 0,
|
||||
"lockfile_write_allowed_count": 0,
|
||||
"telegram_direct_send_allowed_count": 0,
|
||||
"conversation_transcript_allowed_count": 0,
|
||||
"next_approval_task_ids": [
|
||||
"P2-402F",
|
||||
"P2-402G"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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": 68,
|
||||
"overall_completion_percent": 78,
|
||||
"current_priority": "P2",
|
||||
"current_task_id": "P2-402D",
|
||||
"next_task_id": "P2-402E",
|
||||
"current_task_id": "P2-402E",
|
||||
"next_task_id": "P2-402F",
|
||||
"read_only_mode": true,
|
||||
"runtime_authority": "contract_only_no_version_or_runtime_update",
|
||||
"status_note": "P2-402D 已建立 Telegram action-required digest policy、schema、snapshot、只讀 API 與測試;本波仍不送 Telegram、不寫 Gateway queue、不改 Alertmanager route / receiver、不觸發 workflow、不執行 runtime。"
|
||||
"status_note": "P2-402E 已建立 Gitea PR 草案 lane、schema、snapshot、只讀 API 與測試;本波仍不 push branch、不建立或更新 Gitea PR、不觸發 workflow、不改 CI、不寫 lockfile、不升級套件、不發 Telegram。"
|
||||
},
|
||||
"external_source_evidence": [
|
||||
{
|
||||
@@ -637,16 +637,16 @@
|
||||
"completion_percent": 100,
|
||||
"owner_agent": "OpenClaw",
|
||||
"summary": "建立 Telegram action-required digest policy、schema、snapshot、只讀 API 與測試;定義 critical / action-required / failure-only digest 草案、成功降噪、redaction 與 fallback gap 邊界。",
|
||||
"next_gate": "P2-402E_gitea_pr_draft_lane"
|
||||
"next_gate": "P2-402E_completed"
|
||||
},
|
||||
{
|
||||
"task_id": "P2-402E",
|
||||
"priority": "P2",
|
||||
"status": "planned",
|
||||
"completion_percent": 0,
|
||||
"status": "done",
|
||||
"completion_percent": 100,
|
||||
"owner_agent": "Hermes",
|
||||
"summary": "設計 Gitea PR 草案 lane:grouping、automerge=false、tests、rollback、owner response。",
|
||||
"next_gate": "gitea_bot_and_branch_policy_approval_required"
|
||||
"summary": "建立 Gitea PR 草案 lane、schema、snapshot、只讀 API 與測試;定義 grouping、automerge=false、測試證據、rollback、owner response 與 redaction policy。",
|
||||
"next_gate": "P2-402F_host_stateful_version_inventory"
|
||||
},
|
||||
{
|
||||
"task_id": "P2-402F",
|
||||
|
||||
438
docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json
Normal file
438
docs/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json
Normal file
@@ -0,0 +1,438 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://awoooi.wooo.work/schemas/ai_agent_gitea_pr_draft_lane_v1.schema.json",
|
||||
"title": "AI Agent Gitea PR Draft Lane v1",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"generated_at",
|
||||
"program_status",
|
||||
"source_refs",
|
||||
"lane_intent",
|
||||
"branch_strategy",
|
||||
"grouping_rules",
|
||||
"lane_steps",
|
||||
"required_checks",
|
||||
"owner_response_requirements",
|
||||
"rollback_requirements",
|
||||
"draft_templates",
|
||||
"agent_roles",
|
||||
"display_redaction_contract",
|
||||
"operation_boundaries",
|
||||
"approval_boundaries",
|
||||
"rollups"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": "ai_agent_gitea_pr_draft_lane_v1"
|
||||
},
|
||||
"generated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"program_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"overall_completion_percent",
|
||||
"current_priority",
|
||||
"current_task_id",
|
||||
"next_task_id",
|
||||
"read_only_mode",
|
||||
"runtime_authority",
|
||||
"status_note"
|
||||
],
|
||||
"properties": {
|
||||
"overall_completion_percent": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"current_priority": {
|
||||
"type": "string"
|
||||
},
|
||||
"current_task_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"next_task_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"read_only_mode": {
|
||||
"const": true
|
||||
},
|
||||
"runtime_authority": {
|
||||
"const": "draft_lane_only_no_pr_creation_or_branch_push"
|
||||
},
|
||||
"status_note": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"source_refs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"lane_intent": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"purpose",
|
||||
"non_goals",
|
||||
"human_approval_gate"
|
||||
],
|
||||
"properties": {
|
||||
"purpose": {
|
||||
"type": "string"
|
||||
},
|
||||
"non_goals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human_approval_gate": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"branch_strategy": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"draft_branch_prefix",
|
||||
"branch_name_pattern",
|
||||
"branch_push_allowed",
|
||||
"pr_creation_allowed",
|
||||
"automerge",
|
||||
"required_prefix_after_approval"
|
||||
],
|
||||
"properties": {
|
||||
"draft_branch_prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"branch_name_pattern": {
|
||||
"type": "string"
|
||||
},
|
||||
"branch_push_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"pr_creation_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"automerge": {
|
||||
"const": false
|
||||
},
|
||||
"required_prefix_after_approval": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grouping_rules": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"group_id",
|
||||
"display_name",
|
||||
"owner_agent",
|
||||
"risk_tier",
|
||||
"max_batch_size",
|
||||
"draft_only",
|
||||
"automerge",
|
||||
"requires_openclaw_review",
|
||||
"rollback_required",
|
||||
"required_check_ids",
|
||||
"allowed_change_kinds",
|
||||
"blocked_change_kinds"
|
||||
],
|
||||
"properties": {
|
||||
"group_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"risk_tier": {
|
||||
"type": "string"
|
||||
},
|
||||
"max_batch_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"draft_only": {
|
||||
"const": true
|
||||
},
|
||||
"automerge": {
|
||||
"const": false
|
||||
},
|
||||
"requires_openclaw_review": {
|
||||
"const": true
|
||||
},
|
||||
"rollback_required": {
|
||||
"const": true
|
||||
},
|
||||
"required_check_ids": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"allowed_change_kinds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"blocked_change_kinds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lane_steps": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"step_id",
|
||||
"owner_agent",
|
||||
"purpose",
|
||||
"planned_output",
|
||||
"runtime_execution_allowed",
|
||||
"repo_write_allowed",
|
||||
"approval_gate"
|
||||
],
|
||||
"properties": {
|
||||
"step_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"purpose": {
|
||||
"type": "string"
|
||||
},
|
||||
"planned_output": {
|
||||
"type": "string"
|
||||
},
|
||||
"runtime_execution_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"repo_write_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"approval_gate": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required_checks": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"check_id",
|
||||
"display_name",
|
||||
"owner_agent",
|
||||
"blocking",
|
||||
"evidence_required",
|
||||
"run_now_allowed",
|
||||
"planned_command_or_evidence"
|
||||
],
|
||||
"properties": {
|
||||
"check_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"blocking": {
|
||||
"const": true
|
||||
},
|
||||
"evidence_required": {
|
||||
"const": true
|
||||
},
|
||||
"run_now_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"planned_command_or_evidence": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"owner_response_requirements": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"requirement_id",
|
||||
"owner_agent",
|
||||
"required_fields",
|
||||
"required_before_pr_creation"
|
||||
],
|
||||
"properties": {
|
||||
"requirement_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"required_fields": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required_before_pr_creation": {
|
||||
"const": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rollback_requirements": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"requirement_id",
|
||||
"description",
|
||||
"required",
|
||||
"must_be_attached_before_pr_creation"
|
||||
],
|
||||
"properties": {
|
||||
"requirement_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"const": true
|
||||
},
|
||||
"must_be_attached_before_pr_creation": {
|
||||
"const": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"draft_templates": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"template_id",
|
||||
"display_name",
|
||||
"applies_to_group_ids",
|
||||
"required_sections",
|
||||
"forbidden_fields",
|
||||
"automerge",
|
||||
"branch_push_allowed"
|
||||
],
|
||||
"properties": {
|
||||
"template_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"applies_to_group_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required_sections": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"forbidden_fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"automerge": {
|
||||
"const": false
|
||||
},
|
||||
"branch_push_allowed": {
|
||||
"const": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agent_roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"display_redaction_contract": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"conversation_transcript_display_allowed",
|
||||
"redaction_required",
|
||||
"allowed_frontend_fields",
|
||||
"forbidden_frontend_content"
|
||||
],
|
||||
"properties": {
|
||||
"conversation_transcript_display_allowed": {
|
||||
"const": false
|
||||
},
|
||||
"redaction_required": {
|
||||
"const": true
|
||||
},
|
||||
"allowed_frontend_fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"forbidden_frontend_content": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"operation_boundaries": {
|
||||
"type": "object"
|
||||
},
|
||||
"approval_boundaries": {
|
||||
"type": "object"
|
||||
},
|
||||
"rollups": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;完成度 `68%` |
|
||||
| `docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json` | 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory;完成度 `78%` |
|
||||
| `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 |
|
||||
@@ -682,14 +682,17 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
|
||||
| `docs/schemas/ai_agent_telegram_action_required_digest_policy_v1.schema.json` | P2-402D Telegram action-required digest policy schema;鎖定 direct send / Gateway queue write / route change / receiver change / workflow / transcript 全部 false |
|
||||
| `docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-11.json` | P2-402D committed snapshot:8 條 digest rule、3 個 channel 草案、5 類 trigger category、成功降噪、failure-only 與 redaction policy |
|
||||
| `GET /api/v1/agents/agent-telegram-action-required-digest-policy` | 只讀 API;只回傳 digest policy,不送 Telegram、不寫 queue、不改 route / receiver、不觸發 workflow |
|
||||
| `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 |
|
||||
|
||||
**採用順序:**
|
||||
|
||||
1. 先做 repo-only daily freshness:manifest / lockfile / Dockerfile / K8s YAML / runbook / snapshot。✅ P2-402B 已建立 committed snapshot/API;每日排程仍未授權。
|
||||
2. 再評估 external primary source weekly watch:Renovate、OSV-Scanner、Trivy、Syft、Grype、Kubernetes skew policy、Docker Scout。✅ P2-402C 已建立工具採用批准包;工具安裝、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。
|
||||
5. 最後才進人工批准後的 dry-run / smoke / canary / production rollout。
|
||||
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 版本只讀盤點。
|
||||
|
||||
#### 3.2.2 核心缺口與災難場景
|
||||
|
||||
@@ -1327,6 +1330,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence
|
||||
| Repo-only 版本新鮮度快照 | `docs/evaluations/ai_agent_version_freshness_snapshot_2026-06-11.json` + `GET /api/v1/agents/agent-version-freshness-snapshot` | P2-402B:讓 Hermes 可每日產生 repo-only freshness evidence packet;目前只讀 API,不啟用 schedule、不查外部、不升級 | L4×D2 / L7×D4 / L6×D6 |
|
||||
| 工具採用批准包 | `docs/evaluations/ai_agent_tool_adoption_approval_package_2026-06-11.json` + `GET /api/v1/agents/agent-tool-adoption-approval-package` | P2-402C:讓 OpenClaw / Hermes / NemoTron 用官方來源評估 Renovate / OSV-Scanner / Trivy / Syft / Grype;目前只讀 API,不安裝工具、不改 CI、不查外部、不建 PR、不發 Telegram | L4×D2 / L7×D4 / L6×D6 |
|
||||
| Telegram action-required digest policy | `docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-11.json` + `GET /api/v1/agents/agent-telegram-action-required-digest-policy` | P2-402D:讓 OpenClaw / Hermes / NemoTron 先定義 critical / action-required / failure-only digest 與成功降噪;目前只讀 API,不送 Telegram、不寫 queue、不改 route / receiver | L4×D2 / L7×D4 / L6×D6 |
|
||||
| Gitea PR 草案 lane | `docs/evaluations/ai_agent_gitea_pr_draft_lane_2026-06-11.json` + `GET /api/v1/agents/agent-gitea-pr-draft-lane` | P2-402E:讓 Hermes / OpenClaw / NemoTron 先定義 grouping、automerge=false、測試證據、rollback 與 owner response;目前只讀 API,不 push branch、不建立 PR、不觸發 workflow | L4×D2 / L7×D4 / L6×D6 |
|
||||
|
||||
**退出條件(量化)**
|
||||
|
||||
@@ -1716,6 +1720,12 @@ Phase 6 完成後
|
||||
- 更新 `ai_agent_proactive_operations_contract_2026-06-11.json`:整體完成度 `68%`,current task `P2-402D`,next task `P2-402E`。
|
||||
- 本波仍不送 Telegram、不寫 Telegram Gateway queue、不改 Alertmanager route / receiver、不寫 AwoooP event、不觸發 workflow、不查外部掃描、不執行 runtime、不讀取或輸出 secret、不回傳工作視窗對話內容;P2-402E 才設計 Gitea PR 草案 lane。
|
||||
|
||||
### 2026-06-11 23:59 (台北) — §3.2 / §5 — 完成 P2-402E Gitea PR 草案 lane — 回應統帥要求讓 Agent 主動產生版本更新與工具採用 PR 草案但先走批准制
|
||||
|
||||
- 新增 `ai_agent_gitea_pr_draft_lane_v1` schema / committed snapshot / loader / API / 測試,定義 6 條 grouping rule、7 個 lane step、9 個 required check、6 條 rollback requirement、4 個 draft template、owner response 與 redaction policy。
|
||||
- 更新 `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-04-15 (台北) — 全檔 — 建立 v2 骨架,§0/§1 完成 — 統帥批准「單 MASTER + 4 道閘門」結構
|
||||
|
||||
- 從 v1(plans/2026-04-15-MASTER-ai-autonomous-flywheel.md)繼承核心發現
|
||||
|
||||
Reference in New Issue
Block a user