扩展氢能站点、加氢记录与台账原型链路,新增工作台、车辆资产 H5、自营物流等原型,并同步导航注册、PRD 资源与交付技能。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-07-21 17:15:39 +08:00
parent e39df1c7c8
commit a01d2ab708
242 changed files with 102296 additions and 4866 deletions

View File

@@ -0,0 +1,536 @@
#!/usr/bin/env python3
"""Validate a project-scoped Yunxiao lifecycle profile and render an execution plan."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
REQUIRED_LABELS = {"分析", "设计", "开发", "测试"}
REQUIRED_RULE_IDS = {f"R{index:02d}" for index in range(1, 13)}
REQUIRED_TASK_RULE_IDS = {f"TK{index:02d}" for index in range(1, 9)}
REQUIRED_CONTROL_IDS = {
*REQUIRED_RULE_IDS,
*REQUIRED_TASK_RULE_IDS,
*(f"TC{index:02d}" for index in range(1, 4)),
*(f"DF{index:02d}" for index in range(1, 4)),
*(f"CI{index:02d}" for index in range(1, 4)),
"L01",
"L02",
}
VALID_MODES = {
"manual",
"native_rule",
"integration_bridge",
"disabled_legacy",
"not_supported",
}
VALID_RELEASE_EVIDENCE_MODES = {
"native_release_change",
"pipeline_callback",
"manual_release_confirmation",
}
SENSITIVE_FRAGMENTS = (
"password",
"passwd",
"secret",
"token",
"cookie",
"credential",
"private_key",
"otp",
"密码",
"密钥",
)
def load_profile(path: Path) -> dict[str, Any]:
with path.open("r", encoding="utf-8-sig") as handle:
data = json.load(handle)
if not isinstance(data, dict):
raise ValueError("配置根节点必须是 JSON 对象")
return data
def is_text(value: Any) -> bool:
return isinstance(value, str) and bool(value.strip())
def require_text(container: dict[str, Any], key: str, label: str, errors: list[str]) -> None:
if not is_text(container.get(key)):
errors.append(f"{label}.{key} 不能为空")
def walk_sensitive_keys(value: Any, prefix: str = "") -> list[str]:
found: list[str] = []
if isinstance(value, dict):
for key, nested in value.items():
current = f"{prefix}.{key}" if prefix else str(key)
lower = str(key).lower()
if any(fragment in lower for fragment in SENSITIVE_FRAGMENTS):
found.append(current)
found.extend(walk_sensitive_keys(nested, current))
elif isinstance(value, list):
for index, nested in enumerate(value):
found.extend(walk_sensitive_keys(nested, f"{prefix}[{index}]"))
return found
def validate_control_list(
controls: Any, label: str, errors: list[str]
) -> None:
if not isinstance(controls, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
for index, control in enumerate(controls):
item_label = f"{label}[{index}]"
if not isinstance(control, dict):
errors.append(f"{item_label} 必须是对象")
continue
control_id = control.get("id")
if not is_text(control_id):
errors.append(f"{item_label}.id 不能为空")
continue
if control_id in seen:
errors.append(f"{label} 控制项重复:{control_id}")
seen.add(control_id)
if control_id not in REQUIRED_CONTROL_IDS:
errors.append(f"{label} 未知控制项:{control_id}")
mode = control.get("mode")
if mode not in VALID_MODES:
errors.append(f"{item_label}.mode 不合法")
if not isinstance(control.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
for key in ("evidence", "owner", "fallback"):
require_text(control, key, item_label, errors)
if mode in {"disabled_legacy", "not_supported"} and control.get("enabled") is not False:
errors.append(f"{item_label}{mode} 模式要求 enabled=false")
if control_id in {"L01", "L02"} and mode not in {
"disabled_legacy",
"native_rule",
"manual",
}:
errors.append(f"{item_label} 必须明确为停用旧规则、获批原生规则或人工处置")
missing = sorted(REQUIRED_CONTROL_IDS - seen)
if missing:
errors.append(f"{label} 缺少控制项:" + "".join(missing))
def validate_rule_instances(
rules: Any, lifecycle: list[str], label: str, errors: list[str]
) -> None:
if not isinstance(rules, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
orders: set[int] = set()
expected = {
f"R{index + 1:02d}": (lifecycle[index], lifecycle[index + 1])
for index in range(12)
}
for index, rule in enumerate(rules):
item_label = f"{label}[{index}]"
if not isinstance(rule, dict):
errors.append(f"{item_label} 必须是对象")
continue
rule_id = rule.get("id")
if not is_text(rule_id):
errors.append(f"{item_label}.id 不能为空")
continue
if rule_id in seen:
errors.append(f"{label} 规则实例重复:{rule_id}")
seen.add(rule_id)
if rule_id not in REQUIRED_RULE_IDS:
errors.append(f"{label} 未知规则实例:{rule_id}")
mode = rule.get("mode")
if mode not in {"manual", "native_rule", "integration_bridge", "not_supported"}:
errors.append(f"{item_label}.mode 不合法")
for key in ("actual_rule_name", "trigger", "source_status", "target_status", "action", "evidence"):
require_text(rule, key, item_label, errors)
conditions = rule.get("conditions")
if not isinstance(conditions, list) or not conditions or not all(is_text(v) for v in conditions):
errors.append(f"{item_label}.conditions 必须是非空文本数组")
order = rule.get("order")
if not isinstance(order, int) or order < 1:
errors.append(f"{item_label}.order 必须是正整数")
elif order in orders:
errors.append(f"{label} 规则顺序重复:{order}")
else:
orders.add(order)
if not isinstance(rule.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
if rule.get("has_current_state_condition") is not True:
errors.append(f"{item_label}.has_current_state_condition 必须为 true")
if rule_id in expected:
expected_source, expected_target = expected[rule_id]
if rule.get("source_status") != expected_source:
errors.append(f"{item_label}.source_status 应为 {expected_source}")
if rule.get("target_status") != expected_target:
errors.append(f"{item_label}.target_status 应为 {expected_target}")
missing = sorted(REQUIRED_RULE_IDS - seen)
if missing:
errors.append(f"{label} 缺少规则实例:" + "".join(missing))
def validate_task_rule_instances(rules: Any, label: str, errors: list[str]) -> None:
if not isinstance(rules, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
orders: set[int] = set()
for index, rule in enumerate(rules):
item_label = f"{label}[{index}]"
if not isinstance(rule, dict):
errors.append(f"{item_label} 必须是对象")
continue
rule_id = rule.get("id")
if not is_text(rule_id):
errors.append(f"{item_label}.id 不能为空")
continue
if rule_id in seen:
errors.append(f"{label} 规则实例重复:{rule_id}")
seen.add(rule_id)
if rule_id not in REQUIRED_TASK_RULE_IDS:
errors.append(f"{label} 未知任务规则实例:{rule_id}")
if rule.get("mode") not in {"manual", "native_rule", "integration_bridge", "not_supported"}:
errors.append(f"{item_label}.mode 不合法")
for key in ("actual_rule_name", "trigger", "scope", "source_state", "evidence"):
require_text(rule, key, item_label, errors)
for key in ("conditions", "actions"):
values = rule.get(key)
if not isinstance(values, list) or not values or not all(is_text(value) for value in values):
errors.append(f"{item_label}.{key} 必须是非空文本数组")
order = rule.get("order")
if not isinstance(order, int) or order < 1:
errors.append(f"{item_label}.order 必须是正整数")
elif order in orders:
errors.append(f"{label} 规则顺序重复:{order}")
else:
orders.add(order)
if not isinstance(rule.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
missing = sorted(REQUIRED_TASK_RULE_IDS - seen)
if missing:
errors.append(f"{label} 缺少任务规则实例:" + "".join(missing))
def validate_project(
project: Any, index: int, lifecycle: list[str], errors: list[str]
) -> None:
label = f"projects[{index}]"
if not isinstance(project, dict):
errors.append(f"{label} 必须是对象")
return
for key in ("name", "work_item_type", "release_status", "final_status"):
require_text(project, key, label, errors)
if project.get("release_status") != lifecycle[11]:
errors.append(f"{label}.release_status 必须与 lifecycle[11] 一致")
if project.get("final_status") != lifecycle[12]:
errors.append(f"{label}.final_status 必须与 lifecycle[12] 一致")
labels = project.get("labels_observed")
if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)):
errors.append(f"{label}.labels_observed 必须包含:分析、设计、开发、测试")
repositories = project.get("repositories")
if not isinstance(repositories, list) or not repositories:
errors.append(f"{label}.repositories 至少包含一个仓库")
else:
for repo_index, repo in enumerate(repositories):
repo_label = f"{label}.repositories[{repo_index}]"
if not isinstance(repo, dict):
errors.append(f"{repo_label} 必须是对象")
continue
for key in ("name", "role", "integration_branch", "evidence"):
require_text(repo, key, repo_label, errors)
if repo.get("integration_branch") not in {"dev", "develop"}:
errors.append(f"{repo_label}.integration_branch 只能是实际存在的 dev/develop")
for key in ("codeup_integrated", "required_for_mr_gate"):
if not isinstance(repo.get(key), bool):
errors.append(f"{repo_label}.{key} 必须是布尔值")
pipeline = project.get("test_pipeline")
if not isinstance(pipeline, dict):
errors.append(f"{label}.test_pipeline 必须是对象")
else:
for key in ("name", "environment", "evidence"):
require_text(pipeline, key, f"{label}.test_pipeline", errors)
for key in (
"is_clone_for_callback_poc",
"original_pipeline_unchanged",
"docker_success_callback_enabled",
):
if not isinstance(pipeline.get(key), bool):
errors.append(f"{label}.test_pipeline.{key} 必须是布尔值")
security = pipeline.get("callback_security")
if not isinstance(security, dict):
errors.append(f"{label}.test_pipeline.callback_security 必须是对象")
else:
security_keys = (
"signed",
"timestamp_checked",
"replay_protected",
"idempotent_by_execution_id",
"failure_retry_tested",
)
for key in security_keys:
if not isinstance(security.get(key), bool):
errors.append(f"{label}.test_pipeline.callback_security.{key} 必须是布尔值")
if pipeline.get("docker_success_callback_enabled") is True:
if pipeline.get("is_clone_for_callback_poc") is not True:
errors.append(f"{label} 启用回调时必须使用test流水线副本")
if pipeline.get("original_pipeline_unchanged") is not True:
errors.append(f"{label} 启用回调时必须保持原流水线不变")
if any(security.get(key) is not True for key in security_keys):
errors.append(f"{label} 启用回调时必须完成全部安全与重试验证")
release = project.get("release_evidence")
if not isinstance(release, dict):
errors.append(f"{label}.release_evidence 必须是对象")
else:
if release.get("mode") not in VALID_RELEASE_EVIDENCE_MODES:
errors.append(f"{label}.release_evidence.mode 不合法")
for key in ("target_environment", "evidence"):
require_text(release, key, f"{label}.release_evidence", errors)
if release.get("test_success_is_production_release") is not False:
errors.append(f"{label}.release_evidence.test_success_is_production_release 必须为 false")
test_plan = project.get("test_plan")
if not isinstance(test_plan, dict):
errors.append(f"{label}.test_plan 必须是对象")
else:
for key in ("name", "evidence"):
require_text(test_plan, key, f"{label}.test_plan", errors)
if not isinstance(test_plan.get("requirement_formally_related"), bool):
errors.append(f"{label}.test_plan.requirement_formally_related 必须是布尔值")
states = test_plan.get("case_states_observed")
if not isinstance(states, list) or not states or not all(is_text(v) for v in states):
errors.append(f"{label}.test_plan.case_states_observed 必须是非空文本数组")
defect = project.get("defect_workflow")
if not isinstance(defect, dict):
errors.append(f"{label}.defect_workflow 必须是对象")
else:
for key in ("fixed_status", "closed_status", "reopen_status", "evidence"):
require_text(defect, key, f"{label}.defect_workflow", errors)
if defect.get("fixed_is_terminal") is not False:
errors.append(f"{label}.defect_workflow.fixed_is_terminal 必须为 false")
if defect.get("tester_retest_required") is not True:
errors.append(f"{label}.defect_workflow.tester_retest_required 必须为 true")
validate_rule_instances(project.get("rule_instances"), lifecycle, f"{label}.rule_instances", errors)
validate_task_rule_instances(
project.get("task_rule_instances"), f"{label}.task_rule_instances", errors
)
validate_control_list(project.get("control_coverage"), f"{label}.control_coverage", errors)
if not isinstance(project.get("adjunct_automations"), list):
errors.append(f"{label}.adjunct_automations 必须是数组,可为空")
def validate(data: dict[str, Any]) -> list[str]:
errors: list[str] = []
if data.get("schema_version") != 3:
errors.append("schema_version 必须为 3旧版台账不包含阶段任务闭环")
for key in ("organization", "work_item_type"):
require_text(data, key, "profile", errors)
lifecycle = data.get("lifecycle")
if not isinstance(lifecycle, list) or len(lifecycle) != 13 or not all(is_text(v) for v in lifecycle):
errors.append("lifecycle 必须包含 13 个非空有序状态")
lifecycle = [str(index) for index in range(13)]
elif len(set(lifecycle)) != len(lifecycle):
errors.append("lifecycle 状态名称不能重复")
labels = data.get("requirement_labels")
if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)):
errors.append("requirement_labels 必须包含:分析、设计、开发、测试")
policy = data.get("automation_policy")
if not isinstance(policy, dict):
errors.append("automation_policy 必须是对象")
else:
for key in ("require_current_state_condition", "inspect_rule_order_and_cascades"):
if policy.get(key) is not True:
errors.append(f"automation_policy.{key} 必须为 true")
for key in ("allow_automatic_final_close_without_acceptance", "zero_related_items_are_complete"):
if policy.get(key) is not False:
errors.append(f"automation_policy.{key} 必须为 false")
events = policy.get("development_start_event_candidates")
if not isinstance(events, list) or not events or not all(is_text(v) for v in events):
errors.append("automation_policy.development_start_event_candidates 不能为空")
if policy.get("require_code_assets_linked_to_requirement_and_development_task") is not True:
errors.append(
"automation_policy.require_code_assets_linked_to_requirement_and_development_task 必须为 true"
)
idempotency = policy.get("stage_task_creation_idempotency_key_components")
if not isinstance(idempotency, list) or not idempotency or not all(is_text(v) for v in idempotency):
errors.append("automation_policy.stage_task_creation_idempotency_key_components 不能为空")
test_policy = data.get("test_closure_policy")
if not isinstance(test_policy, dict):
errors.append("test_closure_policy 必须是对象")
else:
if test_policy.get("defect_fixed_is_terminal") is not False:
errors.append("test_closure_policy.defect_fixed_is_terminal 必须为 false")
if test_policy.get("require_tester_retest") is not True:
errors.append("test_closure_policy.require_tester_retest 必须为 true")
for key in ("accepted_case_states", "rejected_case_states"):
values = test_policy.get(key)
if not isinstance(values, list) or not values or not all(is_text(v) for v in values):
errors.append(f"test_closure_policy.{key} 不能为空")
projects = data.get("projects")
if not isinstance(projects, list) or not projects:
errors.append("projects 至少包含一个项目")
else:
names: set[str] = set()
for index, project in enumerate(projects):
validate_project(project, index, lifecycle, errors)
if isinstance(project, dict) and is_text(project.get("name")):
name = project["name"]
if name in names:
errors.append(f"项目名称重复:{name}")
names.add(name)
sensitive = walk_sensitive_keys(data)
if sensitive:
errors.append("配置中禁止出现敏感字段:" + "".join(sensitive))
if "请填写" in json.dumps(data, ensure_ascii=False):
errors.append("仍存在“请填写”占位符")
return errors
def render(data: dict[str, Any]) -> str:
lines = [
"# 云效需求生命周期自动化执行计划",
"",
f"- 组织:{data['organization']}",
f"- 工作项类型:{data['work_item_type']}",
f"- 项目数:{len(data['projects'])}",
"- 完整性口径每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。",
"- 安全边界:不修改现有生产流水线;不记录任何凭据或密钥。",
"",
]
for project in data["projects"]:
lines.extend([
f"## 项目:{project['name']}",
"",
f"- 发布状态:{project['release_status']}",
f"- 最终状态:{project['final_status']}",
f"- test流水线{project['test_pipeline']['name']}",
f"- 发布证据方式:{project['release_evidence']['mode']}",
"",
"### 仓库覆盖",
"",
"| 仓库 | 角色 | 集成分支 | Codeup已集成 | 纳入全部MR门禁 | 证据 |",
"|---|---|---|---|---|---|",
])
for repo in project["repositories"]:
lines.append(
f"| {repo['name']} | {repo['role']} | {repo['integration_branch']} | "
f"{'' if repo['codeup_integrated'] else ''} | "
f"{'' if repo['required_for_mr_gate'] else ''} | {repo['evidence']} |"
)
lines.extend([
"",
"### R01R12实际规则实例",
"",
"| ID | 实现 | 实际规则/门禁 | 触发 | 流转 | 条件 | 顺序 | 启用 | 证据 |",
"|---|---|---|---|---|---|---:|---|---|",
])
for rule in sorted(project["rule_instances"], key=lambda item: item["order"]):
conditions = "".join(rule["conditions"])
lines.append(
f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | "
f"{rule['source_status']}{rule['target_status']} | {conditions} | {rule['order']} | "
f"{'' if rule['enabled'] else ''} | {rule['evidence']} |"
)
lines.extend([
"",
"### TK01TK08任务规则实例",
"",
"| ID | 实现 | 实际规则/门禁 | 触发 | 作用域 | 源状态 | 条件 | 动作 | 顺序 | 启用 | 证据 |",
"|---|---|---|---|---|---|---|---|---:|---|---|",
])
for rule in sorted(project["task_rule_instances"], key=lambda item: item["order"]):
conditions = "".join(rule["conditions"])
actions = "".join(rule["actions"])
lines.append(
f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | "
f"{rule['scope']} | {rule['source_state']} | {conditions} | {actions} | {rule['order']} | "
f"{'' if rule['enabled'] else ''} | {rule['evidence']} |"
)
lines.extend([
"",
"### 31项完整性台账",
"",
"| 控制ID | 实现方式 | 启用 | 观察证据 | 负责人 | 回退方案 |",
"|---|---|---|---|---|---|",
])
for control in sorted(project["control_coverage"], key=lambda item: item["id"]):
lines.append(
f"| {control['id']} | {control['mode']} | {'' if control['enabled'] else ''} | "
f"{control['evidence']} | {control['owner']} | {control['fallback']} |"
)
adjunct = project["adjunct_automations"]
lines.extend([
"",
"### 范围外通用自动化盘点",
"",
"- " + ("".join(map(str, adjunct)) if adjunct else "未纳入如需通知、自动指派、SLA、字段同步等应单独授权和设计。"),
"",
])
lines.extend([
"## 执行检查",
"",
"- [ ] 两个或多个项目的规则实例、控制台账和证据已分别填写。",
"- [ ] 已逐条读取真实触发配置、规则顺序、启用状态和执行账号。",
"- [ ] 已检查后继规则、重复规则和跨阶段连锁流转。",
"- [ ] 已验证失败用例、缺陷已修复、复测通过/失败时双方状态一致。",
"- [ ] 已逐仓库确认真实dev/develop分支、正式关联和全部MR门禁。",
"- [ ] 已区分test部署、生产发布、发布变更和业务验收。",
"- [ ] 已执行正向、负向、异步、多仓库、失败重试和旧规则连锁测试。",
"",
])
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers(dest="command", required=True)
validate_parser = subparsers.add_parser("validate", help="验证项目配置")
validate_parser.add_argument("profile", type=Path)
render_parser = subparsers.add_parser("render", help="生成 Markdown 执行计划")
render_parser.add_argument("profile", type=Path)
render_parser.add_argument("--output", required=True, type=Path)
args = parser.parse_args()
try:
data = load_profile(args.profile)
errors = validate(data)
except (OSError, json.JSONDecodeError, ValueError) as error:
print(f"配置读取失败:{error}", file=sys.stderr)
return 2
if errors:
for error in errors:
print(f"[错误] {error}", file=sys.stderr)
return 1
if args.command == "validate":
print(
f"[通过] 配置有效:{len(data['projects'])} 个项目;"
"每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。"
)
return 0
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(render(data), encoding="utf-8")
print(f"[完成] 已生成执行计划:{args.output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())