auto-test-code
当用户明确要求"测试代码"、"运行代码审查"或"进行代码自检"时使用。通过多轮 A 轮批判性代码审查 + B 轮代码质量原则检查,系统化发现、记录、修复程序代码中的问题,并将计划/过程/结果统一沉淀到目标代码根目录的 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 隔离工作区。⚠️ 不适用:用户只是想优化功能(应直接修改)、只是询问代码问题(应直接回答)、没有明确"测试代码"意图。
#code-review
Install
npx skills add https://github.com/huangwb8/skills/tree/main/skills/alpha/auto-test-code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install huangwb8-skills@llmmart
git clone https://github.com/huangwb8/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole huangwb8/skills collection as a plugin from our marketplace. Git is the plain clone.
README
auto-test-code
本 README 面向使用者:如何触发并正确使用 auto-test-code skill。
执行指令与硬性规范在 SKILL.md;默认参数在 config.yaml。
用法
最推荐用法
用 auto-test-code 测试 /path/to/project,进行 2 轮 A 轮审查 + B 轮质量检查
其他常见场景
单轮快速审查
用 auto-test-code 测试 /path/to/project,只做 1 轮 A 轮审查
指定代码语言
用 auto-test-code 测试 /path/to/project,重点审查 Python 和 JavaScript 代码
指定深挖维度(全覆盖 + 重点深挖)
用 auto-test-code 测试 /path/to/project,本轮深挖并发安全和资源管理问题
设计理念
auto-test-code 是一个批判性思维驱动的代码自审查技能,核心价值在于:
- 独立评估模式:每轮审查都基于当前代码状态独立分析,避免确认偏差
- 批判性思维驱动:强制使用"刁钻角度"思考,发现深层系统性问题
- 可追溯文档:所有问题、修复、验证都固化为文档,可复现可复盘
与普通代码审查的区别:
| 维度 | 普通代码审查 | auto-test-code |
|---|---|---|
| 目标 | 发现表面问题(风格、规范) | 发现系统性问题(算法/边界/并发/安全/设计) |
| 方法 | 人工检查 + 主观判断 | 批判性思维框架 + 静态分析 + 动态推理 |
| 输出 | 口头建议 | 可追溯的文档 + 修复计划 + 验证报告 |
| 质量 | 无明确标准 | 强制数量要求(≥10 个问题)和质量门槛(P0+P1 ≥ 60%) |
功能概述
| 特性 | 说明 |
|---|---|
| 多轮 A 轮迭代 | 静态分析 → 动态推理 → 计划 → 优化 → 轻量测试(可重复 N 次) |
| 独立评估模式 | 每轮基于当前代码状态独立审查,不查看历史记录 |
| 批判性思维驱动 | 强制使用刁钻角度(空输入/超大输入/竞态条件/资源耗尽) |
| 强制质量要求 | 每轮至少 10 个问题,P0+P1 占比 ≥ 60%,系统性问题 ≥ 3 个 |
| B 轮质量检查 | 9 大维度代码质量原则检查(算法复杂度、边界覆盖、安全漏洞分类审查、设计质量等) |
| 可追溯文档 | 统一沉淀到 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/ 隔离工作区(REVIEW/PLAN/RUN/REPORT + artifacts),可复现可复盘 |
提示词示例
示例 1:完整审查流程(推荐)
你:用 auto-test-code 测试 /path/to/project,进行 3 轮 A 轮审查 + B 轮质量检查
技能:开始执行完整审查流程...
[A 轮 #1] 发现 15 个问题(P0: 3, P1: 8, P2: 4)
[A 轮 #2] 发现 12 个问题(P0: 2, P1: 7, P2: 3)
[A 轮 #3] 发现 10 个问题(P0: 1, P1: 6, P2: 3)
[B 轮] 完成 9 维度质量检查
产出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/v*/(含 REVIEW/PLAN/RUN/REPORT)+ .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-v*/
示例 2:单轮快速检查
你:用 auto-test-code 测试当前目录的代码,只做 1 轮审查
技能:执行单轮 A 轮审查...
发现 18 个问题(P0: 4, P1: 10, P2: 4)
产出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/
示例 3:指定深挖维度(全覆盖 + 重点深挖)
你:用 auto-test-code 测试 /path/to/project,深挖并发安全和资源管理问题
技能:本轮深挖维度:并发安全性、资源管理(其余维度仍全覆盖)
刁钻角度(用于深挖):竞态条件、死锁、文件描述符泄漏、内存泄漏
发现 15 个相关问题(P0: 5, P1: 7, P2: 3)
示例 4:结合特定代码语言
你:用 auto-test-code 测试 /path/to/project,重点审查 Python 代码
技能:扫描 *.py 文件...
发现 16 个问题(P0: 3, P1: 9, P2: 4)
聚焦:Python 特有问题(GIL、动态类型、资源管理)
隔离工作区
- 每次 skill 执行都会在目标项目根目录创建
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/作为当次隔离工作区。 - 所有计划、报告、日志、辅助脚本和中间产物都只写入该工作区,避免把 skill 文件泄露到源项目其他位置。
- 运行可能产生缓存或临时文件的命令时,优先将工作目录、
TMPDIR、XDG_CACHE_HOME、PYTHONPYCACHEPREFIX等重定向到当前工作区。 - 除了用户明确要求的源码修复外,
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/之外不应新增任何 auto-test-code 相关文件。
输出文件
技能执行后会在目标代码目录下的隔离工作区生成以下文件:
{项目根目录}/
└── tmp/
└── 2026-03-10-15-30/ # 本次技能执行的隔离工作区(示例)
├── .auto-test-code-run.json # 运行清单(记录 code_root / run_id / tests_dir)
└── tests/ # 会话目录(计划/过程/结果都在同一处)
├── v202602161028/ # A 轮会话(示例)
│ ├── REVIEW.md # 批判性审查(问题清单 + 改进计划)
│ ├── TEST_PLAN.md # 测试计划
│ ├── TEST_RUN.md # 测试过程(命令、关键输出摘录、决策)
│ ├── TEST_REPORT.md # 测试结果与证据
│ ├── _artifacts/ # 中间产物
│ └── _scripts/ # 会话内辅助脚本(可选)
└── b-v202602161028/ # B 轮会话(质量检查 + 验证)
└── ...
文件说明
| 文件 | 说明 |
|---|---|
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/v*/REVIEW.md |
A 轮批判性审查(问题清单 + 改进计划) |
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-v*/REVIEW.md |
B 轮代码质量检查报告(9 大维度评估) |
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_PLAN.md |
测试计划,列出本轮验证的修复点 |
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_RUN.md |
测试过程记录(命令、关键输出摘录、关键决策) |
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_REPORT.md |
测试报告,包含验证结果和证据 |
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/_artifacts/ |
中间产物(命令输出、日志、截图等) |
配置选项
在 config.yaml 中可调整以下参数:
轮次控制
| 参数 | 默认值 | 说明 |
|---|---|---|
test_rounds.default_a_rounds |
1 | 默认 A 轮次数 |
test_rounds.max_a_rounds |
10 | 最大 A 轮次数 |
test_rounds.min_suggestions_per_round |
10 | 每轮最少问题数 |
test_rounds.target_suggestions_range |
[15, 20] | 目标问题数范围 |
test_rounds.min_p0_p1_ratio |
60 | P0+P1 最小占比(%) |
A 轮审查范围
| 参数 | 默认值 | 说明 |
|---|---|---|
a_round_check.independent_review.enabled |
true | 独立评估模式开关 |
a_round_check.independent_review.scan_patterns |
["/*.py", "/*.js", ...] | 扫描文件模式 |
a_round_check.independent_review.exclude_patterns |
["tmp/", "node_modules/", ...] | 排除目录 |
B 轮检查维度
| 参数 | 默认值 | 说明 |
|---|---|---|
b_round_check.mandatory |
true | B 轮是否强制执行 |
b_round_check.min_suggestions |
10 | B 轮最少建议数 |
b_round_check.dimensions |
[9 大维度] | 检查维度列表 |
修改方式:编辑你的安装目录下的配置(Codex: ~/.codex/skills/auto-test-code/config.yaml;Claude Code: ~/.claude/skills/auto-test-code/config.yaml)。如需对单个项目做覆盖,可在目标项目根目录创建 .auto-test-code/config.yaml(仅覆盖 directories.tmp、directories.tests 与 templates.*,脚本会做路径安全校验)。
配套脚本(可选)
技能提供辅助脚本用于创建和验证测试会话:
创建测试会话
# 在目标代码根目录内执行
RUN_ID=2026-03-10-15-30
python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id v202602161028
# 或
RUN_ID=2026-03-10-15-30
python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id v202602161028
作用:自动创建 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/ 会话目录骨架(REVIEW/PLAN/RUN/REPORT + _artifacts/_scripts),并写入运行清单 .auto-test-code-run.json
验证测试会话
python3 ~/.codex/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/2026-03-10-15-30/output/tests/v202602161028
# 或
python3 ~/.claude/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/2026-03-10-15-30/output/tests/v202602161028
作用:检查会话完整性(REVIEW/PLAN/RUN/REPORT + 引用一致性);如需强制检查模板占位符是否全部替换,可加 --strict
常见问题
Q:技能没有被触发怎么办?
A:尝试用更明确的描述:
- ✅ "用 auto-test-code 测试 XXX"
- ✅ "运行 auto-test-code 对 XXX 进行代码审查"
- ❌ "帮我测试一下代码"(太模糊)
Q:A 轮和 B 轮有什么区别?
A:
- A 轮:批判性代码审查,发现具体问题(P0/P1/P2),要求每轮 ≥ 10 个问题
- B 轮:代码质量原则检查,从 9 大维度评估代码质量(算法复杂度、边界覆盖、安全漏洞分类审查、设计质量等)
Q:问题优先级 P0/P1/P2 是什么意思?
A:
| 优先级 | 含义 | 典型场景 |
|---|---|---|
| P0 | 必须修复 | 崩溃风险、安全漏洞、数据损坏、资源泄漏 |
| P1 | 强烈建议 | 性能问题、边界条件缺陷、逻辑错误 |
| P2 | 建议优化 | 代码风格、可读性、冗余代码 |
Q:如何选择 A 轮次数?
A:
| 场景 | 推荐轮次 | 理由 |
|---|---|---|
| 初次审查/代码质量较差 | 3-5 轮 | 多轮逐步发现深层问题 |
| 日常审查/代码质量较好 | 1-2 轮 | 快速检查主要问题 |
| 关键项目/上线前 | 5-10 轮 | 确保代码质量达到高标准 |
Q:什么是"独立评估模式"?
A:每轮 A 轮都基于代码的当前状态独立分析,不查看历史 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/ 工作区中的审查文件。好处是让"多轮"带来"多角度",而非"重复确认"。
Q:如何理解"批判性思维"?
A:使用"刁钻角度"思考代码可能的问题:
- 空输入、超大输入、恶意输入会怎样?
- 并发访问时会有竞态条件吗?
- 资源耗尽时会发生什么?
- 异常抛出时资源会正确释放吗?
详见 references/CRITICAL_THINKING_FOR_CODE.md
WHICHMODEL - 模型选择最佳实践
最后更新:2026-01-25
披露信息
- 覆盖厂商:Anthropic, OpenAI(2/6 = 33%)
- 来源构成:社区 65%, 学术 20%, 官方 10%, 技术博客 5%
- 数据时效:2024-06 至 2026-01
- 局限性:未覆盖国产模型,未独立测试代码审查准确率
场景化建议
场景 1:标准代码审查(最常见)
触发条件:日常代码审查,需要发现系统性问题(算法/边界/并发/安全)
| 项目 | 建议 |
|---|---|
| 推荐模型 | Claude Sonnet 4.5 |
| 推理强度 | medium-high |
| 预期成本 | ~$0.10-0.50/轮 |
理由:
- Sonnet 在代码审查任务中表现出色,SWE-bench 得分 72.7%(接近 Opus)
- 速度更快,成本更低(4-5 倍于 Haiku,显著快于 Opus)
- 社区测试 显示 Sonnet 在多数代码任务中与 Opus 质量相当
- 内部测试 显示 Sonnet 解决 64% 编程问题 vs Opus 38%
避免:无需升级到 Opus,除非遇到极端复杂的算法分析
来源:90 天对比测试 + 官方内部数据
场景 2:复杂算法与安全审查
触发条件:
- 需要深度推理的复杂算法(如分布式系统、加密算法)
- 安全漏洞深度分析(如竞态条件、内存安全)
- 需要多步骤抽象推理的场景
| 项目 | 建议 |
|---|---|
| 推荐模型 | Claude Opus 4.5 |
| 推理强度 | high |
| 预期成本 | ~$0.30-1.50/轮 |
理由:
- Opus 在复杂推理任务中表现更优,社区反馈 称其为"复杂推理的巨大飞跃"
- 用户报告 显示 Opus 在"规划、分析和创建上下文定义"方面更强
- 90 天测试 显示 Opus 在中等投入下成本与 Sonnet 相当
避免:简单代码审查不需要 Opus,用 Sonnet 即可
来源:Reddit 社区讨论 + 90 天对比测试
场景 3:快速批量检查
触发条件:
- 需要快速审查多个文件/模块
- 成本敏感,需要高性价比
- 不需要深度推理,主要发现明显问题
| 项目 | 建议 |
|---|---|
| 推荐模型 | Claude Haiku 4.5 或 Sonnet 4.5 |
| 推理强度 | low-medium |
| 预期成本 | ~$0.02-0.20/轮 |
理由:
- Haiku 成本最低,适合快速批量检查
- 但对于批判性思维驱动的代码审查,Haiku 可能无法发现深层系统性问题
- 社区反馈 显示 Haiku 在复杂任务中可能力不从心
- 推荐:快速检查用 Haiku,但质量要求高时用 Sonnet
避免:需要发现系统性问题(算法/边界/并发)时,不要只用 Haiku
来源:社区反馈 + 官方文档
对比总结
| 模型 | 最适合 | 最不适合 | 相对成本 | 相对速度 | 推荐度 |
|---|---|---|---|---|---|
| Sonnet 4.5 | 标准代码审查(95% 场景) | 极端复杂的算法推理 | $$$ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Opus 4.5 | 复杂算法/安全深度分析 | 简单代码审查(浪费) | \($\) | ⭐⭐ | ⭐⭐⭐ |
| Haiku 4.5 | 快速批量检查 | 批判性思维审查(深层问题) | $ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
说明:
- Sonnet 覆盖 95% 的代码审查场景:大多数情况下 Sonnet 性价比最高
- Opus 用于极端复杂场景:分布式系统、加密算法、深层安全分析
- Haiku 用于快速检查:但不推荐用于需要批判性思维的系统性问题发现
通用原则
- 默认从 Sonnet 开始:95% 的代码审查任务 Sonnet 足够,无需 Opus
- 批判性思维需要强推理:auto-test-code 的核心是发现系统性问题(算法/边界/并发/安全),需要比简单工具调用更强的推理能力
- 成本敏感但质量优先:代码审查是质量问题,不能只追求低成本而牺牲审查深度
- 多轮迭代优化成本:如果需要进行多轮 A 轮审查,可考虑第 1-2 轮用 Sonnet,发现问题后用 Opus 深度分析关键问题
- Haiku 的局限性:虽然 Haiku 速度快、成本低,但 社区反馈 显示它在完成基本任务时可能遇到困难
⚠️ 争议点
Sonnet vs Opus:代码审查应该用哪个?
| 观点 | 支持者 | 理由 |
|---|---|---|
| Sonnet 够用 | 社区多数意见 | Sonnet 在代码审查中表现接近 Opus,但速度快、成本低 |
| Opus 必要 | 部分开发者 | Opus 在复杂推理和深层问题发现上仍有优势 |
数据支持:
- 90 天对比测试:Opus 在中等投入下成本与 Sonnet 相当
- 官方内部测试:Sonnet 解决 64% 编程问题 vs Opus 38%(实际场景)
- SWE-bench 得分:Sonnet 72.7%,接近 Opus 水平
建议:
- 默认使用 Sonnet:性价比最高,覆盖 95% 代码审查场景
- 仅在以下情况升级 Opus:
- 需要分析复杂算法(如分布式系统、加密算法)
- 需要深度安全分析(如竞态条件、内存安全)
- Sonnet 无法发现的深层系统性问题
- 关键项目上线前的最终审查
更新记录
- 2026-01-25:首次调研,覆盖 Anthropic/OpenAI
- 建议:2026-07 重新调研(6 个月后)
来源链接
官方文档:
社区讨论:
- Claude Opus 4.5 is insane (Reddit)
- Opus or nothing for 90% of tasks (Reddit)
- Tested GPT-5.1, Gemini 3, and Claude Opus 4.5 (Reddit)
对比测试:
- 90-Day Claude Code Decision Framework
- Claude Sonnet 4 Vs Opus 4.1: Which Model To Use For Coding
- Claude 3.5 Sonnet vs. Opus: the fastest sprinter or the deepest thinker?
学术研究:
- Enhancing Software Code Vulnerability Detection Using GPT-4o and Claude-3.5 Sonnet
- Assessing the Quality and Security of AI-Generated Code
更多文档
SKILL.md— 技能执行指令与硬性规范config.yaml— 可配置参数references/— 详细策略与参考文档CRITICAL_THINKING_FOR_CODE.md— 批判性思维框架(核心)A_ROUND_REVIEW_TEMPLATE.md— A 轮审查报告结构CODE_SMELLS.md— 代码异味识别指南SECURITY_PATTERNS.md— 安全漏洞模式库SECURITY_TAXONOMY.md— 安全漏洞分类审查体系BOUNDARY_CHECKLIST.md— 边界条件检查清单
Skill manifest
auto-test-code(批判性思维驱动的代码自审查技能)
目标
当用户明确要求"测试代码"、"运行代码审查"或"进行代码自检"时使用。通过多轮 A 轮批判性代码审查 + B 轮代码质量原则检查,系统化发现、记录、修复程序代码中的问题,并将计划/过程/结果统一沉淀到目标代码根目录的 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/ 隔离工作区。⚠️ 不适用:用户只是想优化功能(应直接修改)、只是询问代码问题(应直接回答)、没有明确"测试代码"意图。
流程
输入
输入为待审查的代码项目根目录;可选输入包括用户指定的 A 轮次数、运行 ID、测试/审查参数、配置文件和排除目录。必须覆盖核心源代码、配置/构建脚本及测试代码,并排除 tmp/、依赖和缓存目录;触发与不适用边界以本 Skill 的 ## 目标 为准。
执行步骤
工作流程
隔离工作区硬规则
- 每次 skill 执行开始时,必须先在目标项目根目录创建当次专用工作区:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/。 - 所有由 skill 生成的计划、报告、日志、辅助脚本与中间产物,只能写入当前
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/工作区内。 - 运行可能产生缓存或临时文件的命令时,优先将工作目录、
TMPDIR、XDG_CACHE_HOME、PYTHONPYCACHEPREFIX等重定向到当前.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/工作区。 - 除了用户明确要求的源码修复外,不得把 skill 相关文件写到
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/之外的位置,以免污染源软件项目。
概览
用户输入(目标代码路径)
↓
[A轮 × N]:静态分析 → 动态推理 → 安全分类审查 → 计划 → 优化 → 轻量测试
↓
B轮:代码质量原则检查 → 针对性优化 → 轻量验证
↓
完成(文档齐全 + 问题闭环)
A 轮代码审查(可重复 N 次)
A.1 初始化会话(生成测试 ID + 目录)
目标:创建本轮的 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/ 会话骨架(计划/过程/结果都在同一目录)。
推荐使用确定性脚本:
# 在目标代码根目录内执行(选择你实际的安装路径)
RUN_ID=YYYY-MM-DD-HH-MM
python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id vYYYYMMDDHHMM
# 或
RUN_ID=YYYY-MM-DD-HH-MM
python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id vYYYYMMDDHHMM
最低要求:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/存在.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/REVIEW.md、TEST_PLAN.md、TEST_RUN.md、TEST_REPORT.md和_artifacts/存在
A.2 批判性分析与计划生成(写入 `.bensz-api/task-
目标:使用批判性思维发现代码中的系统性问题,写成可执行计划,按 P0/P1/P2 排序。
输出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/REVIEW.md
⚠️ 批判性思维是核心要求:
- 必须使用「刁钻角度」思考(详见
references/CRITICAL_THINKING_FOR_CODE.md) - 必须发现至少 3 个系统性问题(算法设计/边界条件/并发安全/内存安全/安全漏洞/设计质量)
- 禁止列出"不痛不痒"的表面问题(如"缺少注释"等 P2 级别问题不应占多数)
质量要求(强制):
- 每轮至少发现 10 个问题(P0 + P1 + P2 总和)
- 鼓励达到 15-20 个问题
- P0 + P1 占比必须 ≥ 60%
- 系统性问题 ≥ 3 个(算法/边界/并发/内存/安全/架构/设计)
核心要求:
- 独立评估原则(强制):
- 每轮 A 轮必须基于目标代码的当前工作状态独立分析
- 不查看历史
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/工作区中的审查文件 - 每轮都是一次完整的、无偏见的系统性审查
- 审查范围(强制):
- 必须审查:核心源代码文件(以
config.yaml:a_round_check.independent_review.scan_patterns为准) - 必须审查:配置文件、构建脚本、测试代码
- 必须审查:核心源代码文件(以
- 排除范围:
tmp/等 skill 产物目录,以及node_modules/、venv/、__pycache__/等依赖/缓存目录;目标项目的测试代码仍属于必须审查范围 - 全维度覆盖(强制):每轮必须覆盖所有审查维度(以
config.yaml:a_round_check.dimensions为准);不得以“本轮不聚焦”为由跳过任何维度 - 深挖维度(可选):可在全覆盖基础上额外指定 1-2 个深挖维度,对该维度使用刁钻角度做更细致分析;多轮时建议轮换深挖点
- 刁钻角度:在深挖维度上必须使用至少一个刁钻角度(空输入/超大输入/恶意输入/竞态条件/资源耗尽/权限绕过/供应链污染)
- 优先级依据:P0/P1/P2 必须有明确的判定标准
- P0: 崩溃风险、可利用安全漏洞、未授权访问、敏感信息泄露、数据损坏、死锁/活锁、内存泄漏
- P1: 性能问题、边界条件缺陷、逻辑错误、资源泄漏、需要前置条件或组合利用的安全风险
- P2: 代码风格、可读性、冗余代码
- 可追溯性:每个问题必须包含位置、现象、影响、修复建议、验证方法
批判性思维框架(必读):
references/CRITICAL_THINKING_FOR_CODE.md⚠️ 核心文档,必须使用- 框架 1: 静态分析视角(算法设计/数据结构/代码复杂度/设计质量)
- 框架 2: 动态推理视角(边界条件/异常处理/资源管理)
- 框架 3: 问题质量标准(黄金公式 + 质量检查清单)
- 框架 4: 设计质量视角(架构/扩展性/API/状态/建模/耦合/模式)
- 框架 5: 安全漏洞分类视角(CWE/OWASP/STRIDE/七大王国/CVSS)
references/A_ROUND_REVIEW_TEMPLATE.md⚠️ 代码审查计划模板references/CODE_SMELLS.md代码异味识别指南references/SECURITY_PATTERNS.md安全漏洞模式库references/SECURITY_TAXONOMY.md安全漏洞分类审查体系(必须用于安全维度)references/DESIGN_ANTI_PATTERNS.md设计反模式识别指南
A.3 执行优化与轻量测试(写入 `.bensz-api/task-
目标:按计划逐项修复,并用轻量测试验证。
输出:
- 过程:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/TEST_RUN.md - 结果:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/TEST_REPORT.md
轻量测试原则:
- 只验证"核心路径"与"本轮变更点"
- 每条结论必须有可复现证据(命令输出、日志、对比结果)
- 中间产物放入
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/_artifacts/
可选增强:
python3 ~/.codex/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM
# 或
python3 ~/.claude/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM
说明:verify_session.py --strict 仅用于你已将会话文档中的模板占位符全部替换后的最终自检;新建骨架默认会失败(属于预期行为)。
A.4 是否进入下一轮
⚠️ 强制检查:
- 本轮已提出至少 10 个问题(P0 + P1 + P2 总和)
进入下一轮 A 轮的条件:
- 用户指定的轮次数未完成
- 本轮问题(P0/P1/P2)已全部闭环
重要:A 轮结束后,必须进入 B 轮代码质量检查。
B 轮代码质量检查
⚠️ 强制执行:B 轮代码质量检查是自动测试流程的强制性环节。
B.1 产出质量检查报告(写入 `.bensz-api/task-
目标:对 A 轮后的最新代码做系统性质量检查。
输出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/REVIEW.md
推荐使用确定性脚本创建 B 轮会话目录:
# 在目标代码根目录内执行(选择你实际的安装路径)
RUN_ID=YYYY-MM-DD-HH-MM
python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind b --id vYYYYMMDDHHMM --a-test-id vYYYYMMDDHHMM
# 或
RUN_ID=YYYY-MM-DD-HH-MM
python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind b --id vYYYYMMDDHHMM --a-test-id vYYYYMMDDHHMM
检查维度(以 config.yaml 的 b_round_check.dimensions 为准):
- 算法复杂度分析
- 边界条件覆盖
- 异常处理完整性
- 资源管理(内存/文件/连接)
- 并发安全性
- 安全漏洞分类审查(CWE/OWASP/STRIDE/七大王国/CVSS)
- 代码可读性与可维护性
- 测试覆盖充分性
- 设计质量(可扩展性/架构/API/状态/建模/耦合/模式)
模板:templates/B_ROUND_CODE_QUALITY_TEMPLATE.md
B.2 B 轮优化与验证(写入 `.bensz-api/task-
⚠️ 强制修复要求:
- B 轮发现的 所有 P0-P2 问题都必须处理
- P0 问题必须修复
- P1 问题必须修复(除非有合理理由)
- 每个修复必须有验证证据
完成条件:
- P0 问题修复率 = 100%
- P1 问题修复率 ≥ 80%
- 所有修复都有可复现证据
可复用资源
- 配置:
config.yaml - 模板:
templates/- A 轮审查:
templates/CODE_REVIEW_TEMPLATE.md - B 轮质量检查:
templates/B_ROUND_CODE_QUALITY_TEMPLATE.md - 测试计划:
templates/SESSION_TEST_PLAN_TEMPLATE.md - 测试过程:
templates/SESSION_TEST_RUN_TEMPLATE.md - 测试报告:
templates/SESSION_TEST_REPORT_TEMPLATE.md
- A 轮审查:
- 参考:
references/- 批判性思维指南:
references/CRITICAL_THINKING_FOR_CODE.md⚠️ - A 轮审查结构:
references/A_ROUND_REVIEW_TEMPLATE.md⚠️ - 代码异味识别:
references/CODE_SMELLS.md - 安全漏洞模式:
references/SECURITY_PATTERNS.md - 安全漏洞分类审查体系:
references/SECURITY_TAXONOMY.md⚠️ - 边界条件检查清单:
references/BOUNDARY_CHECKLIST.md - 设计反模式识别:
references/DESIGN_ANTI_PATTERNS.md
- 批判性思维指南:
- 辅助脚本:
scripts/create_session.py - 辅助脚本:
scripts/verify_session.py
输出
交付物
本技能的审查结论和验证证据必须落盘到目标代码项目的隔离工作区中,形成可追溯、可复核、后续可接续的会话文件。
默认交付根目录为 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/;实际目录以 config.yaml 中的 directories.tmp 和 directories.tests 为准。
每个 A 轮或 B 轮会话目录都必须包含同一组文件:
REVIEW.md:审查报告。A 轮记录批判性代码审查的问题清单与改进计划;B 轮记录代码质量原则检查结果。TEST_PLAN.md:测试计划,说明本轮要验证的修复点、核心路径和预期证据。TEST_RUN.md:测试过程记录,包含实际执行的命令、关键输出摘录和关键决策。TEST_REPORT.md:测试结果报告,包含结论、证据、遗留问题和后续建议。_artifacts/:中间产物目录,用于保存命令输出、日志、截图、对比结果等证据。
输出管理
BenszAPI 任务工作区
目录与命名规范
- 运行工作区:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/,同一次技能执行的所有 A/B 轮必须复用同一个run_id。 - 会话 ID:
vYYYYMMDDHHMM,使用分钟级时间戳。 - A 轮会话目录:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/。 - B 轮会话目录:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/,即在同类会话 ID 前增加b-前缀。 - 兼容旧目录:
verify_session.py可识别历史目录名tests/B轮-vYYYYMMDDHHMM/;新建目录必须使用.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/。 - 废弃目录:
reviews/不再创建、不再写入;如目标项目中存在旧的reviews/,只视为历史遗留并在审查时排除。
校验
完成条件(验收)
- 用户指定的 A 轮次数已完成
- B 轮代码质量检查已完成
- 每轮 A 轮平均问题数量 ≥ 10 个
- 每轮 P0 + P1 占比 ≥ 60%
- 每轮系统性问题 ≥ 3 个(算法/边界/并发/内存/安全/设计)
- 关键问题(P0/P1)已闭环
-
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/会话结构完整且可追溯(每轮都有REVIEW.md、TEST_PLAN.md、TEST_RUN.md、TEST_REPORT.md和_artifacts/)
失败与恢复
会话脚本、测试命令或依赖失败时,保留 TEST_RUN.md、日志和 _artifacts/ 中的命令输出,标记当前轮次未完成并报告可复现命令;修复后可在同一 run_id 的会话目录重试。目标路径越界、输入缺失或无法安全判断时停止,不把失败或不确定结果写成通过。
约束
公共硬约束
本块由 docs/templates/skill-common-constraints.md 统一维护;每个 SKILL.md 的 ## 约束 必须逐字同步本块,不得在副本中改写公共规则。
- 任务需要落盘时,使用唯一的
./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/根目录;共享材料放入shared/,Skill 专属材料放入该 Skill 的input/、output/、log/。 - 正式交付物、源代码和正式计划按项目约定保存,不写入任务工作区;未经授权不覆盖、删除、迁移或远程写入。
- 项目维护变更检查 BAC 可用性并记录需求、AI 产出、工具结果、文件改动和验证摘要;BAC 只做过程审计,不替代署名、责任或合规判断。
- 不记录 API Key、访问令牌、密码、Cookie、环境/凭据文件、私有 Prompt、身份信息、本地用户名、主机名或不必要的大体积原始数据。
- 文件路径必须规范化并限制在授权项目范围内;外部 URL、子进程和网络访问遵循最小权限,防止路径遍历、SSRF 和命令注入。
- Skill 版本唯一记录在自身
config.yaml:skill_info.version;公开 API、协议、目录或配置变更同步文档与CHANGELOG.md。 bensz-collect-bugs是一个 Agent Skill;仅将 Bensz Agent Skill 或 Bensz 基础设施本身的设计缺陷交给它。先脱敏写入~/.bensz-skills/bugs/,当前任务不中断,只有用户明确要求才公开上报,禁止直接修改用户已安装的 Skill 源码。
Files (skills)
-
references
-
A_ROUND_REVIEW_TEMPLATE.md 4.4 KB
# A 轮代码审查计划结构 **用途**:作为 A 轮代码审查报告的结构参考 --- ## 报告头部 ```markdown # 代码审查计划(v202601231200) **审查日期**: 2026-01-23 **审查ID**: v202601231200 **目标代码路径**: /path/to/project **代码语言**: Python **代码规模**: 约 5,000 行(10 个模块) ``` --- ## 独立评估声明 ```markdown ## 独立评估与审查范围(强制) - [x] 本轮基于目标代码的**当前状态**独立评估 - [x] **未查看**历史 `tmp/run_*/` 工作区中的审查文件 **扫描命令证据**: ```bash # 扫描 Python 文件 find /path/to/project -name "*.py" | head -20 # 统计代码行数 cloc /path/to/project --include-lang=Python # 搜索潜在问题模式 rg -n "TODO|FIXME|XXX|HACK" /path/to/project ``` **审查维度与深挖策略**: - 全维度覆盖(强制):本轮必须覆盖全部审查维度(以 `a_round_check.dimensions` 为准;如项目存在 `.auto-test-code/config.yaml`,同名字段可覆盖) - 深挖维度(可选):算法复杂度分析、边界条件覆盖 - 刁钻角度:空输入、超大输入、并发竞态(用于深挖维度) ``` --- ## 问题清单示例 ### P0 示例 ```markdown ### P0(必须修复) 1) 文件资源泄漏:异常情况下文件未关闭 - **位置**:`src/file_processor.py:56-65` - **问题类型**:资源泄漏 **现象**: ```python def process_file(path): f = open(path, 'r') data = f.read() result = parse(data) # 如果这里抛出异常 f.close() return result ``` **推理**: - parse(data) 可能抛出异常(如 JSON 解析错误) - 异常时 f.close() 不会执行 - 高频调用会耗尽文件描述符 **影响**: - 程序运行一段时间后无法打开新文件 - "Too many open files" 错误 - 服务不可用 **优先级**:P0(资源泄漏,会导致服务不可用) **修复建议**: ```python def process_file(path): with open(path, 'r') as f: data = f.read() return parse(data) ``` **验证方法**: 1. 单元测试:在 parse() 中人为抛出异常 2. 验证文件描述符在异常后仍然释放(lsof -p PID) 3. 压力测试:循环 1000 次,确认文件描述符不泄漏 ``` ### P1 示例 ```markdown ### P1(强烈建议) 1) 算法性能问题:使用线性搜索而非哈希表 - **位置**:`src/user_manager.py:45-52` - **问题类型**:性能问题 **现象**: ```python def find_user(users, user_id): for user in users: if user.id == user_id: return user return None ``` **推理**: - users 是列表,查找是 O(n) - 如果有 100,000 个用户,平均需要 50,000 次比较 **影响**: - 用户量大时响应时间线性增长 - 每个请求都调用 find_user 时会成为瓶颈 **优先级**:P1(性能问题,影响用户体验) **修复建议**: ```python def find_user(users_dict, user_id): return users_dict.get(user_id) ``` **验证方法**: 1. 构造 100,000 个用户的测试数据 2. 测量查找耗时:应从毫秒级降至微秒级 3. 单元测试验证正确性 ``` --- ## 执行步骤示例 ```markdown ## 执行步骤 1. 修复文件资源泄漏(P0-1) - 文件:src/file_processor.py:56-65 - 改为使用 with 语句 2. 修复除零错误(P0-2) - 文件:src/calculator.py:23 - 添加除零检查 3. 优化查找性能(P1-1) - 文件:src/user_manager.py:45-52 - 将 users 列表改为字典 4. 添加输入验证(P1-2) - 文件:src/api.py:34-40 - 验证 user_id 类型 ``` --- ## 轻量测试计划 ```markdown ## 本轮轻量测试 - 会话目录:`tmp/run_20260123115959/tests/v202601231200/` - 测试计划:`tmp/run_20260123115959/tests/v202601231200/TEST_PLAN.md` - 测试报告:`tmp/run_20260123115959/tests/v202601231200/TEST_REPORT.md` **测试范围**: - 验证 P0-1 修复:异常时文件正确关闭 - 验证 P0-2 修复:除零时抛出明确异常 - 验证 P1-1 修复:查找性能 < 1ms ``` --- ## 问题统计 ```markdown ## 问题统计 | 优先级 | 数量 | 占比 | |--------|------|------| | P0 | 3 | 30% | | P1 | 5 | 50% | | P2 | 2 | 20% | | **总计** | **10** | 100% | **系统性问题**: - 算法问题:2 个(查找、排序) - 资源问题:2 个(文件泄漏、内存泄漏) - 边界问题:1 个(除零) **质量检查**: - [x] 总问题数 ≥ 10 - [x] P0 + P1 占比 ≥ 60%(80%) - [x] 系统性问题 ≥ 3(5 个) ``` -
BOUNDARY_CHECKLIST.md 6.1 KB
# 边界条件检查清单 **用途**:系统化检查代码的边界条件处理 --- ## 数值边界 ### 整数边界 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **零值** | `0` | 除法、取模 | 抛出明确异常或返回默认值 | | **负数** | `-1`, `-100` | 数组索引、计算 | 验证范围或明确拒绝 | | **整型溢出** | `INT_MAX`, `INT_MIN` | 计算、计数 | 使用大整数类型或检测溢出 | | **浮点边界** | `INF`, `NaN` | 计算、比较 | 特殊处理 | **示例检查点**: ```python # 检查:除零保护 def divide(a, b): if b == 0: raise ValueError("Cannot divide by zero") return a / b # 检查:整数溢出 def add_ints(a, b): result = a + b if result > 2**31 - 1: # INT_MAX raise OverflowError("Integer overflow") return result ``` --- ## 集合边界 ### 长度边界 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **空集合** | `[]`, `{}`, `""` | 遍历、访问 | 返回空结果或抛出明确异常 | | **单元素** | `[x]` | 排序、分组 | 正确处理 | | **极大集合** | 10^6+ 元素 | 内存、性能 | 分批处理或限制大小 | **示例检查点**: ```python # 检查:空列表处理 def get_first(items): if not items: return None # 或 raise ValueError return items[0] # 检查:大小限制 def process_batch(items, max_size=1000): if len(items) > max_size: raise ValueError(f"Batch too large: {len(items)}") # ... ``` ### 索引边界 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **越界访问** | `arr[-1]`, `arr[n]` | 数组/列表访问 | 检查范围或使用安全方法 | | **负索引** | Python 支持 | 互操作 | 注意跨语言差异 | **示例检查点**: ```python # 检查:索引范围 def get_item(arr, index): if index < 0 or index >= len(arr): raise IndexError(f"Index {index} out of range [0, {len(arr)})") return arr[index] ``` --- ## 字符串边界 ### 特殊字符 | 字符 | 场景 | 检查点 | 预期行为 | |------|------|--------|----------| | **空字符** | `\0` | C 互操作 | 处理或拒绝 | | **换行符** | `\n`, `\r\n` | 日志、协议 | 一致处理 | | **路径分隔符** | `/`, `\`, `../` | 文件操作 | 规范化路径 | | **引号** | `"`, `'`, `` ` `` | SQL/命令/Shell | 转义或参数化 | **示例检查点**: ```python # 检查:路径遍历 def safe_path_join(base, filename): # 拒绝包含 .. 的文件名 if ".." in filename or filename.startswith("/"): raise ValueError("Invalid filename") return os.path.join(base, filename) ``` ### 字符串长度 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **空字符串** | `""` | 解析、处理 | 返回默认值 | | **极长字符串** | 1GB+ | 内存、性能 | 限制大小 | | **Unicode** | Emoji、特殊字符 | 编码、长度 | 正确处理 | --- ## 时间边界 ### 时间值 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **时间戳溢出** | Year 2038 问题 | 时间计算 | 使用 64 位时间 | | **时区** | 跨时区 | 时间比较 | 统一使用 UTC | | **夏令时** | 时钟调整 | 时间差 | 使用 UTC 或明确处理 | **示例检查点**: ```python # 检查:时间戳 from datetime import datetime, timezone def get_timestamp(): # 始终使用 UTC return datetime.now(timezone.utc) ``` ### 超时与过期 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **零超时** | `timeout=0` | 网络请求 | 明确语义 | | **负超时** | `timeout=-1` | 等待操作 | 拒绝或无限等待 | | **过期时间** | `expires_at < now` | 缓存/令牌 | 检查并拒绝 | --- ## 文件边界 ### 文件大小 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **空文件** | 0 字节 | 读取 | 返回空结果 | | **超大文件** | 10GB+ | 内存加载 | 流式处理或限制 | **示例检查点**: ```python # 检查:文件大小 MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB def load_file(path): size = os.path.getsize(path) if size > MAX_FILE_SIZE: raise ValueError(f"File too large: {size} bytes") # ... ``` ### 文件权限 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **权限拒绝** | 无读取权限 | 打开文件 | 明确错误消息 | | **目录** | 路径是目录 | 文件操作 | 检查文件类型 | | **符号链接** | 链接目标 | 安全检查 | 验证解析后路径 | --- ## 网络边界 ### 地址与端口 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **无效地址** | `256.256.256.256` | 连接 | 验证格式 | | **保留端口** | `< 1024` | 绑定 | 检查权限 | | **端口 0** | `port=0` | 绑定 | 随机端口或拒绝 | ### 数据大小 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **空响应** | 0 字节 | HTTP 请求 | 处理空响应 | | **超大响应** | 1GB+ | 内存 | 限制大小或流式 | | **分片** | MTU 大小 | UDP | 处理分片 | --- ## 并发边界 ### 线程数 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **零线程** | `threads=0` | 线程池 | 退化为同步或拒绝 | | **负数** | `threads=-1` | 创建线程 | 拒绝 | | **极大线程数** | `threads=100000` | 资源 | 限制上限 | ### 资源限制 | 场景 | 输入 | 检查点 | 预期行为 | |------|------|--------|----------| | **连接池** | `pool=0` | 数据库 | 拒绝 | | **队列满** | 无界队列 | 内存 | 限制大小 | --- ## 快速检查清单 审查代码时,检查是否处理了: - [ ] **数值**:零、负数、溢出、NaN/Inf - [ ] **集合**:空、单元素、极大、索引越界 - [ ] **字符串**:空、极长、特殊字符(\0, \n, ../) - [ ] **时间**:溢出、时区、超时 - [ ] **文件**:空、超大、权限、符号链接 - [ ] **网络**:无效地址、超大响应、端口范围 - [ ] **并发**:零/负线程数、资源限制 -
CODE_SMELLS.md 5.5 KB
# 代码异味识别指南 **用途**:快速识别代码中的常见异味 --- ## 什么是代码异味? 代码异味(Code Smell)是代码中**可能存在问题**的表面特征。它不一定是 bug,但通常表明代码需要重构。 --- ## 常见代码异味 ### 1. 重复代码(Duplicated Code) **特征**: - 相同或相似的代码出现在多个地方 - 复制粘贴代码片段 **问题**: - 维护成本高(修改需要改多处) - 容易出现不一致 **示例**: ```python # 重复的验证逻辑 def create_user(name): if not name or len(name) < 3: raise ValueError("Invalid name") # ... def update_user(user_id, name): if not name or len(name) < 3: raise ValueError("Invalid name") # 重复 # ... ``` **重构**: ```python def validate_name(name): if not name or len(name) < 3: raise ValueError("Invalid name") def create_user(name): validate_name(name) # ... ``` --- ### 2. 过长函数(Long Method) **特征**: - 函数超过 50 行 - 函数做太多事情 **问题**: - 难以理解 - 难以测试 - 难以复用 **示例**: ```python def process_request(request): # 100+ 行代码 # 1. 验证请求 # 2. 认证用户 # 3. 处理业务逻辑 # 4. 格式化响应 # ... ``` **重构**: ```python def process_request(request): validate_request(request) user = authenticate_user(request) result = process_business_logic(user, request) return format_response(result) ``` --- ### 3. 过大类(Large Class) **特征**: - 类超过 300 行 - 类有太多职责 **问题**: - 难以理解 - 难以维护 - 违反单一职责原则 **示例**: ```python class UserManager: def create_user(self): ... def update_user(self): ... def delete_user(self): ... def send_email(self): ... # 不相关 def generate_report(self): ... # 不相关 def backup_data(self): ... # 不相关 ``` **重构**: ```python class UserManager: def create_user(self): ... def update_user(self): ... def delete_user(self): ... class EmailService: def send_email(self): ... class ReportGenerator: def generate_report(self): ... ``` --- ### 4. 过长参数列表(Long Parameter List) **特征**: - 函数参数超过 4 个 **问题**: - 难以理解 - 难以使用 - 容易传错参数 **示例**: ```python def create_user(name, email, age, address, phone, country, city, zip_code): # ... ``` **重构**: ```python def create_user(user_data: UserData): # ... class UserData: name: str email: str age: int # ... ``` --- ### 5. 特征依恋(Feature Envy) **特征**: - 函数更关心其他类的数据而非自己的类 **问题**: - 违反封装原则 - 高耦合 **示例**: ```python class Order: def calculate_price(self): # 大量访问 Customer 的数据 discount = self.customer.get_discount() tax_rate = self.customer.get_tax_rate() # ... ``` **重构**: ```python class Order: def calculate_price(self): return self.customer.calculate_order_price(self) ``` --- ### 6. 数据泥团(Data Clumps) **特征**: - 多个参数总是一起出现 **问题**: - 代码冗余 - 容易遗漏 **示例**: ```python def func1(x, y, width, height): ... def func2(x, y, width, height): ... def func3(x, y, width, height): ... ``` **重构**: ```python class Rectangle: x: int y: int width: int height: int def func1(rect: Rectangle): ... ``` --- ### 7. 基本类型偏执(Primitive Obsession) **特征**: - 过度使用基本类型而非对象 **问题**: - 丢失类型安全 - 代码重复 **示例**: ```python def connect(host: str, port: int, timeout: int): ... # 调用:connect("localhost", 8080, 30) ``` **重构**: ```python class ConnectionConfig: host: str port: int timeout: int def connect(config: ConnectionConfig): ... ``` --- ### 8. 过度继承(Shotgun Surgery) **特征**: - 修改需要同时修改多个类 **问题**: - 难以维护 - 容易遗漏 **示例**: ```python # 添加新字段需要在多个类中修改 class User: def __init__(self, name): self.name = name class UserView: def render(self, user): return f"Name: {user.name}" class UserSerializer: def serialize(self, user): return {"name": user.name} ``` --- ### 9. 魔法数字(Magic Numbers) **特征**: - 代码中出现未命名的数字常量 **问题**: - 难以理解 - 难以修改 **示例**: ```python if score > 75: # 75 是什么? grade = "A" elif score > 60: # 60 是什么? grade = "B" ``` **重构**: ```python GRADE_A_THRESHOLD = 75 GRADE_B_THRESHOLD = 60 if score > GRADE_A_THRESHOLD: grade = "A" elif score > GRADE_B_THRESHOLD: grade = "B" ``` --- ### 10. 死代码(Dead Code) **特征**: - 从未被执行的代码 - 已注释的代码 **问题**: - 增加维护负担 - 造成困惑 **示例**: ```python def old_function(): # 这个函数不再被调用 pass # def deprecated_function(): # ... ``` **重构**:删除这些代码 --- ## 快速检查清单 在代码审查时,快速检查: - [ ] 是否有重复代码? - [ ] 是否有超过 50 行的函数? - [ ] 是否有超过 4 个参数的函数? - [ ] 是否有未命名的魔法数字? - [ ] 是否有从未被调用的函数? - [ ] 是否有总是一起出现的参数组? - [ ] 是否有过度使用基本类型的情况? - [ ] 类是否过大(>300 行)? -
CRITICAL_THINKING_FOR_CODE.md 15.8 KB
# 代码审查批判性思维指南 **文档版本**:v1.0.0 **创建时间**:2026-01-23 **用途**:为 auto-test-code 提供「如何进行代码批判性思考」的框架 --- ## 核心思想 **批判性代码审查** ≠ 找茬 = 静态分析 + 动态推理 + 边界探索 + 深度挖掘 本文档提供**五大思考框架**,帮助 AI 在每轮 A 轮中发现真正有价值的代码问题。 --- ## A 轮独立评估(强制) 在 auto-test-code 中,每轮 A 轮默认采用**独立评估**模式: - **不查看**历史 `tmp/run_*/` 工作区中的审查文件 - 只基于目标代码的**当前状态**证据 - 目标:让"多轮"带来"多角度",而非"重复确认" --- ## 框架 1: 静态分析视角 ### 目的 通过**代码静态分析**,不运行代码就能发现潜在问题。 ### 维度 1: 算法设计与复杂度 **自问清单**: - 这个算法的时间复杂度是多少?是否存在更优算法? - 数据结构选择是否恰当?(列表 vs 哈希表 vs 树) - 是否存在重复计算?(缓存/记忆化机会) - 是否有不必要的嵌套循环? **高质量问题示例**: ``` 问题:查找操作使用线性搜索,但应该用哈希表。 位置:src/user_manager.py:45-52 现象: ```python def find_user(users, user_id): for user in users: # O(n) 线性搜索 if user.id == user_id: return user return None ``` 影响: - 用户量大时(>10,000),查找性能线性下降 - 如果 find_user 被频繁调用(如每个请求),会成为性能瓶颈 优先级:P1 修复建议: 将 users 列表改为字典(user_id -> user): ```python def find_user(users_dict, user_id): return users_dict.get(user_id) # O(1) ``` 验证方法: - 构造 100,000 个用户的测试数据 - 测量查找耗时,应从毫秒级降至微秒级 ``` ### 维度 2: 代码复杂度与可读性 **自问清单**: - 这个函数是否过长(>100 行)? - 嵌套层级是否过深(>4 层)? - 圈复杂度是否过高(>10)? - 是否存在重复代码? **高质量问题示例**: ``` 问题:process_request 函数圈复杂度为 18,难以理解和维护。 位置:src/handler.py:102-230 现象: - 函数有 128 行 - 包含 9 层 if-elif-else 嵌套 - 有 15 个返回点 影响: - 难以理解执行流程 - 容易引入 bug - 测试困难(需要覆盖所有分支) 优先级:P1 修复建议: 拆分为多个小函数: - validate_request() - authenticate_user() - process_business_logic() - format_response() 验证方法: - 使用工具测量重构后圈复杂度 < 10 - 所有现有测试通过 ``` ### 维度 3: 类型安全与数据流 **自问清单**: - 变量类型是否明确?是否存在类型混淆? - 是否有未初始化变量的使用? - 是否有隐式类型转换? - 数据流是否清晰? **高质量问题示例**: ``` 问题:user_id 参数既接受 int 又接受 str,导致类型混淆。 位置:src/api.py:34-40 现象: ```python def get_user(user_id): # 有时传 int,有时传 str query = f"SELECT * FROM users WHERE id = {user_id}" # ... ``` 影响: - SQL 注入风险(当 user_id 是 str 时) - 类型错误导致运行时异常 优先级:P0 修复建议: ```python def get_user(user_id: int) -> User: if not isinstance(user_id, int): raise TypeError("user_id must be int") query = "SELECT * FROM users WHERE id = %s" cursor.execute(query, (user_id,)) ``` 验证方法: - 传入字符串参数,确认抛出 TypeError - 单元测试覆盖类型检查 ``` --- ## 框架 2: 动态推理视角 ### 目的 通过**推理代码执行过程**,发现运行时问题。 ### 维度 1: 边界条件推理 **测试场景矩阵**: | 输入类型 | 正常输入 | 边缘输入 | 极端输入 | 异常输入 | |----------|----------|----------|----------|----------| | **数值** | 1-100 | 0, -1 | INT_MAX, INT_MIN | NaN, Infinity | | **字符串** | "hello" | "", " " | 1MB 字符串 | "\0", "\n", "../" | | **集合** | [1,2,3] | [], [x] | 10^6 个元素 | None | | **文件** | 1KB 文件 | 0 字节文件 | 10GB 文件 | 权限拒绝 | **高质量问题示例**: ``` 问题:除法操作未检查除零,导致崩溃。 位置:src/calculator.py:23 现象: ```python def calculate_ratio(x, y): return x / y # y = 0 时崩溃 ``` 推理: - 当 y = 0 时,Python 抛出 ZeroDivisionError - 调用方未处理异常,导致程序崩溃 优先级:P0 修复建议: ```python def calculate_ratio(x, y): if y == 0: raise ValueError("Cannot divide by zero") return x / y ``` 验证方法: - 调用 calculate_ratio(10, 0),确认抛出 ValueError - 单元测试覆盖除零场景 ``` ### 维度 2: 资源管理推理 **自问清单**: - 文件打开后是否一定会关闭? - 数据库连接是否会泄漏? - 内存是否会无限增长? - 是否有循环引用? **高质量问题示例**: ``` 问题:文件打开后未在异常情况下关闭。 位置:src/file_processor.py:56-65 现象: ```python def process_file(path): f = open(path, 'r') # 未使用 with data = f.read() # 如果这里抛出异常,文件不会关闭 result = parse(data) f.close() return result ``` 推理: - 如果 parse(data) 抛出异常,f.close() 不会执行 - 高频调用时可能耗尽文件描述符 优先级:P0 修复建议: ```python def process_file(path): with open(path, 'r') as f: data = f.read() return parse(data) ``` 验证方法: - 在 parse() 中人为抛出异常 - 检查文件描述符是否正确释放(lsof) ``` ### 维度 3: 并发场景推理 **自问清单**: - 多线程访问共享变量是否安全? - 是否存在竞态条件? - 是否可能死锁? - 是否存在检查-使用竞态? **高质量问题示例**: ``` 问题:检查-使用竞态条件(TOCTOU)。 位置:src/file_manager.py:78-85 现象: ```python def write_file_if_not_exists(path, content): if not os.path.exists(path): # 检查 # 另一个线程可能在这里创建文件 with open(path, 'w') as f: # 使用 f.write(content) ``` 推理: - 线程 A 检查文件不存在 - 线程 B 检查文件不存在 - 线程 A 创建文件 - 线程 B 覆盖文件(或抛出异常) 优先级:P0 修复建议: ```python def write_file_if_not_exists(path, content): # 原子操作:使用 O_CREAT | O_EXCL fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL) with os.fdopen(fd, 'w') as f: f.write(content) ``` 验证方法: - 并发测试:100 个线程同时调用 - 确认只有一个线程成功,其他抛出 FileExistsError ``` --- ## 框架 3: 问题质量标准 ### 黄金标准 每个问题必须满足: ``` 位置 + 现象 + 影响(为什么重要) + 修复方案 + 验证方法 ``` ### 质量检查清单 #### 检查 1: 位置精确吗? - ❌ "某个函数有问题" - ✅ "src/user_service.py:45-52 的 find_user 函数" #### 检查 2: 现象具体吗? - ❌ "性能不好" - ✅ "使用 O(n) 线性搜索,在 10,000 用户下耗时 50ms" #### 检查 3: 影响明确吗? - ❌ "影响性能" - ✅ "用户量大时会成为性能瓶颈,响应时间线性增长" #### 检查 4: 修复方案具体吗? - ❌ "优化算法" - ✅ "将列表改为字典,使用 users_dict.get(user_id) 实现 O(1) 查找" #### 检查 5: 验证方法明确吗? - ❌ "测试性能" - ✅ "构造 100,000 个用户,测量查找耗时 < 1ms" --- ## 框架 4: 设计质量视角 ### 目的 通过**架构与演化成本**视角,识别“代码能跑但设计脆弱”的问题。 ### 维度 1: 可扩展性 **自问清单**: - 如果需要新增一种 X 类型,需要改几个文件?(如果 > 1,通常违反 OCP) - 类型分发是否依赖长 if-else / switch 链? - 配置和规则是可扩展的,还是硬编码在分支里? ### 维度 2: 架构与依赖方向 **自问清单**: - 这个模块的职责能用一句话描述吗?如果不能,通常违反 SRP - 依赖方向是否向内收敛?是否存在层次穿透或循环依赖? - 是否出现“上帝对象”或“散弹枪手术”迹象? ### 维度 3: API 与契约设计 **自问清单**: - 接口参数命名、顺序和语义是否与调用者心智模型一致? - 同类操作的返回结构是否统一? - 接口是否泄露了不该暴露的底层实现细节? ### 维度 4: 状态管理 **自问清单**: - 状态转换是否显式建模,而不是散落在多个函数的布尔分支里? - 是否存在无保护的状态跃迁? - 中间状态、失败状态、重试状态是否被明确处理? ### 维度 5: 领域建模 **自问清单**: - 业务规则是在领域对象内部,还是散落在外部 Service / Handler 层? - 是否用基本类型代替应当抽象为值对象的业务概念? - 聚合边界是否清晰,还是谁都能直接改内部状态? ### 维度 6: 耦合与内聚 **自问清单**: - 模块之间依赖抽象还是依赖具体实现? - 一个类的方法是否围绕同一组数据和职责,还是各管一摊? - 变更一个需求是否会联动多个不相干模块? ### 维度 7: 设计模式使用是否恰当 **自问清单**: - 该用策略/工厂/状态模式的地方,是否仍用大量条件分支硬撑? - 是否出现只有一种实现却强行抽象接口的过度设计? - 是否把单例当成全局可变状态容器? **高质量问题示例**: ``` 问题:新增支付渠道必须修改 4 处 if-else,扩展成本持续上升。 位置:src/payment/router.py:18-74 现象: ```python def dispatch(channel, payload): if channel == "wechat": return handle_wechat(payload) if channel == "alipay": return handle_alipay(payload) if channel == "card": return handle_card(payload) raise ValueError("unsupported channel") ``` 影响: - 每新增一种渠道都要改原有分发函数,违反开闭原则 - 条件分支继续增长后,测试矩阵和回归成本都会上升 优先级:P1 修复建议: ```python HANDLERS = { "wechat": handle_wechat, "alipay": handle_alipay, "card": handle_card, } def dispatch(channel, payload): try: return HANDLERS[channel](payload) except KeyError as exc: raise ValueError(f"unsupported channel: {channel}") from exc ``` 验证方法: - 新增一种渠道时,只需新增处理函数并注册到映射表 - 回归测试覆盖已存在渠道与未知渠道路径 ``` --- ## 代码特定问题类型 ## 框架 5: 安全漏洞分类视角 ### 目的 通过**安全分类体系**,避免安全审查停留在“搜索危险函数”的浅层检查。 每轮必须结合 `SECURITY_TAXONOMY.md`,至少从以下 5 个角度交叉审查: 1. **CWE 根因**:输入验证、注入、内存安全、认证授权、信息泄露、配置安全、资源耗尽。 2. **OWASP 风险**:访问控制、配置、供应链、密码学、注入、过时组件、认证、完整性、日志、SSRF。 3. **STRIDE 威胁**:身份欺骗、篡改、否认、信息泄露、拒绝服务、权限提升。 4. **七大王国**:输入验证与表示、API 滥用、安全特性、时间和状态、错误处理、代码质量、封装。 5. **CVSS 排序**:攻击向量、攻击复杂度、所需权限、用户交互、机密性/完整性/可用性影响。 ### 安全自问清单 - 外部输入是否会进入 SQL、Shell、模板、表达式、反序列化、文件路径、URL 请求或日志? - 每个敏感功能是否同时做了认证、功能级授权和数据级授权? - 错误响应、日志、缓存、导出文件、调试接口是否泄露敏感信息? - 密钥、密码、令牌、证书是否硬编码或以不安全方式传递? - 依赖、锁文件、Dockerfile、CI/CD、下载脚本是否存在供应链污染入口? - 正则、XML、解压、递归、队列、网络请求是否可被恶意输入耗尽资源? - C/C++/unsafe/FFI 是否存在越界读写、UAF、双重释放、整数溢出或格式字符串风险? ### 高质量问题示例 ``` 问题:订单详情接口只校验登录态,未校验对象归属,导致 IDOR。 位置:src/orders/api.py:42-51 分类: - OWASP: 失效的访问控制 - CWE: CWE-639 / CWE-862 - STRIDE: Information Disclosure / Elevation of Privilege 现象: ```python def get_order(order_id): require_login() return Order.query.get(order_id) ``` 影响: - 攻击者登录普通账号后可遍历 order_id - 可读取其他用户订单、地址、支付摘要等敏感数据 优先级:P0 修复建议: 在查询条件中绑定当前用户,或调用集中对象级授权检查: ```python def get_order(order_id, current_user): order = Order.query.filter_by(id=order_id, user_id=current_user.id).one_or_none() if order is None: raise Forbidden() return order ``` 验证方法: - 用户 A 请求用户 B 的订单,应返回 403/404 - 新增对象级授权回归测试 ``` ### 算法问题 | 类型 | 示例 | 影响 | |------|------|------| | **时间复杂度过高** | 嵌套循环 O(n²) | 性能瓶颈 | | **空间复杂度浪费** | 不必要的数据复制 | 内存浪费 | | **数据结构不当** | 列表用于频繁查找 | 性能问题 | | **重复计算** | 循环内重复计算 | 性能问题 | ### 边界问题 | 类型 | 示例 | 影响 | |------|------|------| | **空输入未处理** | None / "" 导致崩溃 | 运行时异常 | | **除零错误** | 除数未检查 | 崩溃 | | **数组越界** | 未检查索引 | 崩溃/数据损坏 | | **整数溢出** | 未检查溢出 | 数据错误 | ### 并发问题 | 类型 | 示例 | 影响 | |------|------|------| | **竞态条件** | 检查-使用模式 | 数据不一致 | | **数据竞争** | 无保护的共享变量 | 未定义行为 | | **死锁** | 锁顺序不一致 | 程序挂起 | | **活锁** | 无退避的重试 | 性能问题 | ### 资源问题 | 类型 | 示例 | 影响 | |------|------|------| | **内存泄漏** | 未释放大对象 | 内存耗尽 | | **文件描述符泄漏** | 未关闭文件 | 无法打开新文件 | | **连接泄漏** | 未释放数据库连接 | 连接池耗尽 | | **循环引用** | 回调函数引用 | 内存泄漏 | ### 设计问题 | 类型 | 示例 | 影响 | |------|------|------| | **扩展性差** | 新增类型需要修改多个分支 | 演化成本高 | | **架构混乱** | UI 直接操作数据库 | 层次失真,难测试 | | **API 契约不一致** | 同类接口返回结构不同 | 调用方复杂度上升 | | **状态管理脆弱** | 状态切换逻辑散落多处 | 容易出现非法状态 | | **领域建模粗糙** | 贫血模型 + 业务逻辑外泄 | 规则难维护 | | **高耦合低内聚** | 一个类同时管缓存/IO/业务 | 改动面扩大 | | **模式误用/缺失** | 用长分支代替策略模式 | 可扩展性下降 | --- ## 数量要求(强制) ### A 轮要求 **最低要求**: - P0 + P1 + P2 总和 ≥ 10 - P0 + P1 占比 ≥ 60% - 系统性问题 ≥ 3(算法/边界/并发/资源/设计) **推荐分布**: - P0:2-4 个(崩溃风险、安全漏洞、资源泄漏) - P1:4-8 个(性能问题、边界缺陷、逻辑错误) - P2:3-6 个(代码风格、可读性) --- ## 使用指南 ### 组合使用技巧 为达到 10-20 个高质量问题,建议组合使用: 1. **静态分析**(框架 1)→ 发现 3-5 个算法/复杂度/设计问题(P0/P1) 2. **动态推理**(框架 2)→ 发现 3-5 个边界/并发/资源问题(P0/P1) 3. **设计质量视角**(框架 4)→ 发现 2-4 个架构/API/状态问题(P1) 4. **安全漏洞分类视角**(框架 5)→ 发现 2-5 个安全/威胁建模/供应链问题(P0/P1) 5. **代码阅读**(CODE_SMELLS.md)→ 发现 3-6 个细节问题(P1/P2) **总计**:10-18 个问题,P0+P1 占比 ≥ 60% -
DESIGN_ANTI_PATTERNS.md 11 KB
# 设计反模式识别指南 **文档版本**:v1.0.0 **创建时间**:2026-04-04 **用途**:为 auto-test-code 提供设计质量审查时的反模式知识库 --- ## 使用方式 当 A/B 轮审查发现“代码能运行,但扩展、理解、替换、测试都很痛苦”时,优先参考本指南。 每类反模式都包含: - 特征描述 - 典型反模式 - 反模式 vs 正确模式代码示例 - 影响分析 - 重构方向 --- ## 扩展性反模式 ### 类型分发 if-else 链 - **特征描述**:新增一种类型就要修改既有分发函数。 - **典型反模式**:长 if-elif / switch;靠字符串判断行为。 ```python # 反模式 def build_exporter(kind): if kind == "csv": return CsvExporter() if kind == "json": return JsonExporter() if kind == "xlsx": return XlsxExporter() raise ValueError(kind) ``` ```python # 正确模式 EXPORTERS = { "csv": CsvExporter, "json": JsonExporter, "xlsx": XlsxExporter, } def build_exporter(kind): try: return EXPORTERS[kind]() except KeyError as exc: raise ValueError(kind) from exc ``` - **影响分析**:违反开闭原则;类型越多,回归成本越高。 - **重构方向**:改为策略表、工厂模式或插件注册表。 ### 硬编码配置 - **特征描述**:阈值、路径、超时等散落在代码分支里。 - **典型反模式**:环境差异靠改源码实现。 - **影响分析**:一旦环境变化,必须改代码重新发布。 - **重构方向**:把配置外置到配置文件、环境变量或参数对象。 ### 继承层级过深 - **特征描述**:新增行为必须理解多层父类。 - **典型反模式**:5 层以上继承链;子类仅为绕过父类限制。 - **影响分析**:修改一个父类可能破坏多个子类。 - **重构方向**:优先组合优于继承,拆出可替换能力组件。 ### 新增功能需改多个文件 - **特征描述**:每次加功能都要改路由、校验、映射、文案等一串分散位置。 - **典型反模式**:散弹枪手术。 - **影响分析**:极易漏改;需求变更成本指数上升。 - **重构方向**:把同类变化收敛到单一扩展点。 --- ## 架构反模式 ### 层次穿透 - **特征描述**:上层直接访问底层存储、SQL 或外部服务细节。 - **典型反模式**:UI 组件直接调数据库;业务层直接拼 HTTP 请求。 ```python # 反模式 def render_user_page(user_id): row = db.execute(f"SELECT * FROM users WHERE id = {user_id}").fetchone() return f"<h1>{row['name']}</h1>" ``` ```python # 正确模式 def render_user_page(user_id, user_service): user = user_service.get_user(user_id) return f"<h1>{user.display_name}</h1>" ``` - **影响分析**:边界模糊,测试困难,安全风险上升。 - **重构方向**:明确分层职责,通过 service / repository 等边界隔离细节。 ### 循环依赖 - **特征描述**:A 依赖 B,B 又依赖 A。 - **典型反模式**:初始化顺序复杂;局部改动触发大面积联动。 - **影响分析**:模块无法独立测试,容易引入导入时副作用。 - **重构方向**:提取接口、事件或共享抽象,打断循环。 ### 上帝对象 - **特征描述**:一个类既管配置、又管 IO、又管业务。 - **典型反模式**:超大类、超长文件、超多 public 方法。 - **影响分析**:责任不清,任何需求都改它。 - **重构方向**:按职责拆分为多个协作者。 ### 散弹枪手术 - **特征描述**:一个小需求需要修改多个看似不相关的模块。 - **典型反模式**:横切逻辑没有集中点。 - **影响分析**:改动面大,回归风险高。 - **重构方向**:围绕变化轴重组模块。 --- ## API 设计反模式 ### 参数语义不一致 - **特征描述**:同类接口参数顺序、命名、默认值不一致。 - **典型反模式**:`create_user(name, id)` 和 `update_user(id, username)` 混用。 - **影响分析**:调用者容易误用,文档和代码都难记。 - **重构方向**:统一契约,必要时引入参数对象。 ### 接口粒度极端 - **特征描述**:一个接口什么都做,或拆成过多琐碎接口。 - **典型反模式**:God API / chatty API。 - **影响分析**:前者难维护,后者调用成本高。 - **重构方向**:以真实业务动作而非实现步骤设计接口。 ### 泄露实现细节 - **特征描述**:接口要求调用方知道内部表名、缓存键、状态编码。 - **典型反模式**:把内部枚举值、SQL 片段暴露给外部。 - **影响分析**:内部重构会波及所有调用者。 - **重构方向**:对外暴露稳定语义,对内自由演化实现。 ### 返回类型不一致 - **特征描述**:相似操作有时返回 dict,有时返回 tuple,有时返回 bool。 - **典型反模式**:历史演化中逐步失控。 - **影响分析**:调用端需要额外适配分支。 - **重构方向**:统一返回对象、错误模型和状态语义。 --- ## 状态管理反模式 ### 隐式状态机 - **特征描述**:状态转换逻辑散落在多个函数和布尔变量中。 - **典型反模式**:`is_ready`、`is_done`、`has_error` 多个标志互相组合。 ```python # 反模式 class Job: def __init__(self): self.is_running = False self.is_done = False self.has_error = False ``` ```python # 正确模式 from enum import Enum class JobState(Enum): PENDING = "pending" RUNNING = "running" FAILED = "failed" DONE = "done" ``` - **影响分析**:非法状态组合难以避免。 - **重构方向**:显式建模状态枚举或状态机。 ### 无保护的状态转换 - **特征描述**:任意状态都能直接跳到任意状态。 - **典型反模式**:缺少转换校验。 - **影响分析**:产生业务上不可能的状态。 - **重构方向**:集中定义合法转换表。 ### 状态与行为分离 - **特征描述**:对象只存状态,所有行为都在外部 service。 - **典型反模式**:贫血模型和外置状态规则。 - **影响分析**:规则散落,难以发现不变量。 - **重构方向**:让状态对象承载与状态相关的行为和约束。 ### 忘记处理中间/异常状态 - **特征描述**:只考虑成功和失败,忽略重试中、取消中、部分成功。 - **典型反模式**:流程一复杂就开始补丁式修复。 - **影响分析**:线上状态回滚、补偿逻辑容易失真。 - **重构方向**:在状态设计阶段显式列出中间态和异常态。 --- ## 领域建模反模式 ### 贫血模型 - **特征描述**:对象只有 getter/setter,业务规则都在 Service 层。 - **典型反模式**:`Order` 只存数据,`OrderService` 决定一切。 ```python # 反模式 class Order: def __init__(self, total): self.total = total self.status = "pending" ``` ```python # 正确模式 class Order: def __init__(self, total): self.total = total self.status = "pending" def pay(self) -> None: if self.total <= 0: raise ValueError("invalid order total") self.status = "paid" ``` - **影响分析**:不变量无法封装,调用方可随意破坏规则。 - **重构方向**:把关键业务规则拉回领域对象。 ### 基本类型偏执 - **特征描述**:邮箱、金额、用户 ID 等概念都直接用 string/int 表示。 - **典型反模式**:相同基础类型承担多个业务含义。 - **影响分析**:校验缺失,参数传错后难以及时发现。 - **重构方向**:为关键概念引入值对象。 ### 忽略聚合根边界 - **特征描述**:外部可直接修改对象内部集合或子实体。 - **典型反模式**:任何代码都能 `order.items.append(...)`。 - **影响分析**:业务规则失守,状态难以回溯。 - **重构方向**:通过聚合根暴露受控操作。 ### 领域规则泄露到基础设施层 - **特征描述**:数据库层、缓存层、UI 层包含核心业务判断。 - **典型反模式**:SQL 里埋业务状态机;模板里埋权限规则。 - **影响分析**:业务逻辑分裂,难以统一演进。 - **重构方向**:收敛规则到领域或应用服务层。 --- ## 耦合/内聚反模式 ### 直接依赖具体实现 - **特征描述**:模块直接实例化底层实现,无法替换。 - **典型反模式**:业务逻辑里 `S3Client()`、`Redis()` 到处 new。 - **影响分析**:测试难 mock,切换实现成本高。 - **重构方向**:依赖抽象,通过注入传入协作者。 ### 低内聚 - **特征描述**:一个类的方法操作完全不同的数据子集。 - **典型反模式**:一个 manager 同时管用户、账单、通知。 - **影响分析**:类名无法准确描述职责。 - **重构方向**:按职责拆分类和模块。 ### 传递依赖链过深 - **特征描述**:A 调 B,B 调 C,C 调 D,调用链一长就难定位。 - **典型反模式**:控制反转不足或边界抽象失衡。 - **影响分析**:问题定位和性能分析困难。 - **重构方向**:缩短依赖链,必要时引入门面层。 ### 变更一个需求需改多个模块 - **特征描述**:同一个业务概念在多个模块重复编码。 - **典型反模式**:文案、校验、映射表分别维护。 - **影响分析**:漏改概率高。 - **重构方向**:围绕业务概念集中表达。 --- ## 设计模式反模式 ### 单例滥用 - **特征描述**:把全局可变状态包装成单例。 - **典型反模式**:所有模块都能随时改共享状态。 - **影响分析**:测试污染、并发风险、隐藏耦合。 - **重构方向**:显式传递依赖,限制共享可变状态。 ### 过度抽象 - **特征描述**:只有一种实现却预先建接口、工厂、装饰器全家桶。 - **典型反模式**:为“可能永远不会发生的扩展”付出复杂度。 - **影响分析**:认知负担增加,排错路径变长。 - **重构方向**:遵循 YAGNI,需要时再抽象。 ### 模式误用 - **特征描述**:复杂模式被用于非常简单的问题。 - **典型反模式**:用访问者模式处理两个 if 分支。 - **影响分析**:可读性下降,团队维护成本高。 - **重构方向**:选择与问题规模匹配的模式。 ### 模式缺失 - **特征描述**:明显适合策略/工厂/观察者/状态模式的场景仍用硬编码分支。 - **典型反模式**:靠复制粘贴新增行为。 - **影响分析**:扩展性差,分支爆炸。 - **重构方向**:在演化痛点稳定出现后再引入合适模式。 --- ## 设计质量快速检查清单 - 新增一种业务类型时,是否主要靠“扩展”而不是“修改旧代码”? - 是否存在层次穿透、循环依赖或职责失控的超大模块? - API 参数、返回值与错误模型是否一致? - 状态转换是否显式且受保护? - 业务规则是否封装在恰当边界内? - 关键模块是否低耦合、高内聚? - 当前模式选择是“太少”还是“太多”? -
SECURITY_PATTERNS.md 6.1 KB
# 安全漏洞模式库 **用途**:快速识别常见安全漏洞。 完整分类审查口径见 `SECURITY_TAXONOMY.md`。本文件提供快速模式库;每轮安全维度不能只停留在本文件的 11 个模式,还必须覆盖 CWE/OWASP/STRIDE/七大王国/CVSS、供应链、配置运维、密码学、认证授权、DoS 与内存安全。 --- ## 注入类漏洞 ### 1. SQL 注入(SQL Injection) **特征**:用户输入直接拼接到 SQL 语句 **危险度**:P0(可能导致数据泄露/篡改) **示例**: ```python # 危险 def get_user(user_id): query = f"SELECT * FROM users WHERE id = {user_id}" cursor.execute(query) # 安全 def get_user(user_id): query = "SELECT * FROM users WHERE id = %s" cursor.execute(query, (user_id,)) ``` **检测方法**: - 搜索字符串拼接的 SQL 查询 - 检查是否使用参数化查询 --- ### 2. 命令注入(Command Injection) **特征**:用户输入直接用于系统命令 **危险度**:P0(可能导致系统被完全控制) **示例**: ```python # 危险 def convert_file(filename): os.system(f"convert {filename} output.pdf") # 安全 def convert_file(filename): # 验证文件名 if not re.match(r'^[\w.-]+$', filename): raise ValueError("Invalid filename") subprocess.run(["convert", filename, "output.pdf"]) ``` **检测方法**: - 搜索 os.system()、subprocess.call(shell=True) - 检查输入验证 --- ### 3. 路径遍历(Path Traversal) **特征**:用户输入用于文件路径,未做验证 **危险度**:P0(可能访问任意文件) **示例**: ```python # 危险 def read_file(filename): path = f"./data/{filename}" return open(path).read() # 安全 def read_file(filename): # 规范化路径 path = os.path.normpath(f"./data/{filename}") base_dir = os.path.realpath("./data") real_path = os.path.realpath(path) # 验证路径在允许范围内 if not real_path.startswith(base_dir): raise ValueError("Invalid path") return open(real_path).read() ``` **检测方法**: - 搜索 open()、read() 使用用户输入 - 检查路径验证逻辑 --- ### 4. XSS(跨站脚本) **特征**:用户输入直接输出到 HTML **危险度**:P0(可能导致用户会话劫持) **示例**: ```python # 危险 def render_page(username): return f"<h1>Welcome {username}</h1>" # 安全 def render_page(username): from html import escape return f"<h1>Welcome {escape(username)}</h1>" ``` --- ## 认证与授权 ### 5. 硬编码凭证 **特征**:密钥/密码硬编码在代码中 **危险度**:P0(凭证泄露) **示例**: ```python # 危险 API_KEY = "sk-1234567890abcdef" DB_PASSWORD = "admin123" # 安全 API_KEY = os.getenv("API_KEY") DB_PASSWORD = os.getenv("DB_PASSWORD") ``` **检测方法**: - 搜索 "password"、"api_key"、"secret" - 使用 secrets 扫描工具 --- ### 6. 弱密码哈希 **特征**:使用 MD5/SHA1 哈希密码 **危险度**:P1(密码容易被破解) **示例**: ```python # 危险 def hash_password(password): return hashlib.md5(password.encode()).hexdigest() # 安全 def hash_password(password): return bcrypt.hashpw(password.encode(), bcrypt.gensalt()) ``` --- ### 7. 会话固定 **特征**:登录后未更新会话 ID **危险度**:P1(会话劫持) **示例**: ```python # 危险 def login(username, password): if authenticate(username, password): session['user_id'] = user.id # 未更新会话 ID ``` --- ## 数据安全 ### 8. 敏感信息泄露 **特征**:错误消息/日志包含敏感信息 **危险度**:P1(信息泄露) **示例**: ```python # 危险 try: result = process payment(card_data) except Exception as e: logging.error(f"Payment failed: {e}\nData: {card_data}") # 安全 try: result = process_payment(card_data) except Exception as e: logging.error(f"Payment failed: {e}") # 不记录敏感数据 ``` --- ### 9. 不安全的随机数 **特征**:使用伪随机数生成器生成安全 token **危险度**:P0(token 可预测) **示例**: ```python # 危险 import random token = random.randint(0, 1000000) # 安全 import secrets token = secrets.randbelow(1000000) ``` --- ## 加密问题 ### 10. 不安全的加密算法 **特征**:使用 DES/RC4/ECB 模式 **危险度**:P0(数据可被解密) **示例**: ```python # 危险 from Crypto.Cipher import DES cipher = DES.new(key, DES.MODE_ECB) # 安全 from Crypto.Cipher import AES cipher = AES.new(key, AES.MODE_GCM) ``` --- ## 并发安全 ### 11. 竞态条件 **特征**:检查-使用模式未原子化 **危险度**:P0(可能导致数据不一致) **示例**: ```python # 危险 if not os.path.exists(filename): with open(filename, 'w') as f: # 竞态窗口 f.write(data) # 安全 fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL) with os.fdopen(fd, 'w') as f: f.write(data) ``` --- ## 检查清单 在代码审查时,快速检查: - [ ] 是否有 SQL 注入风险?(字符串拼接 SQL) - [ ] 是否有命令注入风险?(os.system 使用用户输入) - [ ] 是否有路径遍历风险?(文件路径未验证) - [ ] 是否有 XSS 风险?(用户输入未转义) - [ ] 是否有硬编码凭证?(密钥/密码在代码中) - [ ] 是否使用弱加密?(MD5/SHA1/DES) - [ ] 是否使用不安全的随机数?(random 模块) - [ ] 错误消息是否泄露敏感信息? - [ ] 是否有竞态条件?(检查-使用模式) - [ ] 是否有认证/授权缺失或 IDOR?(对象级/功能级权限) - [ ] 是否有 SSRF?(服务端请求内网/云元数据/文件协议) - [ ] 是否有不可信反序列化、模板注入、XXE、NoSQL/LDAP/XPath 注入? - [ ] 是否有供应链风险?(未锁版本、CI/CD 注入、依赖混淆、未验证下载/镜像) - [ ] 是否有配置风险?(调试模式、CORS 任意源、TLS/安全头缺失、云资源公开) - [ ] 是否有 DoS 风险?(ReDoS、XML/解压炸弹、无限队列/递归/连接、无速率限制) - [ ] 是否有内存安全风险?(越界读写、UAF、双重释放、整数溢出、格式字符串) -
SECURITY_TAXONOMY.md 9.4 KB
# 安全漏洞分类审查体系 **用途**:把代码安全漏洞分类体系转化为 `auto-test-code` 的可执行审查口径。 本文件来自项目内深度报告 `deep_research/reports/代码安全漏洞分类体系/代码安全漏洞分类体系-深度报道.md` 的知识框架整理,目标不是复制报告全文,而是提供每轮 A/B 审查可直接使用的检查维度、问题类型和优先级判定依据。 --- ## 使用方式 每轮代码审查必须把安全作为独立维度覆盖: 1. 先按攻击面识别入口:外部输入、身份边界、权限边界、文件/网络/进程/数据库/缓存/队列、依赖与构建链路。 2. 再按分类框架交叉检查:CWE 根因、OWASP 风险、STRIDE 威胁、七大王国、CVSS 影响。 3. 最后给出可验证问题:每个安全问题必须包含 `位置 + 可利用场景 + 影响资产 + 修复方案 + 验证方法`。 安全问题优先级默认规则: - **P0**:可导致未授权访问、远程代码执行、凭据泄露、敏感数据泄露/篡改、任意文件读写、供应链植入、严重 DoS、内存破坏或权限提升。 - **P1**:存在可利用前置条件、影响范围有限或需要组合利用的认证授权、配置、加密、日志、依赖、并发时序问题。 - **P2**:防御纵深不足、审计信息不完整、安全头/默认配置可加固、测试覆盖缺口等。 --- ## 六大分类框架 | 框架 | 审查视角 | 在代码审查中的用途 | |------|----------|--------------------| | CWE | 代码弱点根因 | 定位输入验证、内存安全、认证授权、配置、资源管理等具体缺陷 | | OWASP Top 10 | Web/API 业务风险 | 判断访问控制、配置、供应链、加密、注入、认证、完整性、日志、SSRF 等风险 | | MITRE ATT&CK | 攻击者行为 | 推演漏洞是否支持初始访问、执行、权限提升、凭据访问、横向移动、数据外泄或影响 | | STRIDE | 安全属性违反 | 从身份欺骗、篡改、否认、信息泄露、拒绝服务、权限提升检查威胁 | | 七大王国 | 技术域根因 | 覆盖输入验证、API 滥用、安全特性、时间状态、错误处理、代码质量、封装 | | CVSS | 严重程度 | 用攻击向量、复杂度、权限、用户交互、机密性/完整性/可用性影响辅助排序 | --- ## CWE Top 高危根因 审查时优先寻找下列高危弱点: | 类别 | 典型 CWE | 检查重点 | |------|----------|----------| | 注入 | CWE-79, CWE-89, CWE-78, CWE-77, CWE-94, CWE-502 | SQL/命令/代码/XSS/反序列化/模板/NoSQL/日志/CRLF 注入 | | 输入验证 | CWE-20, CWE-22 | 外部输入是否做类型、范围、格式、路径、编码、长度和协议校验 | | 内存安全 | CWE-787, CWE-125, CWE-119, CWE-416, CWE-120, CWE-190 | 越界读写、UAF、缓冲区溢出、整数溢出、空指针、未初始化内存 | | 认证授权 | CWE-287, CWE-306, CWE-862, CWE-863, CWE-269 | 缺少认证、认证绕过、水平/垂直越权、权限管理不当 | | 信息泄露 | CWE-200, CWE-209 | 日志、错误响应、调试端点、接口返回、缓存或导出文件泄露敏感信息 | | 配置安全 | CWE-276, CWE-16 | 默认权限、调试配置、目录列表、CORS、TLS、安全头、云资源暴露 | | 资源与 DoS | CWE-400, CWE-770, CWE-185, CWE-776 | 资源耗尽、ReDoS、XML/解压炸弹、无限队列、无速率限制 | --- ## OWASP 风险清单 Web/API 项目必须逐项检查: - **失效的访问控制**:IDOR、管理接口无校验、对象级/功能级权限缺失、默认权限过宽。 - **安全配置错误**:生产调试模式、CORS 任意源、缺少安全头、云存储公开、目录列表开启。 - **软件供应链失败**:依赖混淆、typosquatting、未锁版本、CI/CD 注入、未验证构建产物/镜像签名。 - **密码学失败**:弱算法、硬编码密钥、IV/Nonce 重用、明文传输、密码明文/可逆存储。 - **注入**:SQL、命令、代码、模板、LDAP、XPath、NoSQL、XXE、日志/CRLF 注入。 - **脆弱和过时组件**:过期依赖、基础镜像、运行时或插件;缺少安全升级策略。 - **身份识别和认证失败**:弱密码、会话固定、会话 ID 可预测、MFA 绕过、暴力破解防护不足。 - **软件和数据完整性失败**:不可信反序列化、自动更新未验证、构建脚本可被外部输入污染。 - **安全日志和监控失败**:关键安全事件不记录、日志可伪造、缺少失败/越权/异常访问告警。 - **SSRF**:服务端请求可访问内网、云元数据、Unix socket、文件协议或绕过域名/IP allowlist。 --- ## STRIDE 威胁建模清单 对每个外部入口、数据流和信任边界逐项提问: | STRIDE | 违反属性 | 审查问题 | |--------|----------|----------| | Spoofing | 认证性 | 调用方身份是否可信?Token、签名、会话、mTLS 是否可伪造或重放? | | Tampering | 完整性 | 请求、文件、消息、缓存、构建产物是否可被未授权篡改? | | Repudiation | 不可否认性 | 关键操作是否有不可伪造、可关联用户/请求的审计记录? | | Information Disclosure | 机密性 | 响应、日志、错误、导出、缓存、临时文件是否泄露敏感信息? | | Denial of Service | 可用性 | 输入大小、循环、递归、正则、连接、队列、锁是否可被耗尽? | | Elevation of Privilege | 授权性 | 低权限用户是否能访问高权限功能、数据或执行上下文? | --- ## 七大王国检查清单 | 王国 | 典型问题 | 审查要点 | |------|----------|----------| | 输入验证与表示 | 注入、XSS、路径穿越、编码绕过、ReDoS | 对所有外部输入做白名单、规范化、长度限制、编码一致性与上下文转义 | | API 滥用 | 忽略返回值、参数误用、不安全回调、协议流程缺失 | 检查安全 API 调用前后置条件、返回码、异常和失败路径 | | 安全特性 | 认证、授权、加密、会话、凭据、随机数缺陷 | 安全机制必须集中、默认拒绝、失败关闭、可审计 | | 时间和状态 | 竞态、TOCTOU、状态机缺陷、死锁 | 检查多步检查/使用、共享状态、锁顺序、临时文件和异步回调 | | 错误处理 | 信息泄露、异常吞没、不当清理、状态不一致 | 错误响应最小化,对内日志脱敏,失败路径释放资源并保持一致状态 | | 代码质量 | 内存安全、整数溢出、初始化、类型安全、资源泄漏 | 对底层语言加强边界、生命周期、所有权、类型转换和资源释放检查 | | 封装 | 调试接口、内部实现暴露、沙箱逃逸、租户隔离不足 | 检查数据/功能隔离、内部接口暴露、跨租户边界和生产调试面 | --- ## 专项漏洞族 ### 注入类 - SQL/NoSQL/LDAP/XPath:禁止拼接查询;使用参数化、绑定变量或安全查询构造器。 - OS 命令/代码/模板/表达式:避免把外部输入解释为命令或代码;必须做 allowlist 和参数数组调用。 - XSS/CRLF/日志注入:根据输出上下文转义;日志字段结构化并清理换行与控制字符。 - XXE/反序列化:禁用外部实体;不反序列化不可信对象;使用签名、allowlist 或安全格式。 ### 认证与授权 - 区分认证、功能级授权和数据级授权。 - 检查水平越权、垂直越权、默认权限过宽、敏感端点缺失认证。 - 会话登录后必须轮换 ID;Cookie 需 `HttpOnly`、`Secure`、`SameSite`;Token 需过期、受众和签名验证。 - 登录、重置密码、MFA、邀请链接、API key 管理必须有速率限制和审计。 ### 配置与运维 - 生产环境禁止调试模式、默认口令、目录列表、测试端点和过宽 CORS。 - TLS、CSP、HSTS、X-Frame-Options、X-Content-Type-Options 等安全配置需按场景检查。 - 云存储、数据库、安全组、IAM、Kubernetes/RBAC、容器权限应遵循最小权限。 ### 供应链 - 检查锁文件、版本范围、已知漏洞依赖、基础镜像、构建脚本、CI secrets、发布签名。 - 防止依赖混淆、typosquatting、install/postinstall 脚本植入、未验证下载和缓存投毒。 ### 密码学与数据保护 - 禁用 DES、RC4、MD5、SHA-1、ECB;优先使用成熟库与 AEAD 模式。 - 密钥不得硬编码;需要轮换、权限隔离和最小暴露。 - IV/Nonce 不得重用;安全 token 必须使用密码学安全随机数。 - 敏感数据传输和存储需要加密,日志和错误响应要脱敏。 ### 内存、并发与 DoS - C/C++/Rust unsafe/FFI 重点检查越界、UAF、双重释放、整数溢出、格式字符串、未初始化内存。 - 并发重点检查 TOCTOU、临时文件、共享状态、锁顺序、信号处理和状态机转换。 - DoS 重点检查 ReDoS、XML/解压炸弹、大输入、无限递归、无限队列、无超时网络调用和无速率限制接口。 --- ## 报告写法 安全问题建议使用以下格式: ```markdown 1) 标题:未授权用户可通过对象 ID 读取他人订单 - 位置:`src/orders/api.py:42` - 分类:OWASP 失效的访问控制 / CWE-639 IDOR / STRIDE 信息泄露 - 可利用场景:攻击者登录普通账号后遍历 `order_id` - 影响资产:其他用户订单、地址、支付摘要 - 优先级:P0(敏感数据未授权访问) - 修复建议:在查询条件中绑定 `current_user.id`,并集中复用对象级授权检查 - 验证方法:用用户 A 登录请求用户 B 的订单,应返回 403;补充回归测试 ```
-
-
scripts
-
create_session.py 18.6 KB
#!/usr/bin/env python3 from __future__ import annotations import argparse import datetime as dt import json import re import shutil import sys import typing from pathlib import Path _TEST_ID_RE = re.compile(r"^v\d{12}$") _RUN_ID_RE = re.compile(r"^(?:\d{4}-\d{2}-\d{2}-\d{2}-\d{2}(?:-\d{2})?|run_\d{14})$") _DEFAULT_DIRECTORIES = { "tmp": "tmp", "tests": "tests", } _DEFAULT_TEMPLATES = { "code_review": "templates/CODE_REVIEW_TEMPLATE.md", "b_round_quality": "templates/B_ROUND_CODE_QUALITY_TEMPLATE.md", "session_test_plan": "templates/SESSION_TEST_PLAN_TEMPLATE.md", "session_test_run": "templates/SESSION_TEST_RUN_TEMPLATE.md", "session_test_report": "templates/SESSION_TEST_REPORT_TEMPLATE.md", } def _generate_test_id(now: dt.datetime) -> str: return f"v{now:%Y%m%d%H%M}" def _generate_run_id(now: dt.datetime) -> str: return f"{now:%Y-%m-%d-%H-%M}" def _ensure_dir(path: Path) -> None: path.mkdir(parents=True, exist_ok=True) def _safe_write(path: Path, content: str, *, overwrite: bool) -> None: if path.exists() and not overwrite: raise FileExistsError(f"Refusing to overwrite existing file: {path}") path.write_text(content, encoding="utf-8") def _render_template(template: str, *, values: dict[str, str]) -> str: rendered = template for key, value in values.items(): rendered = rendered.replace(f"{{{{{key}}}}}", value) return rendered def _copy_or_template( *, dst_path: Path, src_path: Path | None, template_path: Path | None, template_values: dict[str, str] | None, overwrite: bool, ) -> None: if dst_path.exists() and not overwrite: return if src_path is not None and src_path.exists(): if dst_path.exists(): dst_path.unlink() shutil.copyfile(src_path, dst_path) return if template_path is not None and template_path.exists(): template_text = template_path.read_text(encoding="utf-8") if template_values: template_text = _render_template(template_text, values=template_values) _safe_write(dst_path, template_text, overwrite=overwrite) return _safe_write( dst_path, "# TEST_PLAN\n\n(未找到可复制的计划文档或模板,请手动补全)\n", overwrite=overwrite, ) def _normalize_kind(kind: str) -> str: kind = kind.strip().lower() if kind in {"a", "a_round", "a-round", "a轮"}: return "a" if kind in {"b", "b_round", "b-round", "b轮"}: return "b" raise ValueError("kind must be a/b (also accepts: A轮/B轮)") def _fail(parser: argparse.ArgumentParser, message: str) -> typing.NoReturn: parser.print_usage(sys.stderr) print(f"error: {message}", file=sys.stderr) raise SystemExit(2) def _strip_inline_comment(value: str) -> str: if "#" not in value: return value return value.split("#", 1)[0].rstrip() def _parse_simple_yaml_sections(text: str, *, wanted_sections: set[str]) -> dict[str, dict[str, str]]: """ Parse a minimal subset of YAML for auto-test-code. """ result: dict[str, dict[str, str]] = {} current: str | None = None for raw in text.splitlines(): line = raw.rstrip("\n") if not line.strip() or line.lstrip().startswith("#"): continue if not line.startswith(" ") and line.endswith(":"): section = line[:-1].strip() current = section if section in wanted_sections else None continue if current is None: continue if line.startswith(" ") and ":" in line: key, value = line.split(":", 1) key = key.strip() value = _strip_inline_comment(value.strip()) if not value: continue if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")): value = value[1:-1] result.setdefault(current, {})[key] = value return result def _load_config_sections(config_path: Path) -> dict[str, dict[str, str]]: wanted = {"directories", "templates"} if not config_path.exists(): return {} text = config_path.read_text(encoding="utf-8") try: import yaml # type: ignore except Exception: return _parse_simple_yaml_sections(text, wanted_sections=wanted) try: data = yaml.safe_load(text) or {} except Exception: return _parse_simple_yaml_sections(text, wanted_sections=wanted) out: dict[str, dict[str, str]] = {} for section in wanted: v = data.get(section) if isinstance(v, dict): out[section] = {str(k): str(vv) for k, vv in v.items() if isinstance(vv, (str, int, float))} return out def _merge_section( *, base: dict[str, str], override: dict[str, str] | None, ) -> dict[str, str]: merged = dict(base) if override: merged.update({k: v for k, v in override.items() if v}) return merged def _safe_rel_path(value: str, *, default: str) -> str: if not value: return default p = Path(value) if p.is_absolute() or ".." in p.parts: return default return value def _resolve_template_path( *, target_code_root: Path, bundled_skill_root: Path, rel_path: str, ) -> Path | None: def _candidate_within(root: Path) -> Path | None: candidate = root / rel_path if not candidate.exists(): return None resolved = candidate.resolve() try: resolved.relative_to(root) except ValueError: return None return resolved return _candidate_within(target_code_root) or _candidate_within(bundled_skill_root) def _ensure_dir_within_root( parser: argparse.ArgumentParser, *, code_root: Path, path: Path, label: str, ) -> None: if path.exists(): if path.is_symlink(): _fail(parser, f"{label} must not be a symlink: {path}") if not path.is_dir(): _fail(parser, f"{label} must be a directory: {path}") _ensure_dir(path) resolved = path.resolve() try: resolved.relative_to(code_root) except ValueError: _fail(parser, f"{label} resolves outside allowed root: {path} -> {resolved}") def _write_run_manifest( *, run_dir: Path, code_root: Path, tmp_dir: Path, tests_dir: Path, run_id: str, now: dt.datetime, overwrite: bool, ) -> None: manifest_path = run_dir / ".auto-test-code-run.json" if manifest_path.exists() and not overwrite: return manifest = { "run_id": run_id, "created_at": now.isoformat(timespec="seconds"), "code_root": code_root.as_posix(), "tmp_dir_rel": tmp_dir.relative_to(code_root).as_posix(), "run_dir_rel": run_dir.relative_to(code_root).as_posix(), "tests_dir_rel": tests_dir.relative_to(code_root).as_posix(), } _safe_write( manifest_path, json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", overwrite=overwrite, ) def _detect_code_language(code_root: Path) -> str: """检测代码语言(基于常见文件扩展名)""" ignored_parts = { ".git", "tmp", "node_modules", "venv", ".venv", "__pycache__", "dist", "build", } lang_counts: dict[str, int] = { "Python": 0, "JavaScript": 0, "TypeScript": 0, "Java": 0, "Go": 0, "Rust": 0, "C/C++": 0, } for ext in code_root.rglob("*"): if any(part in ignored_parts for part in ext.relative_to(code_root).parts): continue if ext.is_file(): suffix = ext.suffix.lower() if suffix == ".py": lang_counts["Python"] += 1 elif suffix in {".js", ".jsx"}: lang_counts["JavaScript"] += 1 elif suffix in {".ts", ".tsx"}: lang_counts["TypeScript"] += 1 elif suffix == ".java": lang_counts["Java"] += 1 elif suffix == ".go": lang_counts["Go"] += 1 elif suffix == ".rs": lang_counts["Rust"] += 1 elif suffix in {".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"}: lang_counts["C/C++"] += 1 # 返回文件数最多的语言 max_lang = max(lang_counts, key=lang_counts.get) return max_lang if lang_counts[max_lang] > 0 else "Unknown" def main() -> int: parser = argparse.ArgumentParser( description="Create an auto-test-code session skeleton under tmp/run_*/tests/ (A round or B round).", ) parser.add_argument( "--code-root", required=True, help="Target code root directory.", ) parser.add_argument( "--kind", default="a", help="Session kind: a (default) or b (also accepts: A轮/B轮).", ) parser.add_argument( "--id", default="", help="Explicit test id like vYYYYMMDDHHMM (optional).", ) parser.add_argument( "--run-id", default="", help="Run workspace id like YYYY-MM-DD-HH-MM. Reuse the same id across A/B rounds in one skill execution.", ) parser.add_argument( "--create-review", action="store_true", help="(Deprecated flag) Create REVIEW.md skeleton in the session directory (default: create if missing).", ) parser.add_argument( "--seed-test-plan-from-review", action="store_true", help="Copy REVIEW.md into TEST_PLAN.md (advanced).", ) parser.add_argument( "--a-test-id", default="", help="For B round: the corresponding A-round id (defaults to --id).", ) parser.add_argument( "--overwrite", action="store_true", help="Overwrite existing session files (not recommended).", ) args = parser.parse_args() code_root = Path(args.code_root).expanduser().resolve() if not code_root.exists() or not code_root.is_dir(): _fail(parser, f"--code-root does not exist or is not a directory: {code_root}") try: kind = _normalize_kind(args.kind) except ValueError as exc: _fail(parser, str(exc)) now = dt.datetime.now() test_id = args.id.strip() or _generate_test_id(now) if not _TEST_ID_RE.fullmatch(test_id): _fail( parser, "test id must match vYYYYMMDDHHMM, e.g. v202601010000 (omit --id to auto-generate).", ) run_id = args.run_id.strip() or _generate_run_id(now) if not _RUN_ID_RE.fullmatch(run_id): _fail( parser, "run id must match YYYY-MM-DD-HH-MM (legacy run_YYYYMMDDHHMMSS is still accepted).", ) # 尝试从代码根目录的 auto-test-code 配置读取,否则使用内置配置 bundled_skill_root = Path(__file__).resolve().parent.parent local_cfg_path = code_root / ".auto-test-code" / "config.yaml" target_cfg = _load_config_sections(local_cfg_path) if local_cfg_path.exists() else {} bundled_cfg = _load_config_sections(bundled_skill_root / "config.yaml") directories = _merge_section( base=_DEFAULT_DIRECTORIES, override=target_cfg.get("directories") or bundled_cfg.get("directories"), ) templates = _merge_section( base=_DEFAULT_TEMPLATES, override=target_cfg.get("templates") or bundled_cfg.get("templates"), ) target_template_keys = set((target_cfg.get("templates") or {}).keys()) tmp_dir = code_root / _safe_rel_path(directories.get("tmp", ""), default=_DEFAULT_DIRECTORIES["tmp"]) run_dir = tmp_dir / run_id tests_dir = run_dir / _safe_rel_path(directories.get("tests", ""), default=_DEFAULT_DIRECTORIES["tests"]) def template_path(config_key: str) -> Path | None: rel = _safe_rel_path(templates.get(config_key, ""), default="") if not rel: return None return _resolve_template_path( target_code_root=code_root, bundled_skill_root=bundled_skill_root, rel_path=rel, ) def template_path_any(*keys: str) -> Path | None: """ Prefer new config keys, but keep backward compatibility with older `.auto-test-code/config.yaml` that used `test_plan/test_run/test_report`. """ for k in keys: p = template_path(k) if p is not None: return p return None def template_path_prefer(*, new_keys: tuple[str, ...], old_keys: tuple[str, ...]) -> Path | None: """ Prefer the key family that was explicitly configured in the *project-local* `.auto-test-code/config.yaml` (if present). Otherwise, default to `new_keys`. """ if any(k in target_template_keys for k in new_keys): return template_path_any(*new_keys, *old_keys) if any(k in target_template_keys for k in old_keys): return template_path_any(*old_keys, *new_keys) return template_path_any(*new_keys, *old_keys) _ensure_dir_within_root(parser, code_root=code_root, path=tmp_dir, label="tmp directory") _ensure_dir_within_root(parser, code_root=code_root, path=run_dir, label="run directory") _ensure_dir_within_root(parser, code_root=run_dir, path=tests_dir, label="tests directory") _write_run_manifest( run_dir=run_dir, code_root=code_root, tmp_dir=tmp_dir, tests_dir=tests_dir, run_id=run_id, now=now, overwrite=args.overwrite, ) # 检测代码语言 code_language = _detect_code_language(code_root) template_values: dict[str, str] = { "TEST_ID": test_id, "RUN_ID": run_id, "TARGET_CODE_ROOT": code_root.as_posix(), "CODE_LANGUAGE": code_language, "PLAN_TIME": now.isoformat(timespec="minutes"), "CHECK_TIME": now.isoformat(timespec="minutes"), "REPORT_TIME": now.isoformat(timespec="minutes"), "PLAN_DATE": now.date().isoformat(), # 默认值 "FOCUS_DIMENSIONS": "(待填写)", "TRICKY_ANGLE": "(待填写)", "FIX_1": "(待填写)", "FIX_2": "(待填写)", "FIX_3": "(待填写)", "P0_COUNT": "0", "P1_COUNT": "0", "P2_COUNT": "0", "P0_RATIO": "0", "P1_RATIO": "0", "P2_RATIO": "0", "TOTAL_COUNT": "0", "SYSTEMIC_COUNT": "0", "SECURITY_COUNT": "0", } if kind == "a": session_name = test_id test_plan_template = template_path_prefer(new_keys=("session_test_plan",), old_keys=("test_plan",)) review_template = template_path("code_review") round_kind = "A轮代码审查" else: session_name = f"b-{test_id}" test_plan_template = template_path_prefer(new_keys=("session_test_plan",), old_keys=("test_plan",)) review_template = template_path("b_round_quality") round_kind = "B轮代码质量检查" template_values["ROUND_KIND"] = round_kind template_values["TMP_DIR_REL"] = tmp_dir.relative_to(code_root).as_posix() template_values["RUN_DIR_REL"] = run_dir.relative_to(code_root).as_posix() template_values["SESSION_NAME"] = session_name session_dir_rel = (tests_dir / session_name).relative_to(code_root).as_posix() template_values["SESSION_DIR_REL"] = session_dir_rel template_values["REVIEW_DOC_PATH"] = f"{session_dir_rel}/REVIEW.md" template_values["TEST_PLAN_REL"] = f"{session_dir_rel}/TEST_PLAN.md" template_values["TEST_RUN_REL"] = f"{session_dir_rel}/TEST_RUN.md" template_values["TEST_REPORT_REL"] = f"{session_dir_rel}/TEST_REPORT.md" if kind == "b": a_test_id = args.a_test_id.strip() or test_id if not _TEST_ID_RE.fullmatch(a_test_id): _fail(parser, "--a-test-id must match vYYYYMMDDHHMM (e.g. v202601010000)") template_values["A_TEST_ID"] = a_test_id session_dir = tests_dir / session_name _ensure_dir_within_root(parser, code_root=code_root, path=session_dir, label="session directory") _ensure_dir(session_dir / "_artifacts") _ensure_dir(session_dir / "_scripts") # REVIEW.md(A轮批判性审查 / B轮质量检查)统一放在会话目录内 review_doc_path = session_dir / "REVIEW.md" if (not review_doc_path.exists()) or args.overwrite or args.create_review: if review_template is not None: _safe_write( review_doc_path, _render_template(review_template.read_text(encoding="utf-8"), values=template_values), overwrite=args.overwrite, ) else: _safe_write( review_doc_path, f"# {round_kind}\n\n(未找到模板,请手动补全)\n", overwrite=args.overwrite, ) _copy_or_template( dst_path=session_dir / "TEST_PLAN.md", src_path=review_doc_path if (args.seed_test_plan_from_review and review_doc_path.exists()) else None, template_path=test_plan_template, template_values=template_values, overwrite=args.overwrite, ) test_run_path = session_dir / "TEST_RUN.md" test_run_template = template_path_prefer(new_keys=("session_test_run",), old_keys=("test_run",)) if not test_run_path.exists() or args.overwrite: if test_run_template is not None: _safe_write( test_run_path, _render_template(test_run_template.read_text(encoding="utf-8"), values=template_values), overwrite=args.overwrite, ) else: _safe_write( test_run_path, "# 测试过程记录\n\n(建议记录:命令、关键输出摘录、关键决策与证据路径)\n", overwrite=args.overwrite, ) report_path = session_dir / "TEST_REPORT.md" test_report_template = template_path_prefer(new_keys=("session_test_report",), old_keys=("test_report",)) if not report_path.exists() or args.overwrite: if test_report_template is not None: _safe_write( report_path, _render_template(test_report_template.read_text(encoding="utf-8"), values=template_values), overwrite=args.overwrite, ) else: _safe_write( report_path, "# 测试报告\n\n" f"**测试会话**: {session_name}\n\n" "## 结果\n\n" "- 状态:✅ 通过 / ❌ 失败 / ⚠️ 部分通过\n\n" "## 证据\n\n" "- (填入命令输出、文件路径、对比结果等)\n", overwrite=args.overwrite, ) print(str(session_dir)) return 0 if __name__ == "__main__": raise SystemExit(main()) -
verify_session.py 13.4 KB
#!/usr/bin/env python3 from __future__ import annotations import argparse import json import re import sys import typing from dataclasses import dataclass from pathlib import Path _TEST_ID_RE = re.compile(r"^v\d{12}$") _RUN_ID_RE = re.compile(r"^run_\d{14}$") _PLACEHOLDER_RE = re.compile(r"\{\{[A-Z0-9_]+\}\}") _DEFAULT_DIRECTORIES = { "tmp": "tmp", "tests": "tests", } @dataclass(frozen=True) class Issue: severity: str # P0/P1/P2 message: str def _fail(message: str) -> typing.NoReturn: print(f"error: {message}", file=sys.stderr) raise SystemExit(2) def _find_code_root(start: Path) -> Path | None: """尝试找到代码根目录(优先读取 run manifest;兼容旧版 tests/reviews 布局)""" p = start.resolve() for _ in range(20): manifest_path = p / ".auto-test-code-run.json" if manifest_path.exists() and manifest_path.is_file(): try: data = json.loads(manifest_path.read_text(encoding="utf-8")) except Exception: data = None if isinstance(data, dict): raw = data.get("code_root") if isinstance(raw, str) and raw: candidate = Path(raw).expanduser().resolve() if candidate.exists() and candidate.is_dir(): return candidate if _RUN_ID_RE.fullmatch(p.name) and p.parent != p: return p.parent.parent if p.parent.parent != p.parent else None if p.parent == p: break p = p.parent p = start for _ in range(20): if (p / ".auto-test-code" / "config.yaml").exists(): return p if (p / "reviews").exists() or (p / "tests").exists(): return p if p.parent == p: return None p = p.parent return None def _strip_inline_comment(value: str) -> str: if "#" not in value: return value return value.split("#", 1)[0].rstrip() def _parse_simple_yaml_sections(text: str, *, wanted_sections: set[str]) -> dict[str, dict[str, str]]: """解析最小 YAML 子集""" result: dict[str, dict[str, str]] = {} current: str | None = None for raw in text.splitlines(): line = raw.rstrip("\n") if not line.strip() or line.lstrip().startswith("#"): continue if not line.startswith(" ") and line.endswith(":"): section = line[:-1].strip() current = section if section in wanted_sections else None continue if current is None: continue if line.startswith(" ") and ":" in line: key, value = line.split(":", 1) key = key.strip() value = _strip_inline_comment(value.strip()) if not value: continue if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")): value = value[1:-1] result.setdefault(current, {})[key] = value return result def _load_directories(config_path: Path) -> dict[str, str]: if not config_path.exists(): return dict(_DEFAULT_DIRECTORIES) text = config_path.read_text(encoding="utf-8") data = _parse_simple_yaml_sections(text, wanted_sections={"directories"}) out = data.get("directories") or {} merged = dict(_DEFAULT_DIRECTORIES) merged.update({str(k): str(v) for k, v in out.items()}) return merged def _safe_rel_path(value: str, *, default: str) -> str: if not value: return default p = Path(value) if p.is_absolute() or ".." in p.parts: return default return value def _extract_markdown_field(text: str, field_label: str) -> str | None: pat = re.compile(rf"^\*\*{re.escape(field_label)}\*\*:\s*(.+?)\s*$", re.MULTILINE) m = pat.search(text) if not m: return None return m.group(1).strip() def _scan_placeholders(text: str) -> list[str]: return [m.group(0) for m in _PLACEHOLDER_RE.finditer(text)] def _check_report_status(text: str) -> tuple[bool, bool]: """检查报告状态(是否选择了明确的状态)""" for line in text.splitlines(): if "状态:" in line or "**状态**:" in line: return ("/" not in line, True) return (False, False) def _classify_session(session_dir: Path) -> tuple[str, str] | None: name = session_dir.name if name.startswith("b-"): test_id = name.removeprefix("b-") if _TEST_ID_RE.fullmatch(test_id): return ("b", test_id) return None if name.startswith("B轮-"): test_id = name.removeprefix("B轮-") if _TEST_ID_RE.fullmatch(test_id): return ("b", test_id) return None if _TEST_ID_RE.fullmatch(name): return ("a", name) return None def _find_run_dir(session_dir: Path) -> Path | None: current = session_dir.resolve() for candidate in (current, *current.parents): if _RUN_ID_RE.fullmatch(candidate.name): return candidate return None def verify_session( *, session_dir: Path, code_root: Path, require_review: bool, strict: bool, ) -> list[Issue]: issues: list[Issue] = [] if not session_dir.exists() or not session_dir.is_dir(): return [Issue("P0", f"session_dir is not a directory: {session_dir}")] # 读取配置 local_cfg_path = code_root / ".auto-test-code" / "config.yaml" directories = _load_directories(local_cfg_path) if local_cfg_path.exists() else {} if not directories: directories = dict(_DEFAULT_DIRECTORIES) tmp_dir = code_root / _safe_rel_path(directories.get("tmp", ""), default=_DEFAULT_DIRECTORIES["tmp"]) run_dir = _find_run_dir(session_dir) if run_dir is not None: tests_dir = run_dir / _safe_rel_path(directories.get("tests", ""), default=_DEFAULT_DIRECTORIES["tests"]) if run_dir.parent.resolve() != tmp_dir.resolve(): issues.append(Issue("P0", f"run directory is not under configured tmp directory: {run_dir}")) try: session_dir.resolve().relative_to(tests_dir.resolve()) except ValueError: issues.append(Issue("P0", f"session directory is not inside run tests directory: {session_dir}")) else: tests_dir = code_root / _safe_rel_path(directories.get("tests", ""), default=_DEFAULT_DIRECTORIES["tests"]) issues.append(Issue("P1", f"session directory is not under isolated tmp/run_* workspace: {session_dir}")) if tests_dir.exists() and tests_dir.is_symlink(): issues.append(Issue("P1", f"tests directory is a symlink (discouraged): {tests_dir}")) kind_info = _classify_session(session_dir) if kind_info is None: issues.append(Issue("P1", f"unexpected session directory name (expected vYYYYMMDDHHMM or b-vYYYYMMDDHHMM): {session_dir.name}")) kind = "unknown" test_id = "" else: kind, test_id = kind_info # 检查必需的文件和目录 required_paths = [ (session_dir / "REVIEW.md", "REVIEW.md"), (session_dir / "TEST_PLAN.md", "TEST_PLAN.md"), (session_dir / "TEST_RUN.md", "TEST_RUN.md"), (session_dir / "TEST_REPORT.md", "TEST_REPORT.md"), (session_dir / "_artifacts", "_artifacts/"), (session_dir / "_scripts", "_scripts/"), ] for p, label in required_paths: if not p.exists(): issues.append(Issue("P0", f"missing required {label}: {p}")) elif label.endswith("/") and not p.is_dir(): issues.append(Issue("P0", f"{label} is not a directory: {p}")) review_text = "" report_text = "" test_plan_text = "" test_run_text = "" review_path: Path | None = session_dir / "REVIEW.md" if require_review: if review_path is None or not review_path.exists(): issues.append(Issue("P0", f"missing REVIEW.md: {session_dir / 'REVIEW.md'}")) else: review_text = review_path.read_text(encoding="utf-8", errors="replace") test_plan_path = session_dir / "TEST_PLAN.md" if test_plan_path.exists(): test_plan_text = test_plan_path.read_text(encoding="utf-8", errors="replace") if strict: placeholders = _scan_placeholders(test_plan_text) if placeholders: uniq = sorted(set(placeholders)) shown = uniq[:8] suffix = " (and more)" if len(uniq) > len(shown) else "" issues.append(Issue("P0", f"TEST_PLAN.md contains unresolved placeholders: {shown}{suffix}")) test_run_path = session_dir / "TEST_RUN.md" if test_run_path.exists(): test_run_text = test_run_path.read_text(encoding="utf-8", errors="replace") if strict: placeholders = _scan_placeholders(test_run_text) if placeholders: uniq = sorted(set(placeholders)) shown = uniq[:8] suffix = " (and more)" if len(uniq) > len(shown) else "" issues.append(Issue("P0", f"TEST_RUN.md contains unresolved placeholders: {shown}{suffix}")) report_path = session_dir / "TEST_REPORT.md" if report_path.exists(): report_text = report_path.read_text(encoding="utf-8", errors="replace") if strict: placeholders = _scan_placeholders(report_text) if placeholders: uniq = sorted(set(placeholders)) shown = uniq[:8] suffix = " (and more)" if len(uniq) > len(shown) else "" issues.append(Issue("P0", f"TEST_REPORT.md contains unresolved placeholders: {shown}{suffix}")) ok, found = _check_report_status(report_text) if not found: issues.append(Issue("P1", "TEST_REPORT.md is missing a status line ('状态:...').")) elif not ok: issues.append(Issue("P1", "TEST_REPORT.md status line looks like the template placeholder; set it to a single status.")) if require_review and review_path is not None and review_text and strict: placeholders = _scan_placeholders(review_text) if placeholders: uniq = sorted(set(placeholders)) shown = uniq[:8] suffix = " (and more)" if len(uniq) > len(shown) else "" issues.append(Issue("P0", f"REVIEW.md contains unresolved placeholders: {shown}{suffix}")) if require_review: # 验证 TEST_PLAN/TEST_REPORT 是否引用了正确的审查文档 expected_review_resolved = review_path.resolve() if (review_path is not None and review_path.exists()) else None for label, text in [("TEST_PLAN.md", test_plan_text), ("TEST_RUN.md", test_run_text), ("TEST_REPORT.md", report_text)]: if not text: continue ref = _extract_markdown_field(text, "对应审查") or _extract_markdown_field(text, "对应A轮审查") if ref is None: issues.append(Issue("P1", f"{label} missing '**对应审查**: ...' field")) continue ref_path = Path(ref) if ref_path.is_absolute() or ".." in ref_path.parts: issues.append(Issue("P0", f"{label} has unsafe review path: {ref}")) continue abs_ref = (code_root / ref_path).resolve() try: abs_ref.relative_to(code_root) except ValueError: issues.append(Issue("P0", f"{label} review path resolves outside code_root: {ref} -> {abs_ref}")) continue if not abs_ref.exists(): issues.append(Issue("P0", f"{label} references missing review doc: {ref}")) continue if expected_review_resolved is not None and abs_ref != expected_review_resolved: issues.append(Issue("P1", f"{label} references a different review doc than expected by session id: {ref}")) return issues def main() -> int: parser = argparse.ArgumentParser(description="Verify an auto-test-code session directory for completeness.") parser.add_argument( "session_dir", help="Session directory path (e.g. .bensz-api/skills/auto-test-code/YYYY-MM-DD-HH-MM/output/tests/vYYYYMMDDHHMM).", ) parser.add_argument( "--code-root", default="", help="Explicit code root. If omitted, auto-detect by walking up from session_dir.", ) parser.add_argument( "--require-review", action="store_true", help="Require REVIEW.md to exist and be consistent with TEST_PLAN/TEST_RUN/TEST_REPORT.", ) parser.add_argument( "--strict", action="store_true", help="Fail if any unresolved {{PLACEHOLDER}} remains in session documents (recommended only after you finish writing).", ) args = parser.parse_args() session_dir = Path(args.session_dir).expanduser() if args.code_root.strip(): code_root = Path(args.code_root).expanduser().resolve() else: resolved_session = session_dir.resolve() code_root = _find_code_root(resolved_session) or _fail(f"could not locate code root from: {resolved_session}") issues = verify_session( session_dir=session_dir.resolve(), code_root=code_root, require_review=args.require_review, strict=args.strict, ) if issues: order = {"P0": 0, "P1": 1, "P2": 2} issues_sorted = sorted(issues, key=lambda i: order.get(i.severity, 99)) for it in issues_sorted: print(f"{it.severity}: {it.message}", file=sys.stderr) return 2 print("OK") return 0 if __name__ == "__main__": raise SystemExit(main())
-
-
templates
-
B_ROUND_CODE_QUALITY_TEMPLATE.md 12.1 KB
# B轮代码质量检查报告({{TEST_ID}}) **检查ID**: B轮-{{TEST_ID}} **检查时间**: {{CHECK_TIME}} **对应A轮审查**: {{A_TEST_ID}} **目标代码路径**: {{TARGET_CODE_ROOT}} --- ## 检查结果总览 | 维度 | 状态 | 得分 | 备注 | |------|------|------|------| | 算法复杂度分析 | ✅ / ⚠️ / ❌ | {{SCORE_1}}/10 | {{NOTE_1}} | | 边界条件覆盖 | ✅ / ⚠️ / ❌ | {{SCORE_2}}/10 | {{NOTE_2}} | | 异常处理完整性 | ✅ / ⚠️ / ❌ | {{SCORE_3}}/10 | {{NOTE_3}} | | 资源管理 | ✅ / ⚠️ / ❌ | {{SCORE_4}}/10 | {{NOTE_4}} | | 并发安全性 | ✅ / ⚠️ / ❌ | {{SCORE_5}}/10 | {{NOTE_5}} | | 安全漏洞分类审查 | ✅ / ⚠️ / ❌ | {{SCORE_6}}/15 | {{NOTE_6}} | | 代码可读性 | ✅ / ⚠️ / ❌ | {{SCORE_7}}/5 | {{NOTE_7}} | | 测试覆盖充分性 | ✅ / ⚠️ / ❌ | {{SCORE_8}}/15 | {{NOTE_8}} | | 设计质量 | ✅ / ⚠️ / ❌ | {{SCORE_9}}/15 | {{NOTE_9}} | | **总分** | | **{{TOTAL_SCORE}}/100** | | --- ## 1. 算法复杂度分析 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **确保算法的时间和空间复杂度合理,数据结构选择恰当**。 ### 检查维度 #### ✅ 算法合理 - **时间复杂度适当**:核心操作无明显性能瓶颈(如 O(n²) 可优化为 O(n log n)) - **空间复杂度可控**:无明显内存浪费,避免不必要的数据复制 - **数据结构选择恰当**:根据操作特点选择合适的容器(列表/哈希表/树/堆) - **避免重复计算**:相同计算结果被缓存或复用 #### ⚠️ 存在优化空间 - 存在嵌套循环但可通过哈希表优化 - 频繁的字符串拼接(应使用 StringBuilder/join) - 在循环中进行重复的 I/O 操作 - 使用线性查找但应该用二分查找或哈希表 #### ❌ 严重性能问题 - 核心算法时间复杂度为 O(n³) 或更高 - 在热点路径上使用低效算法 - 无限制的内存增长(如无限追加到列表) - N+1 查询问题(数据库/网络) ### 本轮发现 - {{FINDING_1}} ### 改进建议 - {{SUGGESTION_1}} --- ## 2. 边界条件覆盖 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **处理各种极端和异常输入情况,确保代码鲁棒性**。 ### 边界检查清单 #### 空输入与零值 - [ ] 空字符串 `""` - [ ] 空列表/数组 `[]` - [ ] 空对象/None/undefined - [ ] 零值 `0` / `0.0` #### 单元素与最小值 - [ ] 单元素列表/数组 - [ ] 最小边界值(如 `INT_MIN`) - [ ] 负数 #### 极大输入 - [ ] 超大数值(溢出风险) - [ ] 超长字符串(栈溢出/性能) - [ ] 超大集合(内存/性能) #### 特殊字符与格式 - [ ] 空格、制表符、换行符 - [ ] Unicode 特殊字符 - [ ] 路径分隔符(`../`, `\`) - [ ] 引号与转义字符 #### 格式异常 - [ ] 类型不匹配 - [ ] 缺少必需字段 - [ ] 额外未知字段 ### 本轮发现 - {{FINDING_2}} ### 改进建议 - {{SUGGESTION_2}} --- ## 3. 异常处理完整性 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **正确处理错误和异常情况,确保资源释放和错误传播**。 ### 检查维度 #### ✅ 异常处理完善 - **try-catch-finally 完整**:异常捕获后有适当的处理逻辑 - **资源释放保证**:使用 RAII / with / try-with-resources 确保资源释放 - **错误信息有用**:异常消息包含足够的上下文信息 - **异常不吞噬**:不捕获后静默忽略,至少记录日志 #### ⚠️ 异常处理不足 - 捕获了过于宽泛的异常(如 `Exception` / `Throwable`) - finally 块可能抛出异常(覆盖原异常) - 异常消息过于笼统(如"处理失败") - 未对可能失败的操作进行异常处理 #### ❌ 严重异常处理问题 - 吞噬关键异常(静默失败) - 在 finally 中返回或抛出异常(掩盖原异常) - 错误时资源未释放(文件/连接/内存) - 未处理 NULL/None 检查(可能崩溃) ### 本轮发现 - {{FINDING_3}} ### 改进建议 - {{SUGGESTION_3}} --- ## 4. 资源管理 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **确保所有资源(内存、文件、连接、句柄)被正确管理和释放**。 ### 检查维度 #### ✅ 资源管理正确 - **内存无泄漏**:无循环引用、无未释放的大对象 - **文件正确关闭**:使用 `with` / `try-with-resources` 确保文件关闭 - **连接正确释放**:数据库/网络连接在使用后关闭 - **句柄正确管理**:文件描述符、套接字等不泄漏 #### ⚠️ 资源管理有风险 - 文件/连接未在异常情况下关闭 - 大对象未及时释放(如缓存无限增长) - 循环引用可能导致内存泄漏 - 未使用上下文管理器管理资源 #### ❌ 严重资源管理问题 - 明显的内存泄漏(未释放的动态分配) - 文件描述符泄漏(未关闭文件/套接字) - 连接池耗尽(连接未归还) - 重复打开资源而不关闭 ### 本轮发现 - {{FINDING_4}} ### 改进建议 - {{SUGGESTION_4}} --- ## 5. 并发安全性 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **在多线程/多进程环境下保证数据一致性和线程安全**。 ### 检查维度 #### ✅ 并发安全 - **共享数据受保护**:使用锁/互斥量/原子操作保护共享状态 - **无竞态条件**:不存在"检查-使用"竞态窗口 - **无死锁风险**:锁获取顺序一致,无嵌套锁或超时机制 - **无活锁风险**:重试逻辑有退避策略 #### ⚠️ 存在并发风险 - 非原子操作的多步状态变更 - 检查与使用之间可能被中断 - 锁粒度过大(影响性能)或过小(不安全) - 使用全局/静态可变状态 #### ❌ 严重并发问题 - 明显的竞态条件(如双重检查锁定错误) - 死锁风险(锁获取顺序不一致) - 数据竞争(无保护的共享状态访问) - 信号量/条件变量使用错误 ### 本轮发现 - {{FINDING_5}} ### 改进建议 - {{SUGGESTION_5}} --- ## 6. 安全漏洞分类审查 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **安全审查必须覆盖代码根因、业务风险、威胁模型、供应链与严重程度,而不是只搜索少数危险函数**。 ### 检查维度 #### ✅ 安全覆盖充分 - **CWE 根因**:输入验证、注入、内存安全、认证授权、信息泄露、配置、资源耗尽均已检查 - **OWASP 风险**:访问控制、配置、供应链、密码学、注入、过时组件、认证、完整性、日志、SSRF 均已过一遍 - **STRIDE 威胁**:身份欺骗、篡改、否认、信息泄露、拒绝服务、权限提升均有对应判断 - **专项族**:配置运维、供应链、密码学、认证授权、DoS、内存/并发安全有语言和项目类型适配 #### ⚠️ 安全覆盖不足 - 只搜索了 SQL/命令注入,未覆盖认证授权、配置、供应链、密码学或 DoS - 发现安全问题但缺少可利用场景、影响资产或验证方法 - 只看源代码,遗漏 Dockerfile、CI/CD、lockfile、云/部署配置 - 安全问题未映射到明确分类,优先级判断缺少依据 #### ❌ 严重安全盲区 - 外部入口、权限边界或敏感数据流完全未审查 - 存在可利用 P0 安全漏洞但未进入修复计划 - 依赖/构建/部署链路可被污染却未检查 - 密钥、令牌、密码或个人敏感信息可能泄露 ### 本轮发现 - {{FINDING_6}} ### 改进建议 - {{SUGGESTION_6}} --- ## 7. 代码可读性与可维护性 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **代码应该易于理解、修改和扩展**。 ### 检查维度 #### ✅ 代码清晰 - **命名有意义**:变量/函数/类名清晰表达意图 - **函数简短**:单个函数不超过 50 行 - **低复杂度**:圈复杂度 < 10 - **无重复代码**:相似逻辑被抽象复用 - **注释恰当**:解释"为什么"而非"是什么" #### ⚠️ 可读性不足 - 函数过长(>100 行) - 嵌套过深(>4 层) - 魔法数字未命名常量化 - 变量名缩写过度(如 `tmp1`, `dt`) #### ❌ 可维护性差 - 圈复杂度过高(>20) - 大量复制粘贴代码 - 全局变量滥用 - 逻辑与 UI/数据访问耦合 ### 本轮发现 - {{FINDING_7}} ### 改进建议 - {{SUGGESTION_7}} --- ## 8. 测试覆盖充分性 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **核心逻辑有测试覆盖,边界条件和异常情况有测试**。 ### 检查维度 #### ✅ 测试充分 - **核心路径有测试**:主要功能有单元测试 - **边界有测试**:空输入、单元素、极大输入有测试 - **异常有测试**:错误处理路径有测试 - **测试独立**:测试之间无依赖,可并行运行 #### ⚠️ 测试覆盖不足 - 仅测试正常路径,无边界测试 - 测试依赖外部服务/状态(不稳定) - 测试使用了固定的随机数种子 - 关键逻辑缺少测试 #### ❌ 测试严重不足 - 无任何单元测试 - 测试仅验证显而易见的功能 - 测试与实现代码重复(无意义) - Mock 不当导致测试无效 ### 本轮发现 - {{FINDING_8}} ### 改进建议 - {{SUGGESTION_8}} --- ## 9. 设计质量 **状态**: ✅ / ⚠️ / ❌ ### 核心原则 **代码的设计应支持长期演化,而非仅满足当下需求**。 ### 检查维度 #### ✅ 设计合理 - **可扩展**:新增功能通过扩展而非频繁修改已有分支实现(OCP) - **架构清晰**:层次分明、依赖方向正确、无循环依赖 - **API 一致**:接口契约统一、参数语义明确、返回结构稳定 - **状态可追溯**:状态转换显式、受保护、可审计 - **领域建模恰当**:核心业务规则封装在领域对象或清晰边界内 - **低耦合高内聚**:模块通过抽象交互、类职责单一 #### ⚠️ 存在设计风险 - 新增类型需改多处 if-else / switch 分支(缺少策略或工厂抽象) - 状态逻辑散落多个函数(缺少状态机或统一状态门面) - 接口返回结构不一致,调用方需要写兼容分支 - 业务逻辑泄露到 UI 层、脚本层或数据访问层 #### ❌ 严重设计缺陷 - 上帝对象(超大类/超大模块同时管理多个不相干职责) - 循环依赖导致模块无法独立测试或复用 - 贫血模型严重,所有业务规则都堆在 Service 层 - 硬编码配置/流程,导致系统几乎无法扩展 - 层次穿透(上层直接操作底层存储或外部服务) ### 本轮发现 - {{FINDING_9}} ### 改进建议 - {{SUGGESTION_9}} --- ## 改进建议汇总 ⚠️ **数量要求**:B 轮必须提出至少 10-20 个建设性建议 ### P0(必须修复) - {{P0_ITEM_1}} ### P1(强烈建议) - {{P1_ITEM_1}} ### P2(可选) - {{P2_ITEM_1}} --- ## 🚨 挑衅性代码检查 ### 1. 最坏情况分析 找出 3 个"在最坏情况下会出问题"的代码段: 1. **位置**:{{WORST_CASE_1_LOCATION}} - 最坏情况:{{WORST_CASE_1_SCENARIO}} - 后果:{{WORST_CASE_1_IMPACT}} - 如何修复:{{WORST_CASE_1_FIX}} 2. **位置**:{{WORST_CASE_2_LOCATION}} - 最坏情况:{{WORST_CASE_2_SCENARIO}} - 后果:{{WORST_CASE_2_IMPACT}} - 如何修复:{{WORST_CASE_2_FIX}} 3. **位置**:{{WORST_CASE_3_LOCATION}} - 最坏情况:{{WORST_CASE_3_SCENARIO}} - 后果:{{WORST_CASE_3_IMPACT}} - 如何修复:{{WORST_CASE_3_FIX}} ### 2. 边界压力测试 如果用户输入以下内容会怎样? - **空输入**:`""` / `[]` / `None` - 位置:{{EMPTY_INPUT_LOCATION}} - 当前行为:{{EMPTY_INPUT_BEHAVIOR}} - 是否安全?:{{EMPTY_INPUT_SAFE}} - **超大输入**:1GB 文件 / 10^9 个元素 - 位置:{{HUGE_INPUT_LOCATION}} - 当前行为:{{HUGE_INPUT_BEHAVIOR}} - 是否有保护?:{{HUGE_INPUT_PROTECTED}} - **特殊字符**:`\0`, `\n`, `../`, `"; DROP TABLE--"` - 位置:{{SPECIAL_CHARS_LOCATION}} - 当前行为:{{SPECIAL_CHARS_BEHAVIOR}} - 是否有验证?:{{SPECIAL_CHARS_VALIDATED}} ### 3. 隐式假设挖掘 列出 5 个代码未说明但假设成立的条件: 1. {{ASSUMPTION_1}} → 失效场景:{{ASSUMPTION_1_FAILURE}} 2. {{ASSUMPTION_2}} → 失效场景:{{ASSUMPTION_2_FAILURE}} 3. {{ASSUMPTION_3}} → 失效场景:{{ASSUMPTION_3_FAILURE}} 4. {{ASSUMPTION_4}} → 失效场景:{{ASSUMPTION_4_FAILURE}} 5. {{ASSUMPTION_5}} → 失效场景:{{ASSUMPTION_5_FAILURE}} -
CODE_REVIEW_TEMPLATE.md 3 KB
# 代码审查计划({{TEST_ID}}) **审查日期**: {{PLAN_DATE}} **审查ID**: {{TEST_ID}} **目标代码路径**: {{TARGET_CODE_ROOT}} **代码语言**: {{CODE_LANGUAGE}} --- ## 独立评估与审查范围(强制) - [ ] 本轮基于目标代码的**当前状态**独立评估 - [ ] **未查看**历史 `tmp/run_*/` 工作区中的审查文件(避免确认偏差/路径依赖) **扫描命令证据**(建议填入): - `find {{TARGET_CODE_ROOT}} -name "*.py" -o -name "*.js" ...` - `rg -n "..."` - `cloc {{TARGET_CODE_ROOT}}` **审查维度与深挖策略**: - 全维度覆盖(强制):本轮必须覆盖全部审查维度(以 `a_round_check.dimensions` 为准;如项目存在 `.auto-test-code/config.yaml`,同名字段可覆盖) - 深挖维度(可选):{{FOCUS_DIMENSIONS}}(在全覆盖基础上额外深挖 1-2 个维度;可留空) - 刁钻角度:{{TRICKY_ANGLE}}(用于深挖维度;空输入/超大输入/恶意输入/竞态条件/资源耗尽/权限绕过/供应链污染) **安全分类覆盖(强制)**: - [ ] 已按 `references/SECURITY_TAXONOMY.md` 覆盖 CWE 根因、OWASP 风险、STRIDE 威胁、七大王国与 CVSS 优先级口径 - [ ] 已检查注入、认证授权、配置运维、供应链、密码学与数据保护、内存/并发/DoS 风险 - [ ] 安全问题均包含可利用场景、影响资产、修复建议与验证方法 --- ## 问题清单(按优先级) > 每个问题必须包含:位置(文件:行号)、现象、影响(为什么重要)、修复方案、验证方法。 ### P0(必须修复 - 崩溃/安全/数据损坏/内存泄漏) 1) 标题: - **位置**:`path/to/file:line` - **问题类型**:算法错误 / 边界缺陷 / 并发竞态 / 内存泄漏 / 安全漏洞(CWE/OWASP/STRIDE 分类) - **现象**: - **影响**: - **修复建议**: - **验证方法**: ### P1(强烈建议 - 性能/逻辑/资源/安全) 1) 标题: - **位置**:`path/to/file:line` - **问题类型**:性能问题 / 边界条件 / 逻辑缺陷 / 资源泄漏 / 需前置条件或组合利用的安全风险 - **现象**: - **影响**: - **修复建议**: - **验证方法**: ### P2(可选 - 风格/可读性) 1) 标题: - **位置**:`path/to/file:line` - **现象**: - **修复建议**: --- ## 执行步骤(按顺序) 1) ... 2) ... --- ## 本轮轻量测试 - 会话目录:`{{SESSION_DIR_REL}}/` - 测试计划:`{{TEST_PLAN_REL}}` - 测试报告:`{{TEST_REPORT_REL}}` --- ## 问题统计 | 优先级 | 数量 | 占比 | |--------|------|------| | P0 | {{P0_COUNT}} | {{P0_RATIO}}% | | P1 | {{P1_COUNT}} | {{P1_RATIO}}% | | P2 | {{P2_COUNT}} | {{P2_RATIO}}% | | **总计** | **{{TOTAL_COUNT}}** | 100% | **系统性问题**(算法/边界/并发/内存/安全/设计):{{SYSTEMIC_COUNT}} 个 **安全问题**(CWE/OWASP/STRIDE/供应链/配置/密码学/认证授权/DoS):{{SECURITY_COUNT}} 个 **质量检查**: - [ ] 总问题数 ≥ 10 - [ ] P0 + P1 占比 ≥ 60% - [ ] 系统性问题 ≥ 3 - [ ] 安全漏洞分类审查已覆盖 -
SESSION_TEST_PLAN_TEMPLATE.md 955 B
# 测试计划({{TEST_ID}}) **创建时间**: {{PLAN_TIME}} **对应审查**: {{REVIEW_DOC_PATH}} **目标代码路径**: {{TARGET_CODE_ROOT}} --- ## 测试目标 本次轻量测试旨在验证以下修复: - {{FIX_1}} - {{FIX_2}} - {{FIX_3}} --- ## 测试环境 - 操作系统:{{OS}} - 语言版本:{{LANGUAGE_VERSION}} - 依赖版本:{{DEPENDENCIES}} --- ## 测试用例 ### 用例 1: {{CASE_1_NAME}} **描述**:{{CASE_1_DESCRIPTION}} **步骤**: 1. {{CASE_1_STEP_1}} 2. {{CASE_1_STEP_2}} **预期结果**:{{CASE_1_EXPECTED}} --- ### 用例 2: {{CASE_2_NAME}} **描述**:{{CASE_2_DESCRIPTION}} **步骤**: 1. {{CASE_2_STEP_1}} 2. {{CASE_2_STEP_2}} **预期结果**:{{CASE_2_EXPECTED}} --- ## 测试执行 记录实际执行结果... --- ## 测试结果 | 用例 | 状态 | 备注 | |------|------|------| | {{CASE_1_NAME}} | ✅ / ❌ | {{CASE_1_NOTE}} | | {{CASE_2_NAME}} | ✅ / ❌ | {{CASE_2_NOTE}} | -
SESSION_TEST_REPORT_TEMPLATE.md 1.1 KB
# 测试报告({{TEST_ID}}) **完成时间**: {{REPORT_TIME}} **对应审查**: {{REVIEW_DOC_PATH}} **目标代码路径**: {{TARGET_CODE_ROOT}} --- ## 修复清单 ### P0 修复 1) {{P0_1_TITLE}} - **位置**:`{{P0_1_LOCATION}}` - **修复方式**:{{P0_1_FIX}} - **验证证据**: ```bash {{P0_1_EVIDENCE}} ``` - **状态**:✅ 已验证 / ❌ 验证失败 ### P1 修复 1) {{P1_1_TITLE}} - **位置**:`{{P1_1_LOCATION}}` - **修复方式**:{{P1_1_FIX}} - **验证证据**: ```bash {{P1_1_EVIDENCE}} ``` - **状态**:✅ 已验证 / ⏭️ 跳过(原因:{{P1_1_SKIP_REASON}}) --- ## 遗留问题 | 问题 | 优先级 | 未修复原因 | 后续计划 | |------|--------|-----------|----------| | {{ISSUE_1}} | P0/P1/P2 | {{REASON_1}} | {{PLAN_1}} | --- ## 测试产物 所有中间产物保存在 `_artifacts/` 目录: - {{ARTIFACT_1}} - {{ARTIFACT_2}} --- ## 总体结论 - [ ] P0 问题全部修复 - [ ] P1 问题全部修复(或不修复率 < 20%) - [ ] 所有修复有可复现证据 **本轮状态**:✅ 通过 / ⚠️ 部分通过 / ❌ 未通过 -
SESSION_TEST_RUN_TEMPLATE.md 667 B
# 测试过程记录({{TEST_ID}}) **开始时间**: {{PLAN_TIME}} **对应审查**: {{REVIEW_DOC_PATH}} **目标代码路径**: {{TARGET_CODE_ROOT}} --- ## 变更概览 - 本轮主要变更点: - (待填写) ## 执行记录(按时间顺序) > 建议只记录可复现的关键信息:命令、关键输出摘录、定位到的文件与行号、决定性的对比结果。 ### 1) 环境与准备 ```bash # (待填写) ``` ### 2) 复现与定位 ```bash # (待填写) ``` ### 3) 修复与回归验证 ```bash # (待填写) ``` ## 关键证据索引 - `_artifacts/`: - (待填写:文件名/路径 + 一句话说明)
-
-
CHANGELOG.md 4.2 KB
# auto-test-code - 变更日志 版本号以 `auto-test-code/config.yaml:skill_info.version` 为单一真相来源。 ## [Unreleased] ### Added(新增) - `references/SECURITY_TAXONOMY.md`:新增安全漏洞分类审查体系,将代码安全漏洞分类报告中的 CWE、OWASP、MITRE ATT&CK、STRIDE、七大王国、CVSS、供应链、配置、密码学、认证授权、DoS 与内存安全口径转化为可执行检查清单。 ### Changed(变更) - `config.yaml`:版本号升级为 `0.5.0`;A/B 轮新增 `security_vulnerability_analysis` 独立维度,并扩展 Dockerfile、CI/CD workflow、lockfile 等安全相关扫描模式。 - `SKILL.md` / `README.md` / `templates/` / `references/`:将安全漏洞分类审查纳入强制覆盖范围,B 轮从 8 大维度升级为 9 大维度,并同步更新优先级、刁钻角度、模板字段与参考资料说明。 - 通过 1 次 `auto-test-skill` 优化修复口径漂移:脚本默认填充 `SECURITY_COUNT`,语言检测跳过生成物/依赖目录,配置注释与 README 路径更新为真实字段,且默认排除范围不再跳过目标项目测试代码。 ## [0.4.0] - 2026-04-04 ### Added(新增) - `references/DESIGN_ANTI_PATTERNS.md`:新增设计反模式识别指南,覆盖扩展性、架构、API、状态管理、领域建模、耦合/内聚、设计模式 7 类设计问题。 - `config.yaml`:A/B 轮维度新增 `design_quality`,将设计质量正式纳入审查范围。 ### Changed(变更) - `templates/B_ROUND_CODE_QUALITY_TEMPLATE.md`:B 轮质量检查升级为 8 大维度,新增“设计质量”评分与专门检查章节,并重分配评分权重保持总分 100。 - `references/CRITICAL_THINKING_FOR_CODE.md`:从三大思考框架升级为四大框架,补充设计质量视角与自问清单。 - `SKILL.md` / `README.md`:同步更新设计质量维度、B 轮 8 维度口径与参考资料说明。 ## [0.3.0] - 2026-03-10 ### Added(新增) - `scripts/create_session.py`:新增 `--run-id` 与 `.auto-test-code-run.json` 运行清单,支持将一次 auto-test-code 执行的全部会话统一收口到 `tmp/run_*/tests/` 隔离工作区。 ### Changed(变更) - 默认工作区从项目根 `tests/` 调整为 `tmp/run_{timestamp}/tests/`,避免计划、报告、日志和中间产物泄露到源码项目其他位置。 - `scripts/verify_session.py`:新增对 `tmp/run_*/tests/` 布局的自动识别与隔离校验;旧布局仍可识别,但会提示不满足新的隔离规范。 - `config.yaml`:新增 `directories.tmp`,并将 `tmp/**` 加入默认排除列表,避免审查时把 skill 自身产物再次扫入。 - `SKILL.md` / `README.md` / `references/` / `templates/`:同步更新隔离工作区规则、脚本用法示例与路径口径。 ## [0.2.0] - 2026-02-16 ### Added(新增) - `templates/SESSION_TEST_RUN_TEMPLATE.md`:新增“测试过程记录”模板,用于标准化沉淀测试过程(命令、关键输出摘录、关键决策与证据索引)。 ### Changed(变更) - 废弃 `reviews/` 输出目录:A/B 轮的计划/过程/结果统一沉淀到 `tests/{session}/` 会话目录(新增 `REVIEW.md` + `TEST_RUN.md` 并保留 `TEST_PLAN.md`/`TEST_REPORT.md`)。 - 会话命名规范:B 轮会话目录从 `tests/B轮-v*` 调整为 `tests/b-v*`(脚本兼容识别旧命名)。 - `scripts/create_session.py`:创建的骨架改为仅依赖 `tests/`,并在会话目录内生成 `REVIEW.md`/`TEST_RUN.md`。 - `scripts/verify_session.py`:验证逻辑调整为会话内 `REVIEW.md`;新增 `--strict`(仅在完成填写后用于占位符强校验)。 - `templates/`:将会话模板文件名从 `TEST_*_TEMPLATE.md` 重命名为 `SESSION_TEST_*_TEMPLATE.md`,避免与 `auto-test-skill` 的 `templates/TEST_PLAN_TEMPLATE.md`/`TEST_REPORT_TEMPLATE.md` 重名导致会话骨架误用与口径漂移。 - `templates/SESSION_TEST_PLAN_TEMPLATE.md` / `templates/SESSION_TEST_REPORT_TEMPLATE.md`:`**对应审查**` 字段改为引用 `REVIEW.md` 的相对路径,便于脚本一致性校验。 - `SKILL.md` / `README.md`:同步更新输出结构、命名规范与脚本用法示例(补齐 Codex/Claude Code 两种安装路径,并补充 `.auto-test-code/config.yaml` 项目级覆盖提示)。 -
config.yaml 6.7 KB
# auto-test-code 配置文件 # # 注意: # - `scripts/create_session.py` 仅解析 `directories.tmp`、`directories.tests` 与 `templates.*`。 # - 其余字段用于 A/B 轮规划口径,供 AI/人类参考。 # ============================================================================ # Skill 信息 # ============================================================================ skill_info: name: "auto-test-code" version: "0.5.0" description: "批判性思维驱动的代码自审查与优化技能 - 支持多轮 A 轮静态/动态分析、安全漏洞分类审查与 B 轮代码质量原则检查(文档统一沉淀在 .bensz-api/skills/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/ 隔离工作区)" author: "Bensz Conan" category: "normal" # ============================================================================ # 目录与命名规范 # ============================================================================ directories: tmp: ".bensz-api/skills/auto-test-code" tests: "output/tests" # ============================================================================ # 文档模板配置 # ============================================================================ templates: code_review: "templates/CODE_REVIEW_TEMPLATE.md" session_test_plan: "templates/SESSION_TEST_PLAN_TEMPLATE.md" session_test_run: "templates/SESSION_TEST_RUN_TEMPLATE.md" session_test_report: "templates/SESSION_TEST_REPORT_TEMPLATE.md" b_round_quality: "templates/B_ROUND_CODE_QUALITY_TEMPLATE.md" # ============================================================================ # 轮次控制 # ============================================================================ test_rounds: default_a_rounds: 1 max_a_rounds: 10 min_suggestions_per_round: 10 target_suggestions_range: [15, 20] min_p0_p1_ratio: 60 min_systemic_issues: 3 # ============================================================================ # A轮审查范围 # ============================================================================ a_round_check: independent_review: enabled: true exclude_patterns: - "tmp/**" - "node_modules/**" - "venv/**" - "__pycache__/**" - ".git/**" - "dist/**" - "build/**" - "*.egg-info/**" # 默认扫描模式(可根据项目类型调整) scan_patterns: - "**/*.py" - "**/*.js" - "**/*.ts" - "**/*.java" - "**/*.go" - "**/*.rs" - "**/*.c" - "**/*.cpp" - "**/*.h" - "**/*.hpp" - "**/Cargo.toml" - "**/package.json" - "**/requirements.txt" - "**/setup.py" - "**/pyproject.toml" - "**/pom.xml" - "**/build.gradle" - "**/Dockerfile" - "**/Dockerfile.*" - "**/docker-compose*.yml" - "**/docker-compose*.yaml" - "**/.github/workflows/*.yml" - "**/.github/workflows/*.yaml" - "**/.gitlab-ci.yml" - "**/.circleci/config.yml" - "**/Jenkinsfile" - "**/*.tf" - "**/Chart.yaml" - "**/values*.yaml" - "**/package-lock.json" - "**/pnpm-lock.yaml" - "**/yarn.lock" - "**/poetry.lock" - "**/uv.lock" - "**/go.sum" - "**/Gemfile.lock" # 核心文件必须审查 required_files: [] dimensions: - name: "algorithm_complexity" label: "算法复杂度分析" description: "检查时间复杂度、空间复杂度、算法设计合理性、数据结构选择合理性、逻辑正确性" - name: "boundary_coverage" label: "边界条件覆盖" description: "检查空输入、单元素、极大输入、特殊字符等边界情况" - name: "exception_handling" label: "异常处理完整性" description: "检查错误处理、异常传播、资源释放" - name: "resource_management" label: "资源管理" description: "检查内存泄漏、文件关闭、连接释放、句柄管理" - name: "concurrency_safety" label: "并发安全性" description: "检查竞态条件、死锁、线程安全、原子操作" - name: "security_vulnerability_analysis" label: "安全漏洞分类审查" description: "按 CWE 根因、OWASP 风险、STRIDE 威胁、七大王国、供应链/配置/密码学/认证授权/DoS/内存安全等分类检查漏洞风险" - name: "code_readability" label: "代码可读性与可维护性" description: "检查命名、注释、函数复杂度、代码重复" - name: "test_coverage" label: "测试覆盖充分性" description: "检查单元测试覆盖、边界测试、异常测试" - name: "design_quality" label: "设计质量" description: "检查可扩展性、架构合理性、API 设计、状态管理、领域建模、耦合/内聚、设计模式使用是否恰当" # ============================================================================ # B轮代码质量检查 # ============================================================================ b_round_check: mandatory: true min_suggestions: 10 target_suggestions_range: [15, 20] constructive_suggestion_required: true p0_fix_rate_required: 100 p1_fix_rate_required: 80 dimensions: - name: "algorithm_complexity" label: "算法复杂度分析" description: "检查时间复杂度、空间复杂度、算法设计合理性、数据结构选择合理性、逻辑正确性" - name: "boundary_coverage" label: "边界条件覆盖" description: "检查空输入、单元素、极大输入、特殊字符等边界情况" - name: "exception_handling" label: "异常处理完整性" description: "检查错误处理、异常传播、资源释放" - name: "resource_management" label: "资源管理" description: "检查内存泄漏、文件关闭、连接释放、句柄管理" - name: "concurrency_safety" label: "并发安全性" description: "检查竞态条件、死锁、线程安全、原子操作" - name: "security_vulnerability_analysis" label: "安全漏洞分类审查" description: "按 CWE 根因、OWASP 风险、STRIDE 威胁、七大王国、供应链/配置/密码学/认证授权/DoS/内存安全等分类检查漏洞风险" - name: "code_readability" label: "代码可读性与可维护性" description: "检查命名、注释、函数复杂度、代码重复" - name: "test_coverage" label: "测试覆盖充分性" description: "检查单元测试覆盖、边界测试、异常测试" - name: "design_quality" label: "设计质量" description: "检查可扩展性、架构合理性、API 设计、状态管理、领域建模、耦合/内聚、设计模式使用是否恰当" -
README.md 19.5 KB
# auto-test-code 本 README 面向**使用者**:如何触发并正确使用 `auto-test-code` skill。 执行指令与硬性规范在 `SKILL.md`;默认参数在 `config.yaml`。 --- ## 用法 ### 最推荐用法 ``` 用 auto-test-code 测试 /path/to/project,进行 2 轮 A 轮审查 + B 轮质量检查 ``` ### 其他常见场景 #### 单轮快速审查 ``` 用 auto-test-code 测试 /path/to/project,只做 1 轮 A 轮审查 ``` #### 指定代码语言 ``` 用 auto-test-code 测试 /path/to/project,重点审查 Python 和 JavaScript 代码 ``` #### 指定深挖维度(全覆盖 + 重点深挖) ``` 用 auto-test-code 测试 /path/to/project,本轮深挖并发安全和资源管理问题 ``` --- ## 设计理念 `auto-test-code` 是一个**批判性思维驱动的代码自审查技能**,核心价值在于: - **独立评估模式**:每轮审查都基于当前代码状态独立分析,避免确认偏差 - **批判性思维驱动**:强制使用"刁钻角度"思考,发现深层系统性问题 - **可追溯文档**:所有问题、修复、验证都固化为文档,可复现可复盘 **与普通代码审查的区别**: | 维度 | 普通代码审查 | auto-test-code | |------|-------------|----------------| | 目标 | 发现表面问题(风格、规范) | 发现系统性问题(算法/边界/并发/安全/设计) | | 方法 | 人工检查 + 主观判断 | 批判性思维框架 + 静态分析 + 动态推理 | | 输出 | 口头建议 | 可追溯的文档 + 修复计划 + 验证报告 | | 质量 | 无明确标准 | 强制数量要求(≥10 个问题)和质量门槛(P0+P1 ≥ 60%) | --- ## 功能概述 | 特性 | 说明 | |------|------| | **多轮 A 轮迭代** | 静态分析 → 动态推理 → 计划 → 优化 → 轻量测试(可重复 N 次) | | **独立评估模式** | 每轮基于当前代码状态独立审查,不查看历史记录 | | **批判性思维驱动** | 强制使用刁钻角度(空输入/超大输入/竞态条件/资源耗尽) | | **强制质量要求** | 每轮至少 10 个问题,P0+P1 占比 ≥ 60%,系统性问题 ≥ 3 个 | | **B 轮质量检查** | 9 大维度代码质量原则检查(算法复杂度、边界覆盖、安全漏洞分类审查、设计质量等) | | **可追溯文档** | 统一沉淀到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 隔离工作区(REVIEW/PLAN/RUN/REPORT + artifacts),可复现可复盘 | --- ## 提示词示例 ### 示例 1:完整审查流程(推荐) ``` 你:用 auto-test-code 测试 /path/to/project,进行 3 轮 A 轮审查 + B 轮质量检查 技能:开始执行完整审查流程... [A 轮 #1] 发现 15 个问题(P0: 3, P1: 8, P2: 4) [A 轮 #2] 发现 12 个问题(P0: 2, P1: 7, P2: 3) [A 轮 #3] 发现 10 个问题(P0: 1, P1: 6, P2: 3) [B 轮] 完成 9 维度质量检查 产出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/v*/(含 REVIEW/PLAN/RUN/REPORT)+ .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-v*/ ``` ### 示例 2:单轮快速检查 ``` 你:用 auto-test-code 测试当前目录的代码,只做 1 轮审查 技能:执行单轮 A 轮审查... 发现 18 个问题(P0: 4, P1: 10, P2: 4) 产出:.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/ ``` ### 示例 3:指定深挖维度(全覆盖 + 重点深挖) ``` 你:用 auto-test-code 测试 /path/to/project,深挖并发安全和资源管理问题 技能:本轮深挖维度:并发安全性、资源管理(其余维度仍全覆盖) 刁钻角度(用于深挖):竞态条件、死锁、文件描述符泄漏、内存泄漏 发现 15 个相关问题(P0: 5, P1: 7, P2: 3) ``` ### 示例 4:结合特定代码语言 ``` 你:用 auto-test-code 测试 /path/to/project,重点审查 Python 代码 技能:扫描 *.py 文件... 发现 16 个问题(P0: 3, P1: 9, P2: 4) 聚焦:Python 特有问题(GIL、动态类型、资源管理) ``` --- ## 隔离工作区 - 每次 skill 执行都会在目标项目根目录创建 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 作为当次隔离工作区。 - 所有计划、报告、日志、辅助脚本和中间产物都只写入该工作区,避免把 skill 文件泄露到源项目其他位置。 - 运行可能产生缓存或临时文件的命令时,优先将工作目录、`TMPDIR`、`XDG_CACHE_HOME`、`PYTHONPYCACHEPREFIX` 等重定向到当前工作区。 - 除了用户明确要求的源码修复外,`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 之外不应新增任何 auto-test-code 相关文件。 --- ## 输出文件 技能执行后会在目标代码目录下的隔离工作区生成以下文件: ``` {项目根目录}/ └── tmp/ └── 2026-03-10-15-30/ # 本次技能执行的隔离工作区(示例) ├── .auto-test-code-run.json # 运行清单(记录 code_root / run_id / tests_dir) └── tests/ # 会话目录(计划/过程/结果都在同一处) ├── v202602161028/ # A 轮会话(示例) │ ├── REVIEW.md # 批判性审查(问题清单 + 改进计划) │ ├── TEST_PLAN.md # 测试计划 │ ├── TEST_RUN.md # 测试过程(命令、关键输出摘录、决策) │ ├── TEST_REPORT.md # 测试结果与证据 │ ├── _artifacts/ # 中间产物 │ └── _scripts/ # 会话内辅助脚本(可选) └── b-v202602161028/ # B 轮会话(质量检查 + 验证) └── ... ``` ### 文件说明 | 文件 | 说明 | |------|------| | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/v*/REVIEW.md` | A 轮批判性审查(问题清单 + 改进计划) | | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-v*/REVIEW.md` | B 轮代码质量检查报告(9 大维度评估) | | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_PLAN.md` | 测试计划,列出本轮验证的修复点 | | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_RUN.md` | 测试过程记录(命令、关键输出摘录、关键决策) | | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/TEST_REPORT.md` | 测试报告,包含验证结果和证据 | | `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/*/_artifacts/` | 中间产物(命令输出、日志、截图等) | --- ## 配置选项 在 `config.yaml` 中可调整以下参数: ### 轮次控制 | 参数 | 默认值 | 说明 | |------|--------|------| | `test_rounds.default_a_rounds` | 1 | 默认 A 轮次数 | | `test_rounds.max_a_rounds` | 10 | 最大 A 轮次数 | | `test_rounds.min_suggestions_per_round` | 10 | 每轮最少问题数 | | `test_rounds.target_suggestions_range` | [15, 20] | 目标问题数范围 | | `test_rounds.min_p0_p1_ratio` | 60 | P0+P1 最小占比(%) | ### A 轮审查范围 | 参数 | 默认值 | 说明 | |------|--------|------| | `a_round_check.independent_review.enabled` | true | 独立评估模式开关 | | `a_round_check.independent_review.scan_patterns` | ["**/*.py", "**/*.js", ...] | 扫描文件模式 | | `a_round_check.independent_review.exclude_patterns` | ["tmp/**", "node_modules/**", ...] | 排除目录 | ### B 轮检查维度 | 参数 | 默认值 | 说明 | |------|--------|------| | `b_round_check.mandatory` | true | B 轮是否强制执行 | | `b_round_check.min_suggestions` | 10 | B 轮最少建议数 | | `b_round_check.dimensions` | [9 大维度] | 检查维度列表 | **修改方式**:编辑你的安装目录下的配置(Codex: `~/.codex/skills/auto-test-code/config.yaml`;Claude Code: `~/.claude/skills/auto-test-code/config.yaml`)。如需对单个项目做覆盖,可在目标项目根目录创建 `.auto-test-code/config.yaml`(仅覆盖 `directories.tmp`、`directories.tests` 与 `templates.*`,脚本会做路径安全校验)。 --- ## 配套脚本(可选) 技能提供辅助脚本用于创建和验证测试会话: ### 创建测试会话 ```bash # 在目标代码根目录内执行 RUN_ID=2026-03-10-15-30 python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id v202602161028 # 或 RUN_ID=2026-03-10-15-30 python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id v202602161028 ``` **作用**:自动创建 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 会话目录骨架(REVIEW/PLAN/RUN/REPORT + _artifacts/_scripts),并写入运行清单 `.auto-test-code-run.json` ### 验证测试会话 ```bash python3 ~/.codex/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/2026-03-10-15-30/output/tests/v202602161028 # 或 python3 ~/.claude/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/2026-03-10-15-30/output/tests/v202602161028 ``` **作用**:检查会话完整性(REVIEW/PLAN/RUN/REPORT + 引用一致性);如需强制检查模板占位符是否全部替换,可加 `--strict` --- ## 常见问题 ### Q:技能没有被触发怎么办? A:尝试用更明确的描述: - ✅ "用 auto-test-code 测试 XXX" - ✅ "运行 auto-test-code 对 XXX 进行代码审查" - ❌ "帮我测试一下代码"(太模糊) ### Q:A 轮和 B 轮有什么区别? A: - **A 轮**:批判性代码审查,发现具体问题(P0/P1/P2),要求每轮 ≥ 10 个问题 - **B 轮**:代码质量原则检查,从 9 大维度评估代码质量(算法复杂度、边界覆盖、安全漏洞分类审查、设计质量等) ### Q:问题优先级 P0/P1/P2 是什么意思? A: | 优先级 | 含义 | 典型场景 | |--------|------|----------| | **P0** | 必须修复 | 崩溃风险、安全漏洞、数据损坏、资源泄漏 | | **P1** | 强烈建议 | 性能问题、边界条件缺陷、逻辑错误 | | **P2** | 建议优化 | 代码风格、可读性、冗余代码 | ### Q:如何选择 A 轮次数? A: | 场景 | 推荐轮次 | 理由 | |------|---------|------| | 初次审查/代码质量较差 | 3-5 轮 | 多轮逐步发现深层问题 | | 日常审查/代码质量较好 | 1-2 轮 | 快速检查主要问题 | | 关键项目/上线前 | 5-10 轮 | 确保代码质量达到高标准 | ### Q:什么是"独立评估模式"? A:每轮 A 轮都基于代码的**当前状态**独立分析,不查看历史 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 工作区中的审查文件。好处是让"多轮"带来"多角度",而非"重复确认"。 ### Q:如何理解"批判性思维"? A:使用"刁钻角度"思考代码可能的问题: - 空输入、超大输入、恶意输入会怎样? - 并发访问时会有竞态条件吗? - 资源耗尽时会发生什么? - 异常抛出时资源会正确释放吗? 详见 `references/CRITICAL_THINKING_FOR_CODE.md` --- ## WHICHMODEL - 模型选择最佳实践 **最后更新**:2026-01-25 ### 披露信息 - **覆盖厂商**:Anthropic, OpenAI(2/6 = 33%) - **来源构成**:社区 65%, 学术 20%, 官方 10%, 技术博客 5% - **数据时效**:2024-06 至 2026-01 - **局限性**:未覆盖国产模型,未独立测试代码审查准确率 --- ### 场景化建议 #### 场景 1:标准代码审查(最常见) **触发条件**:日常代码审查,需要发现系统性问题(算法/边界/并发/安全) | 项目 | 建议 | |------|------| | **推荐模型** | Claude Sonnet 4.5 | | **推理强度** | medium-high | | **预期成本** | ~$0.10-0.50/轮 | **理由**: - Sonnet 在代码审查任务中表现出色,SWE-bench 得分 72.7%(接近 Opus) - 速度更快,成本更低(4-5 倍于 Haiku,显著快于 Opus) - [社区测试](https://alirezarezvani.medium.com/claude-opus-4-5-vs-sonnet-i-tested-both-for-90-days-in-claude-code-bb4976923e3a) 显示 Sonnet 在多数代码任务中与 Opus 质量相当 - [内部测试](https://spartner.software/blog/claude-sonnet-vs-opus-which-one-do-you-choose) 显示 Sonnet 解决 64% 编程问题 vs Opus 38% **避免**:无需升级到 Opus,除非遇到极端复杂的算法分析 **来源**:90 天对比测试 + 官方内部数据 --- #### 场景 2:复杂算法与安全审查 **触发条件**: - 需要深度推理的复杂算法(如分布式系统、加密算法) - 安全漏洞深度分析(如竞态条件、内存安全) - 需要多步骤抽象推理的场景 | 项目 | 建议 | |------|------| | **推荐模型** | Claude Opus 4.5 | | **推理强度** | high | | **预期成本** | ~$0.30-1.50/轮 | **理由**: - Opus 在复杂推理任务中表现更优,[社区反馈](https://www.reddit.com/r/ClaudeAI/comments/1por062/claude_opus_45_is_insane_and_it_ruined_other/) 称其为"复杂推理的巨大飞跃" - [用户报告](https://www.reddit.com/r/ClaudeAI/comments/1lqnqn6/anyone_else_in_the_mindset_of_its_opus_or_nothing/) 显示 Opus 在"规划、分析和创建上下文定义"方面更强 - [90 天测试](https://alirezarezvani.medium.com/claude-opus-4-5-vs-sonnet-i-tested-both-for-90-days-in-claude-code-bb4976923e3a) 显示 Opus 在中等投入下成本与 Sonnet 相当 **避免**:简单代码审查不需要 Opus,用 Sonnet 即可 **来源**:Reddit 社区讨论 + 90 天对比测试 --- #### 场景 3:快速批量检查 **触发条件**: - 需要快速审查多个文件/模块 - 成本敏感,需要高性价比 - 不需要深度推理,主要发现明显问题 | 项目 | 建议 | |------|------| | **推荐模型** | Claude Haiku 4.5 或 Sonnet 4.5 | | **推理强度** | low-medium | | **预期成本** | ~$0.02-0.20/轮 | **理由**: - Haiku 成本最低,适合快速批量检查 - 但对于批判性思维驱动的代码审查,Haiku 可能无法发现深层系统性问题 - [社区反馈](https://www.reddit.com/r/ClaudeAI/comments/1o856eb/tested_haiku_45_it_is_fast_but_cant_complete/) 显示 Haiku 在复杂任务中可能力不从心 - **推荐**:快速检查用 Haiku,但质量要求高时用 Sonnet **避免**:需要发现系统性问题(算法/边界/并发)时,不要只用 Haiku **来源**:社区反馈 + 官方文档 --- ### 对比总结 | 模型 | 最适合 | 最不适合 | 相对成本 | 相对速度 | 推荐度 | |------|-------|---------|---------|---------|-------| | **Sonnet 4.5** | 标准代码审查(95% 场景) | 极端复杂的算法推理 | $$$ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | **Opus 4.5** | 复杂算法/安全深度分析 | 简单代码审查(浪费) | $$$$$ | ⭐⭐ | ⭐⭐⭐ | | **Haiku 4.5** | 快速批量检查 | 批判性思维审查(深层问题) | $ | ⭐⭐⭐⭐⭐ | ⭐⭐ | **说明**: - **Sonnet 覆盖 95% 的代码审查场景**:大多数情况下 Sonnet 性价比最高 - **Opus 用于极端复杂场景**:分布式系统、加密算法、深层安全分析 - **Haiku 用于快速检查**:但不推荐用于需要批判性思维的系统性问题发现 --- ### 通用原则 1. **默认从 Sonnet 开始**:95% 的代码审查任务 Sonnet 足够,无需 Opus 2. **批判性思维需要强推理**:auto-test-code 的核心是发现系统性问题(算法/边界/并发/安全),需要比简单工具调用更强的推理能力 3. **成本敏感但质量优先**:代码审查是质量问题,不能只追求低成本而牺牲审查深度 4. **多轮迭代优化成本**:如果需要进行多轮 A 轮审查,可考虑第 1-2 轮用 Sonnet,发现问题后用 Opus 深度分析关键问题 5. **Haiku 的局限性**:虽然 Haiku 速度快、成本低,但 [社区反馈](https://www.reddit.com/r/ClaudeAI/comments/1o856eb/tested_haiku_45_it_is_fast-but-cant-complete/) 显示它在完成基本任务时可能遇到困难 --- ### ⚠️ 争议点 #### Sonnet vs Opus:代码审查应该用哪个? | 观点 | 支持者 | 理由 | |------|-------|------| | **Sonnet 够用** | 社区多数意见 | Sonnet 在代码审查中表现接近 Opus,但速度快、成本低 | | **Opus 必要** | 部分开发者 | Opus 在复杂推理和深层问题发现上仍有优势 | **数据支持**: - [90 天对比测试](https://alirezarezvani.medium.com/claude-opus-4-5-vs-sonnet-i-tested-both-for-90-days-in-claude-code-bb4976923e3a):Opus 在中等投入下成本与 Sonnet 相当 - [官方内部测试](https://spartner.software/blog/claude-sonnet-vs-opus-which-one-do-you-choose):Sonnet 解决 64% 编程问题 vs Opus 38%(实际场景) - [SWE-bench 得分](https://labs.adaline.ai/p/claude-4):Sonnet 72.7%,接近 Opus 水平 **建议**: - **默认使用 Sonnet**:性价比最高,覆盖 95% 代码审查场景 - **仅在以下情况升级 Opus**: - 需要分析复杂算法(如分布式系统、加密算法) - 需要深度安全分析(如竞态条件、内存安全) - Sonnet 无法发现的深层系统性问题 - 关键项目上线前的最终审查 --- ### 更新记录 - 2026-01-25:首次调研,覆盖 Anthropic/OpenAI - 建议:2026-07 重新调研(6 个月后) --- ### 来源链接 **官方文档**: - [Choosing the right model](https://platform.claude.com/docs/en/about-claude/models/choosing-a-model) - [Claude Opus 4.5 vs Sonnet 4.5: Full Report](https://www.datastudios.org/post/claude-opus-4-5-vs-claude-sonnet-4-5-full-report-and-comparison-of-features-performance-pricing-a) **社区讨论**: - [Claude Opus 4.5 is insane (Reddit)](https://www.reddit.com/r/ClaudeAI/comments/1por062/claude_opus_45_is_insane_and_it_ruined_other/) - [Opus or nothing for 90% of tasks (Reddit)](https://www.reddit.com/r/ClaudeAI/comments/1lqnqn6/anyone_else_in_the_mindset_of_its_opus_or_nothing/) - [Tested GPT-5.1, Gemini 3, and Claude Opus 4.5 (Reddit)](https://www.reddit.com/r/ClaudeAI/comments/1pd83la/tested_gpt51_gemini_3_and_claude_opus_45_on/) **对比测试**: - [90-Day Claude Code Decision Framework](https://alirezarezvani.medium.com/claude-opus-4-5-vs-sonnet-i-tested-both-for-90-days-in-claude-code-bb4976923e3a) - [Claude Sonnet 4 Vs Opus 4.1: Which Model To Use For Coding](https://labs.adaline.ai/p/claude-4) - [Claude 3.5 Sonnet vs. Opus: the fastest sprinter or the deepest thinker?](https://spartner.software/blog/claude-sonnet-vs-opus-which-one-do-you-choose) **学术研究**: - [Enhancing Software Code Vulnerability Detection Using GPT-4o and Claude-3.5 Sonnet](https://www.mdpi.com/2079-9292/13/13/2657) - [Assessing the Quality and Security of AI-Generated Code](https://arxiv.org/html/2508.14727v1) --- ## 更多文档 - `SKILL.md` — 技能执行指令与硬性规范 - `config.yaml` — 可配置参数 - `references/` — 详细策略与参考文档 - `CRITICAL_THINKING_FOR_CODE.md` — 批判性思维框架(核心) - `A_ROUND_REVIEW_TEMPLATE.md` — A 轮审查报告结构 - `CODE_SMELLS.md` — 代码异味识别指南 - `SECURITY_PATTERNS.md` — 安全漏洞模式库 - `SECURITY_TAXONOMY.md` — 安全漏洞分类审查体系 - `BOUNDARY_CHECKLIST.md` — 边界条件检查清单 -
SKILL.md 16.5 KB
--- name: auto-test-code category: normal description: 当用户明确要求测试代码、代码审查或代码自检时使用。系统化发现并验证代码问题。⚠️ 不适用:用户只是想实现或优化功能、询问代码问题,或没有明确测试意图。 metadata: author: Bensz Conan short-description: 批判性思维驱动的代码自审查与优化流水线(多轮 A 轮静态/动态/安全分析 + B 轮代码质量原则检查) keywords: - auto-test-code - 代码审查 - code review - 静态分析 - 安全漏洞审查 - 代码自检 --- # auto-test-code(批判性思维驱动的代码自审查技能) ## 目标 当用户明确要求"测试代码"、"运行代码审查"或"进行代码自检"时使用。通过多轮 A 轮批判性代码审查 + B 轮代码质量原则检查,系统化发现、记录、修复程序代码中的问题,并将计划/过程/结果统一沉淀到目标代码根目录的 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 隔离工作区。⚠️ 不适用:用户只是想优化功能(应直接修改)、只是询问代码问题(应直接回答)、没有明确"测试代码"意图。 ## 流程 ### 输入 输入为待审查的代码项目根目录;可选输入包括用户指定的 A 轮次数、运行 ID、测试/审查参数、配置文件和排除目录。必须覆盖核心源代码、配置/构建脚本及测试代码,并排除 `tmp/`、依赖和缓存目录;触发与不适用边界以本 Skill 的 `## 目标` 为准。 ### 执行步骤 #### 工作流程 ##### 隔离工作区硬规则 - 每次 skill 执行开始时,必须先在目标项目根目录创建当次专用工作区:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/`。 - 所有由 skill 生成的计划、报告、日志、辅助脚本与中间产物,**只能**写入当前 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 工作区内。 - 运行可能产生缓存或临时文件的命令时,优先将工作目录、`TMPDIR`、`XDG_CACHE_HOME`、`PYTHONPYCACHEPREFIX` 等重定向到当前 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 工作区。 - 除了用户明确要求的源码修复外,不得把 skill 相关文件写到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 之外的位置,以免污染源软件项目。 ##### 概览 ``` 用户输入(目标代码路径) ↓ [A轮 × N]:静态分析 → 动态推理 → 安全分类审查 → 计划 → 优化 → 轻量测试 ↓ B轮:代码质量原则检查 → 针对性优化 → 轻量验证 ↓ 完成(文档齐全 + 问题闭环) ``` ##### A 轮代码审查(可重复 N 次) ###### A.1 初始化会话(生成测试 ID + 目录) 目标:创建本轮的 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 会话骨架(计划/过程/结果都在同一目录)。 推荐使用确定性脚本: ```bash # 在目标代码根目录内执行(选择你实际的安装路径) RUN_ID=YYYY-MM-DD-HH-MM python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id vYYYYMMDDHHMM # 或 RUN_ID=YYYY-MM-DD-HH-MM python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind a --id vYYYYMMDDHHMM ``` 最低要求: - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 存在 - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/REVIEW.md`、`TEST_PLAN.md`、`TEST_RUN.md`、`TEST_REPORT.md` 和 `_artifacts/` 存在 ###### A.2 批判性分析与计划生成(写入 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 会话目录) 目标:使用**批判性思维**发现代码中的系统性问题,写成可执行计划,按 P0/P1/P2 排序。 输出:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/REVIEW.md` ⚠️ **批判性思维是核心要求**: - **必须使用「刁钻角度」思考**(详见 `references/CRITICAL_THINKING_FOR_CODE.md`) - **必须发现至少 3 个系统性问题**(算法设计/边界条件/并发安全/内存安全/安全漏洞/设计质量) - **禁止列出"不痛不痒"的表面问题**(如"缺少注释"等 P2 级别问题不应占多数) **质量要求**(强制): - 每轮至少发现 10 个问题(P0 + P1 + P2 总和) - 鼓励达到 15-20 个问题 - **P0 + P1 占比必须 ≥ 60%** - **系统性问题 ≥ 3 个**(算法/边界/并发/内存/安全/架构/设计) **核心要求**: - **独立评估原则**(强制): - 每轮 A 轮必须基于目标代码的**当前工作状态**独立分析 - **不查看**历史 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/` 工作区中的审查文件 - 每轮都是一次完整的、无偏见的系统性审查 - **审查范围**(强制): - 必须审查:核心源代码文件(以 `config.yaml:a_round_check.independent_review.scan_patterns` 为准) - 必须审查:配置文件、构建脚本、测试代码 - 排除范围:`tmp/` 等 skill 产物目录,以及 `node_modules/`、`venv/`、`__pycache__/` 等依赖/缓存目录;目标项目的测试代码仍属于必须审查范围 - **全维度覆盖**(强制):每轮必须覆盖所有审查维度(以 `config.yaml:a_round_check.dimensions` 为准);不得以“本轮不聚焦”为由跳过任何维度 - **深挖维度**(可选):可在全覆盖基础上额外指定 1-2 个**深挖维度**,对该维度使用刁钻角度做更细致分析;多轮时建议轮换深挖点 - **刁钻角度**:在深挖维度上必须使用至少一个刁钻角度(空输入/超大输入/恶意输入/竞态条件/资源耗尽/权限绕过/供应链污染) - **优先级依据**:P0/P1/P2 必须有明确的判定标准 - P0: 崩溃风险、可利用安全漏洞、未授权访问、敏感信息泄露、数据损坏、死锁/活锁、内存泄漏 - P1: 性能问题、边界条件缺陷、逻辑错误、资源泄漏、需要前置条件或组合利用的安全风险 - P2: 代码风格、可读性、冗余代码 - **可追溯性**:每个问题必须包含位置、现象、影响、修复建议、验证方法 **批判性思维框架**(必读): - `references/CRITICAL_THINKING_FOR_CODE.md` ⚠️ **核心文档,必须使用** - 框架 1: 静态分析视角(算法设计/数据结构/代码复杂度/设计质量) - 框架 2: 动态推理视角(边界条件/异常处理/资源管理) - 框架 3: 问题质量标准(黄金公式 + 质量检查清单) - 框架 4: 设计质量视角(架构/扩展性/API/状态/建模/耦合/模式) - 框架 5: 安全漏洞分类视角(CWE/OWASP/STRIDE/七大王国/CVSS) - `references/A_ROUND_REVIEW_TEMPLATE.md` ⚠️ 代码审查计划模板 - `references/CODE_SMELLS.md` 代码异味识别指南 - `references/SECURITY_PATTERNS.md` 安全漏洞模式库 - `references/SECURITY_TAXONOMY.md` 安全漏洞分类审查体系(必须用于安全维度) - `references/DESIGN_ANTI_PATTERNS.md` 设计反模式识别指南 ###### A.3 执行优化与轻量测试(写入 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/`) 目标:按计划逐项修复,并用轻量测试验证。 输出: - 过程:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/TEST_RUN.md` - 结果:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/TEST_REPORT.md` 轻量测试原则: - 只验证"核心路径"与"本轮变更点" - 每条结论必须有可复现证据(命令输出、日志、对比结果) - 中间产物放入 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/_artifacts/` 可选增强: ```bash python3 ~/.codex/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM # 或 python3 ~/.claude/skills/auto-test-code/scripts/verify_session.py --require-review .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM ``` 说明:`verify_session.py --strict` 仅用于你已将会话文档中的模板占位符全部替换后的最终自检;新建骨架默认会失败(属于预期行为)。 ###### A.4 是否进入下一轮 ⚠️ **强制检查**: - [ ] 本轮已提出至少 10 个问题(P0 + P1 + P2 总和) **进入下一轮 A 轮的条件**: - [ ] 用户指定的轮次数未完成 - [ ] 本轮问题(P0/P1/P2)已全部闭环 **重要**:A 轮结束后,必须进入 B 轮代码质量检查。 ##### B 轮代码质量检查 ⚠️ **强制执行**:B 轮代码质量检查是自动测试流程的强制性环节。 ###### B.1 产出质量检查报告(写入 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 会话目录) 目标:对 A 轮后的最新代码做系统性质量检查。 输出:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/REVIEW.md` 推荐使用确定性脚本创建 B 轮会话目录: ```bash # 在目标代码根目录内执行(选择你实际的安装路径) RUN_ID=YYYY-MM-DD-HH-MM python3 ~/.codex/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind b --id vYYYYMMDDHHMM --a-test-id vYYYYMMDDHHMM # 或 RUN_ID=YYYY-MM-DD-HH-MM python3 ~/.claude/skills/auto-test-code/scripts/create_session.py --code-root . --run-id "$RUN_ID" --kind b --id vYYYYMMDDHHMM --a-test-id vYYYYMMDDHHMM ``` 检查维度(以 `config.yaml` 的 `b_round_check.dimensions` 为准): - 算法复杂度分析 - 边界条件覆盖 - 异常处理完整性 - 资源管理(内存/文件/连接) - 并发安全性 - 安全漏洞分类审查(CWE/OWASP/STRIDE/七大王国/CVSS) - 代码可读性与可维护性 - 测试覆盖充分性 - 设计质量(可扩展性/架构/API/状态/建模/耦合/模式) 模板:`templates/B_ROUND_CODE_QUALITY_TEMPLATE.md` ###### B.2 B 轮优化与验证(写入 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/`) ⚠️ **强制修复要求**: - B 轮发现的 **所有 P0-P2 问题都必须处理** - P0 问题必须修复 - P1 问题必须修复(除非有合理理由) - 每个修复必须有验证证据 **完成条件**: - [ ] P0 问题修复率 = 100% - [ ] P1 问题修复率 ≥ 80% - [ ] 所有修复都有可复现证据 #### 可复用资源 - 配置:`config.yaml` - 模板:`templates/` - A 轮审查:`templates/CODE_REVIEW_TEMPLATE.md` - B 轮质量检查:`templates/B_ROUND_CODE_QUALITY_TEMPLATE.md` - 测试计划:`templates/SESSION_TEST_PLAN_TEMPLATE.md` - 测试过程:`templates/SESSION_TEST_RUN_TEMPLATE.md` - 测试报告:`templates/SESSION_TEST_REPORT_TEMPLATE.md` - 参考:`references/` - **批判性思维指南**:`references/CRITICAL_THINKING_FOR_CODE.md` ⚠️ - A 轮审查结构:`references/A_ROUND_REVIEW_TEMPLATE.md` ⚠️ - 代码异味识别:`references/CODE_SMELLS.md` - 安全漏洞模式:`references/SECURITY_PATTERNS.md` - 安全漏洞分类审查体系:`references/SECURITY_TAXONOMY.md` ⚠️ - 边界条件检查清单:`references/BOUNDARY_CHECKLIST.md` - 设计反模式识别:`references/DESIGN_ANTI_PATTERNS.md` - 辅助脚本:`scripts/create_session.py` - 辅助脚本:`scripts/verify_session.py` ### 输出 #### 交付物 本技能的审查结论和验证证据必须落盘到目标代码项目的隔离工作区中,形成可追溯、可复核、后续可接续的会话文件。 默认交付根目录为 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/`;实际目录以 `config.yaml` 中的 `directories.tmp` 和 `directories.tests` 为准。 每个 A 轮或 B 轮会话目录都必须包含同一组文件: - `REVIEW.md`:审查报告。A 轮记录批判性代码审查的问题清单与改进计划;B 轮记录代码质量原则检查结果。 - `TEST_PLAN.md`:测试计划,说明本轮要验证的修复点、核心路径和预期证据。 - `TEST_RUN.md`:测试过程记录,包含实际执行的命令、关键输出摘录和关键决策。 - `TEST_REPORT.md`:测试结果报告,包含结论、证据、遗留问题和后续建议。 - `_artifacts/`:中间产物目录,用于保存命令输出、日志、截图、对比结果等证据。 ### 输出管理 #### BenszAPI 任务工作区 #### 目录与命名规范 - 运行工作区:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/`,同一次技能执行的所有 A/B 轮必须复用同一个 `run_id`。 - 会话 ID:`vYYYYMMDDHHMM`,使用分钟级时间戳。 - A 轮会话目录:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/vYYYYMMDDHHMM/`。 - B 轮会话目录:`.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/`,即在同类会话 ID 前增加 `b-` 前缀。 - 兼容旧目录:`verify_session.py` 可识别历史目录名 `tests/B轮-vYYYYMMDDHHMM/`;新建目录必须使用 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/b-vYYYYMMDDHHMM/`。 - 废弃目录:`reviews/` 不再创建、不再写入;如目标项目中存在旧的 `reviews/`,只视为历史遗留并在审查时排除。 ### 校验 #### 完成条件(验收) - [ ] 用户指定的 A 轮次数已完成 - [ ] B 轮代码质量检查已完成 - [ ] 每轮 A 轮平均问题数量 ≥ 10 个 - [ ] **每轮 P0 + P1 占比 ≥ 60%** - [ ] **每轮系统性问题 ≥ 3 个**(算法/边界/并发/内存/安全/设计) - [ ] 关键问题(P0/P1)已闭环 - [ ] `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/auto-test-code/{yyyy-mm-dd-hh-mm}/output/tests/` 会话结构完整且可追溯(每轮都有 `REVIEW.md`、`TEST_PLAN.md`、`TEST_RUN.md`、`TEST_REPORT.md` 和 `_artifacts/`) ### 失败与恢复 会话脚本、测试命令或依赖失败时,保留 `TEST_RUN.md`、日志和 `_artifacts/` 中的命令输出,标记当前轮次未完成并报告可复现命令;修复后可在同一 `run_id` 的会话目录重试。目标路径越界、输入缺失或无法安全判断时停止,不把失败或不确定结果写成通过。 ## 约束 <!-- BEGIN COMMON CONSTRAINTS --> <!-- Source-Hash: sha256:15120201e9e0c7569517261d57ecefb63ac279c26ed13876f8e95b6dc35854d3 --> <!-- Template-ID: skill-common-constraints; Template-Version: 1; Sync-Policy: exact-block --> ### 公共硬约束 本块由 `docs/templates/skill-common-constraints.md` 统一维护;每个 `SKILL.md` 的 `## 约束` 必须逐字同步本块,不得在副本中改写公共规则。 - 任务需要落盘时,使用唯一的 `./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/` 根目录;共享材料放入 `shared/`,Skill 专属材料放入该 Skill 的 `input/`、`output/`、`log/`。 - 正式交付物、源代码和正式计划按项目约定保存,不写入任务工作区;未经授权不覆盖、删除、迁移或远程写入。 - 项目维护变更检查 BAC 可用性并记录需求、AI 产出、工具结果、文件改动和验证摘要;BAC 只做过程审计,不替代署名、责任或合规判断。 - 不记录 API Key、访问令牌、密码、Cookie、环境/凭据文件、私有 Prompt、身份信息、本地用户名、主机名或不必要的大体积原始数据。 - 文件路径必须规范化并限制在授权项目范围内;外部 URL、子进程和网络访问遵循最小权限,防止路径遍历、SSRF 和命令注入。 - Skill 版本唯一记录在自身 `config.yaml:skill_info.version`;公开 API、协议、目录或配置变更同步文档与 `CHANGELOG.md`。 - `bensz-collect-bugs` 是一个 Agent Skill;仅将 Bensz Agent Skill 或 Bensz 基础设施本身的设计缺陷交给它。先脱敏写入 `~/.bensz-skills/bugs/`,当前任务不中断,只有用户明确要求才公开上报,禁止直接修改用户已安装的 Skill 源码。 <!-- End of canonical common constraints. --> <!-- END COMMON CONSTRAINTS -->
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.