feat(governance): 新增 Agent Gitea PR 草案 lane
All checks were successful
CD Pipeline / tests (push) Successful in 1m32s
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / build-and-deploy (push) Successful in 6m10s
CD Pipeline / post-deploy-checks (push) Successful in 1m38s

This commit is contained in:
Your Name
2026-06-11 15:09:39 +08:00
parent cd92885277
commit 4da7f2c506
16 changed files with 1779 additions and 27 deletions

View 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()))

View 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

View File

@@ -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

View File

@@ -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