feat: Agentic QE Fleet v2.0.0 - 14-agent quality engineering platform
- 14 specialized AI agents across 5 battle zones (Prepare/Analyze/Design/Review/Monitor) - New: risk-assessor, test-strategist, data-builder, coverage-auditor, quality-gatekeeper, execution-analyst, knowledge-curator - New: fleet_runner.py orchestrator with multi-zone manifest pipeline - New: fleet_config.yml for centralized configuration - New: knowledge activation system (keyword + semantic matching) - New: semantic conflict detection with severity grading (P0-P3) - New: three-tier quality gate (PASS/PASS_WITH_FIX/BLOCKED) - New: monitor zone for test execution analysis and auto knowledge curation - Backward compatible: /case_generate alias, case_pipeline.py preserved - Comprehensive docs: USER_GUIDE.md + MAINTENANCE_GUIDE.md
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
WORKFLOW_TRIGGER_PATHS = {
|
||||
"AGENTS.md",
|
||||
"README.md",
|
||||
"操作手册.md",
|
||||
"scripts/case_pipeline.py",
|
||||
"scripts/export_excel.py",
|
||||
"scripts/governance_audit.py",
|
||||
"knowledge_base/01_standards/test_case_template.md",
|
||||
"knowledge_base/01_standards/definition_of_done.md",
|
||||
"knowledge_base/01_standards/review_checklist.md",
|
||||
"knowledge_base/01_standards/terminology.md",
|
||||
"knowledge_base/01_standards/terminology_optional_saas.md",
|
||||
}
|
||||
WORKFLOW_TRIGGER_PREFIXES = (
|
||||
".claude/",
|
||||
"agents/",
|
||||
"scripts/",
|
||||
"knowledge_base/01_standards/",
|
||||
)
|
||||
STRUCTURE_TRIGGER_PREFIXES = (
|
||||
".claude/",
|
||||
"agents/",
|
||||
"scripts/",
|
||||
"knowledge_base/01_standards/",
|
||||
)
|
||||
IGNORED_PREFIXES = (
|
||||
"output/",
|
||||
"requirements/",
|
||||
"knowledge_base/02_history/",
|
||||
"knowledge_base/03_best_practices/",
|
||||
)
|
||||
|
||||
CONSISTENCY_EXPECTATIONS = {
|
||||
"AGENTS.md": [
|
||||
"python3 scripts/case_pipeline.py prepare --requirement",
|
||||
"python3 scripts/case_pipeline.py verify --requirement",
|
||||
"python3 scripts/case_pipeline.py export --requirement",
|
||||
"output/manifests/{BASE_NAME}.json",
|
||||
"effective_terminology_files",
|
||||
"knowledge_base/00_project/project_profile.md",
|
||||
"knowledge_base/01_standards/review_checklist.md",
|
||||
"normalized_requirement_file",
|
||||
],
|
||||
".claude/commands/case_generate.md": [
|
||||
"python3 scripts/case_pipeline.py prepare --requirement",
|
||||
"python3 scripts/case_pipeline.py verify --requirement",
|
||||
"python3 scripts/case_pipeline.py export --requirement",
|
||||
"output/manifests/{BASE_NAME}.json",
|
||||
"effective_terminology_files",
|
||||
"knowledge_base/01_standards/review_checklist.md",
|
||||
"normalized_requirement_file",
|
||||
],
|
||||
".claude/skills/case_generate/SKILL.md": [
|
||||
"python3 scripts/case_pipeline.py prepare --requirement",
|
||||
"python3 scripts/case_pipeline.py verify --requirement",
|
||||
"python3 scripts/case_pipeline.py export --requirement",
|
||||
"output/manifests/{BASE_NAME}.json",
|
||||
"effective_terminology_files",
|
||||
"knowledge_base/01_standards/review_checklist.md",
|
||||
"normalized_requirement_file",
|
||||
],
|
||||
".claude/instructions.md": [
|
||||
"scripts/case_pipeline.py",
|
||||
"effective_terminology_files",
|
||||
"knowledge_base/00_project/project_profile.md",
|
||||
"knowledge_base/01_standards/review_checklist.md",
|
||||
"normalized_requirement_file",
|
||||
],
|
||||
"README.md": [
|
||||
"output/manifests/{BASE_NAME}.json",
|
||||
"术语文件按需求内容自动识别生效范围",
|
||||
"knowledge_base/00_project/project_profile.md",
|
||||
"review_checklist.md",
|
||||
"source_docs/requirements_raw",
|
||||
"output/versions/{BASE_NAME}/v1/",
|
||||
"VERSION_INDEX.md",
|
||||
"最近 `3` 个版本",
|
||||
],
|
||||
"操作手册.md": [
|
||||
"terminology_optional_saas.md",
|
||||
"自动识别",
|
||||
"project_profile.md",
|
||||
"review_checklist.md",
|
||||
"source_docs/",
|
||||
"output/versions/{BASE_NAME}/v1/",
|
||||
"snapshot_meta.json",
|
||||
"最近 `3` 个版本",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def read_text(relative_path: str) -> str:
|
||||
return (REPO_ROOT / relative_path).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def git_status_paths() -> list[tuple[str, str]]:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "status", "--porcelain"],
|
||||
cwd=REPO_ROOT,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
raise RuntimeError(exc.stderr.strip() or "无法读取 git status。") from exc
|
||||
|
||||
items: list[tuple[str, str]] = []
|
||||
for raw_line in result.stdout.splitlines():
|
||||
if not raw_line:
|
||||
continue
|
||||
status = raw_line[:2]
|
||||
payload = raw_line[3:]
|
||||
path = payload.split(" -> ")[-1].strip()
|
||||
if path:
|
||||
items.append((status, path))
|
||||
return items
|
||||
|
||||
|
||||
def matches_any_prefix(path: str, prefixes: tuple[str, ...]) -> bool:
|
||||
return any(path.startswith(prefix) for prefix in prefixes)
|
||||
|
||||
|
||||
def should_ignore(path: str) -> bool:
|
||||
return matches_any_prefix(path, IGNORED_PREFIXES)
|
||||
|
||||
|
||||
def is_workflow_trigger(path: str) -> bool:
|
||||
return path in WORKFLOW_TRIGGER_PATHS or matches_any_prefix(path, WORKFLOW_TRIGGER_PREFIXES)
|
||||
|
||||
|
||||
def is_structure_trigger(path: str, status: str) -> bool:
|
||||
if "?" in status or "A" in status or "D" in status or "R" in status:
|
||||
return matches_any_prefix(path, STRUCTURE_TRIGGER_PREFIXES)
|
||||
return False
|
||||
|
||||
|
||||
def classify_changed_files(items: list[tuple[str, str]]) -> dict[str, list[str]]:
|
||||
workflow_hits: list[str] = []
|
||||
structure_hits: list[str] = []
|
||||
ignored_hits: list[str] = []
|
||||
|
||||
for status, path in items:
|
||||
if should_ignore(path):
|
||||
ignored_hits.append(path)
|
||||
continue
|
||||
if is_workflow_trigger(path):
|
||||
workflow_hits.append(path)
|
||||
if is_structure_trigger(path, status):
|
||||
structure_hits.append(path)
|
||||
|
||||
return {
|
||||
"workflow_hits": sorted(set(workflow_hits)),
|
||||
"structure_hits": sorted(set(structure_hits)),
|
||||
"ignored_hits": sorted(set(ignored_hits)),
|
||||
}
|
||||
|
||||
|
||||
def audit_consistency() -> list[str]:
|
||||
issues: list[str] = []
|
||||
|
||||
for relative_path, snippets in CONSISTENCY_EXPECTATIONS.items():
|
||||
content = read_text(relative_path)
|
||||
for snippet in snippets:
|
||||
if snippet not in content:
|
||||
issues.append(f"{relative_path} 缺少关键片段: {snippet}")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def print_trigger_report(classification: dict[str, list[str]]) -> bool:
|
||||
workflow_hits = classification["workflow_hits"]
|
||||
structure_hits = classification["structure_hits"]
|
||||
should_run = bool(workflow_hits or structure_hits)
|
||||
|
||||
print(f"should_run={str(should_run).lower()}")
|
||||
if workflow_hits:
|
||||
print("workflow_hits:")
|
||||
for path in workflow_hits:
|
||||
print(f" - {path}")
|
||||
if structure_hits:
|
||||
print("structure_hits:")
|
||||
for path in structure_hits:
|
||||
print(f" - {path}")
|
||||
return should_run
|
||||
|
||||
|
||||
def command_should_run() -> int:
|
||||
classification = classify_changed_files(git_status_paths())
|
||||
should_run = print_trigger_report(classification)
|
||||
return 0 if should_run else 1
|
||||
|
||||
|
||||
def command_audit() -> int:
|
||||
issues = audit_consistency()
|
||||
if issues:
|
||||
print("audit_status=fail")
|
||||
for issue in issues:
|
||||
print(f"- {issue}")
|
||||
return 1
|
||||
|
||||
print("audit_status=pass")
|
||||
return 0
|
||||
|
||||
|
||||
def command_auto() -> int:
|
||||
classification = classify_changed_files(git_status_paths())
|
||||
should_run = print_trigger_report(classification)
|
||||
if not should_run:
|
||||
print("audit_skipped=true")
|
||||
return 0
|
||||
|
||||
issues = audit_consistency()
|
||||
if issues:
|
||||
print("audit_status=fail")
|
||||
for issue in issues:
|
||||
print(f"- {issue}")
|
||||
return 1
|
||||
|
||||
print("audit_status=pass")
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="流程一致性与文档滞后审计。")
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
subparsers.add_parser("should-run", help="根据当前 git 变更判断是否需要触发治理审计。")
|
||||
subparsers.add_parser("audit", help="执行一致性与文档滞后检查。")
|
||||
subparsers.add_parser("auto", help="自动判断是否触发,并在需要时执行审计。")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "should-run":
|
||||
return command_should_run()
|
||||
if args.command == "audit":
|
||||
return command_audit()
|
||||
if args.command == "auto":
|
||||
return command_auto()
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user