pdlc-test-setup
立测试地基(探测技术栈 → 验证并生成 test-commands.yml → 脚手架测试目录 → 接本地钩子)
Install
npx skills add https://github.com/kanfu-panda/pdlc-skills/tree/main/skills/pdlc-test-setup
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kanfu-panda-pdlc-skills@llmmart
git clone https://github.com/kanfu-panda/pdlc-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole kanfu-panda/pdlc-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
立测试地基
给项目一键立起「客观 check」的地基:探测技术栈 → 逐条验证命令真能跑 → 写 docs/00_standards/test-commands.yml → 脚手架测试目录 → 接本地钩子。
⛔ IRON LAW · 不可违反的硬门禁
以下规则为不可协商的执行约束:
- 文件必须落盘:所有带编号(功能ID / 缺陷ID)的文档,必须作为实际文件写入磁盘,不可仅在对话中输出。
- 阶段必须落章:每个阶段完成后必须在状态机
docs/.pdlc-state/<feature-id>.json追加 history,不可跳过。 - 测试必须存在:进入
/pdlc-implement前,对应测试必须存在且处于红灯状态。违反则中止。 - 自检必须执行:段二自检为强制步骤,不得以"已经很好了"为由跳过。
- 防循环:段三修复为单次,不递归。无法自动修复的问题记录到报告,继续往下走。
- 状态必推进:成功执行某 phase 后
current_stage必须变更。收尾时若发现current_stage未推进,视为失败并报错,不得静默返回(防止外层循环拿滞后的状态空转烧额度)。唯一例外:命中人工点主动 block 时,current_stage保持不变但必须写last_phase_result.ok=false+blocked_reason。
违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。
非交互模式(--autonomous)
若本命令的参数含 --autonomous,本命令进入无人值守模式,按以下规则处理原本需要人应答的交互点。参数是唯一真源:不带 --autonomous 即为交互模式,一切照旧正常询问用户;绝不回读状态机 run_mode 兜底(「掉出 autonomous」是安全的失败方向)。
- 流程性确认(如「测试已绿是否继续」「是否覆盖已有文件」)→ 不询问,按预设默认前进,并把决策追加到状态机
history[].auto_decisions[]:{ "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" } - 真需人判断(PRD 关键取舍、评审「需人工确认」项、真实循环依赖等无法安全默认的点)→ 不猜:
current_stage保持不变(不推进)- 写
last_phase_result.ok = false且blocked_reason = "<原因>" - 末行输出哨兵:
<<<PDLC blocked reason="<原因>">>> - 立即结束命令,交还人类
- 破坏性操作(发布 / 部署 / 打 tag / 触发 CI / DROP / force-push 等不可逆·外发操作)→
--autonomous无效,仍必须人工显式确认。 - 顺手的 sidecar 产物(如缺失时创建
CHANGELOG.md、补全文档 PDLC-TRACE 的创建时间等本阶段职责内、可安全默认的辅助改动)→ 视为流程性默认,直接做并记入auto_decisions[];这类改动不新增外部副作用,不属破坏性操作。
进入 autonomous 模式时,在状态机顶层写
run_mode: "autonomous"仅供留痕(复盘区分人工 vs 循环产出)。
为什么需要它
pdlc 的命门是「checks 只认命令退出码,绝不用模型自评」——pdlc-tdd / pdlc-implement / pdlc-review
与外层循环全都从 docs/00_standards/test-commands.yml 取命令。但在此之前没有任何东西帮你把这个文件立起来,
没有它,整条客观化链路就是空的。本命令把这块地基变成 turnkey。
⛔ 本命令最重要的一条纪律:写进
test-commands.yml的每条命令,必须先被真跑过一次、亲眼看到退出码。 一条"看起来对但跑不了"的命令比留空更坏——它会让下游每个阶段都拿到假的checks, 而整个 pdlc 的可信度正建立在这些 checks 是真的之上。猜出来的命令一律不写。
--refresh:让这份 yml 跟上项目的演进
项目会漂移——脚本改名、runner 换代、工具从依赖里移除、子项目增删。这份 yml 一旦过期,
下游所有 checks 就开始失真。--refresh 是重新探测 + 给出 diff,而不是从头再来:
- 逐条复跑现有命令,按
check-commands.md的三态判定谁还活着(跑不通 ≠ 检查没过)。 - 重新探测候选,与现状对比,得出变更清单。
- 按方向决定自不自动(这条是安全底线):
跑 check 命令:退出码的三态语义
命令取自 docs/00_standards/test-commands.yml(唯一真源)。逐条真跑,按退出码分三态——
不是两态。这是 IRON LAW「checks 只认客观事实」在执行层的落法:
| 观察到的 | 含义 | 写进 checks |
|---|---|---|
退出码 0 |
通过 | 对应键 = true |
| 退出码非 0(命令跑起来了,只是没过) | 未通过 | 对应键 = false |
退出码 127 / command not found / 脚本文件不存在 / 该项为空字符串 |
无法判定 | 省略该键,或写 null——绝不能是 false |
⛔ 唯一的红线是不许写
false:那是会误导人的虚报——它说的是"检查失败了", 于是有人去查代码,但真正的问题是配置过期,代码可能完全没毛病。省略键与
null等价,两种都可以:对消费方而言无法区分(jq '.checks.lint_clean'在两种情况下都返回null)。null甚至更明确——省略是歧义的("没看"还是"看了判不出"),null明说"看了,判不出"。别在这上面纠结,力气花在不写false上。这与「没有检查命令可跑的阶段 →
checks: {}」同源。
「跑不了」= test-commands.yml 过期信号(顺带检测,零额外成本)
命令跑不起来,几乎总意味着这份 yml 已经跟不上项目了——脚本改名、runner 换了、 工具从依赖里移除、子项目路径调整。真实项目里这类漂移是常态(例如某前端框架升级后 移除了内置 lint 子命令,而 yml 里那条命令还在)。
由于各阶段本来就在跑这些命令,这个信号是白捡的。检测到时:
- 在本阶段的报告里单列一条:「
test-commands.yml疑似过期」,写明是哪一项、 观察到什么(退出码 / 报错原文)、以及为什么判定为"跑不了"而非"没通过"。 - 提示补救:
/pdlc-test-setup --refresh(重新探测并给出 diff)。 - 不要自作主张改 yml——本阶段的职责是干活,不是改配置;只报告,不动手。
变更方向决定自动化程度(--refresh 时适用)
更新这份 yml 等于改变"通过"的定义,所以按方向区别对待:
| 方向 | 例子 | 处理 |
|---|---|---|
| 让闸门变严 | 空着的 e2e 现在能跑了、覆盖率阈值上调 |
可自动应用,报告留痕 |
| 平移替换 | 命令改名但语义相同,且新命令已验证能跑 | 可自动应用,报告留痕 |
| 让闸门变松 | 删掉某条 check、把命令改成空、下调阈值 | 必须人确认,绝不自动 |
⚠️ 这条方向规则是防「自动修复把闸门修没了」:lint 命令坏掉时,把它留空是最省事的 "修法",结果闸门悄悄松了、报告还是绿的——比不更新更危险。 变严可以自动,变松必须由人签字。
- 不管自动与否,全部变更都要在报告里列出:改了什么、为什么、依据是哪次真跑的退出码。
自动应用的也要能一眼看出来,便于事后
git diff复核。
⚠️ 最危险的"自动修复"是把坏掉的 check 留空——闸门瞬间松了,报告还是绿的。 所以留空 / 删除 / 降阈值 / 替换一律走人工确认,
--autonomous也不豁免。替换也算,哪怕换上去的命令能跑、看起来更严:原命令的规则往往已不可考,没法证明新命令与它等价—— 新命令可能在这一维更严、在另一维更松。方向不可判,就不能当作变严自动应用(与三态「查不了 ≠ 没问题」 同一条纪律)。能自动应用的只有一种:原来留空的项,这次探测到命令并真跑验证通过——那是从「没测」到 「测了」,方向确定。
从哪来的过期信号:不用你盯着——pdlc-tdd / pdlc-implement / pdlc-review 每次跑 check
时遇到"命令跑不了"都会提示,/pdlc-quality 的报告里还有专门的「配置健康度」一节。
看到提示再来 --refresh 即可。
段一:探测与验证
1.1 技术栈探测
扫描特征文件,识别语言 / 包管理器 / 测试框架:
| 特征文件 | 栈 | 典型 unit | 典型 coverage | 典型 lint |
|---|---|---|---|---|
Cargo.toml |
Rust | cargo test |
cargo llvm-cov --fail-under-lines <阈值> |
cargo clippy -- -D warnings |
package.json |
Node | pnpm test / npm test |
vitest run --coverage.thresholds.lines=<阈值> |
npx eslint . |
pyproject.toml / requirements.txt |
Python | pytest |
pytest --cov --cov-fail-under=<阈值> |
ruff check . |
go.mod |
Go | go test ./... |
go test ./... -cover |
golangci-lint run |
pom.xml / build.gradle |
JVM | mvn test / ./gradlew test |
jacoco check | mvn checkstyle:check |
仅 *.sh |
Shell | 项目自有测试脚本 | —(通常无) | shellcheck <文件> |
多语言 / monorepo:逐个子项目探测;test-commands.yml 只能有一组命令,所以要么用能覆盖全仓的聚合命令
(如 pnpm -r test),要么与用户确认以哪个子项目为准。探测不到唯一答案时不要自己拍板(见 §1.3)。
1.2 逐条验证(不可跳过)
对每个候选命令真的跑一次,按退出码归类:
| 观察到的结果 | 结论 | 动作 |
|---|---|---|
| 退出码 0 | 命令可用且当前通过 | 采纳 |
| 退出码非 0、非 127,且输出像测试/lint 报告 | 命令可用,只是当前有失败项 | 采纳(地基是"命令能跑",不是"当前全绿") |
退出码 127 / command not found / 工具未安装 |
命令不可用 | 留空,在报告里写明缺什么 |
| 无对应配置(如没配覆盖率工具) | 该项本项目暂无 | 留空 |
| 命令挂起 / 需要交互 | 不适合做自动 check | 留空,报告里说明 |
⚠️ 留空是合法且诚实的结果,与状态机里「没有检查命令可跑的阶段 →
checks: {}留空」同一条纪律。 宁可空着并在报告里提示怎么补,也不要写一条没验证过的命令。
覆盖率达标线写死在命令参数里(如 --cov-fail-under=85),不做二次解释——这样"达标"就是退出码本身,
不需要任何一方去解析百分比数字。默认阈值 85%;项目已有更高要求则沿用已有。
1.3 需要人拍板的点(--autonomous 下 block,不猜)
以下属判断题而非流程题,不得自动选,须写明原因交还人类:
- 探测到多个并列候选(如同时有
jest和vitest配置),无法判定以哪个为准 - 零候选(项目还没有任何测试框架)——装哪个框架是技术选型,必须人定
- monorepo 里以哪个子项目 / 哪条聚合命令为准
- 覆盖率阈值定多少(若项目无既有约定)
探测到唯一候选且验证通过 → 属流程性确认,--autonomous 下自动采纳并在报告里留痕。
段二:落地
2.1 写 docs/00_standards/test-commands.yml
以 本 skill 目录下的 assets/test-commands-template.yml 为骨架。这是 surface 型产物——就地编辑,不做 -v2 累积。
- 文件已存在 → 不覆盖。改为逐条校验现有命令是否仍能跑:
- 仍能跑 → 保持原样(用户的选择优先于探测结果)
- 已跑不通(工具改名 / 脚本删了)→ 报告里列出,建议改法,等人确认。
--autonomous下同样不改原值—— 建议的替换命令哪怕已真跑通过,也只写进报告的「⚠️ 待人工」,yml 里保持原样 - 缺失的项(空字符串)→ 若这次探测到可用命令,提议补上
- 文件不存在 → 用本次验证通过的命令生成;未验证通过的项留空字符串。
2.2 脚手架测试目录(已有则不动)
按栈惯例建空目录 + 一个占位说明,不生成业务测试用例:
- Rust
tests/、Nodesrc/__tests__/或tests/、Pythontests/、Go 同包*_test.go、JVMsrc/test/java/ - 跟随项目既有布局,不新造平行目录。守卫侧(
/pdlc-tdd、/pdlc-implement)的测试定位规则 是布局无关的,所以这里不必迁就任何预设结构
2.3 接本地钩子(不进 CI)
在本地 git 钩子里跑基础 check(husky / lefthook / pre-commit / 原生 .git/hooks,按项目已有的来):
- pre-commit:
lint(快,秒级) - pre-push:
unit(+coverage若已配)
不新建 CI workflow:这些 check 本地秒级可得,放 CI 只会让每次迭代都烧配额。 已有 CI 的项目也不改它的触发条件——那需要项目所有者单独授权。
2.4 老项目:可选的轻量底线回填
仅当用户要求:为当前覆盖率最低的若干核心模块补特征化测试(characterization test,锁住现有行为),
把覆盖率抬到阈值线。这不是补齐测试,只是让地基能立住。深度用例仍走 /pdlc-tdd。
段三:自检(强制)
段二:自检(强制)
重新阅读本次产出物,按质量关卡清单逐项检查。勾选已通过,标注未通过原因。
注意:自检清单的具体内容由各命令自行定义,本片段只规定结构。
段三:修复(单次,不递归)
针对自检段标注为未通过的项:
- 可自动修复:直接修复(如补缺字段、修正格式、补齐缺失段落)
- 修复后回验:再次运行自检,确认被修复项现在通过
- 无法自动修复:记录到自审报告,不再尝试,流程继续
⚠️ 单次修复原则:若一轮修复后仍有项未通过,不再递归修复,防止死循环。
自检清单(必须全部检查)
-
test-commands.yml里每一条非空命令,都在本次会话中被真跑过、看到过退出码 - 收尾复跑一遍:从写好的 yml 里逐条读命令再跑一次,确认与写入时的结论一致(防止写错路径 / 引号)
- 留空的项,报告里都写明了「为什么空」和「怎么补」
- 覆盖率阈值已写死在命令参数里,不依赖任何一方解析百分比
- 测试目录跟随项目既有布局;若写了
test-commands.yml,其unit命令能定位到这些测试 - 钩子是本地的,没有新建或修改任何 CI workflow
- 已存在的
test-commands.yml没有被静默覆盖 - (
--refresh时)所有变更已在报告里列出;没有任何"让闸门变松"的改动被自动应用 - (
--refresh时)已存在但跑不通的命令原值未动;替换方案只写在报告里等人确认,没有以「变严」为由自动替换
段四:修复(单次,不递归)
防循环规则
本命令所有的自检-修复循环均受以下约束:
- 单次检查:同一个自检清单在本次命令执行中只跑一次(起始 + 修复后验证共两次读)
- 单次修复:发现的问题只尝试修复一轮
- 不递归:修复后不再重新触发自检的全量重跑
- 失败降级:无法自动修复的问题 → 记录到自审报告 → 流程继续 → 最终报告标注待人工处理
这是为了防止 agent 在"修完再查、查完再修"的往返中陷入死循环。
- 复跑发现某条命令与写入时结论不一致 → 修正或改为留空
- 无法自动修复 → 记入报告,交还人类
段五:交接
段四:交接(Handoff)
命令完成后必须输出以下格式的最终消息:
✅ <阶段名> 完成:<主要产出物路径>
📊 自检:<通过数>/<总数> 通过(若有未通过,附要点)
📦 状态快照:docs/.pdlc-state/<feature-id>.json
👉 下一步:/pdlc-<next_step>
(如果有分叉)或 /pdlc-<alt>(条件:<选择依据>)
规则:
- 主流程命令(写状态机的命令;下一跳见正文里「本命令的状态机取值」)必须显式输出"下一步",不可省略
- 工具型命令(Layer 3)可以没有
next_step,此时输出👉 下一步:(本次流程结束,无后续) - 分叉场景必须说明选择条件,例如"若需补充测试用例 →
/pdlc-tdd;若测试已齐 →/pdlc-review"
本命令的 handoff 输出:
✅ 测试地基已立:docs/00_standards/test-commands.yml
unit : <命令> (退出码 <N>,已验证)
coverage : <命令 | 留空> (<验证结论 | 为什么空>)
lint : <命令> (退出码 <N>,已验证)
e2e : <命令 | 留空> (<验证结论 | 为什么空>)
🪝 本地钩子:pre-commit → lint · pre-push → unit
📁 测试目录:<路径列表>
⚠️ 待人工:<留空项怎么补 / 需要拍板的选型>
👉 下一步:/pdlc-tdd <功能描述> —— 本命令只立地基,深度用例走 TDD
诚实边界(务必如实说明,不要夸大)
- 本命令只立地基 + 可选补底线,不生成完整测试套件。AI 生成的测试容易浅、容易只测 happy path,
真正的用例设计仍走
/pdlc-tdd(测试先行、红灯门)。 - 留空的项就是当前没有,不要为了让输出好看而填一条没验证过的命令。
- 覆盖率阈值只是一条线,过线不等于测得好——它挡的是"几乎没测",不保证用例有效。
目标项目: $ARGUMENTS
Files (pdlc-skills)
-
assets
-
test-commands-template.yml 772 B
# docs/00_standards/test-commands.yml — 本项目 check 命令的唯一真源 # # pdlc-tdd / pdlc-implement / pdlc-review 与外层循环都从这里取「check」,消除歧义。 # 每条是一个可执行命令;约定「退出码 0 = 通过」。 # 达标线(如覆盖率阈值)写死在命令自身参数里,不做二次解释。 # 用不到的项留空字符串。 unit: "<单元测试命令>" # 例:cargo test / ./gradlew test / pnpm test coverage: "<覆盖率命令>" # 例:cargo llvm-cov --fail-under-lines 85(达标线写死在 --fail-under) lint: "<lint 命令>" # 例:cargo clippy -- -D warnings / npx eslint . e2e: "<e2e 命令>" # 例:cargo test --test e2e / pnpm exec playwright test(可选)
-
-
SKILL.md 18.9 KB
--- name: pdlc-test-setup description: 立测试地基(探测技术栈 → 验证并生成 test-commands.yml → 脚手架测试目录 → 接本地钩子) argument-hint: [项目目录] [--refresh] [--autonomous] allowed-tools: Read, Write, Edit, Glob, Grep, Bash layer: 3 stage: engineering artifact_type: surface produces: - docs/00_standards/test-commands.yml requires: [] next_step: null terminal_state: null recommended_model: sonnet recommended_effort: medium --- # 立测试地基 给项目一键立起「客观 check」的地基:**探测技术栈 → 逐条验证命令真能跑 → 写 `docs/00_standards/test-commands.yml` → 脚手架测试目录 → 接本地钩子**。 <!-- @include templates/prompts/iron-law.md(已内联于下方,无需另读) --> ⛔ **IRON LAW · 不可违反的硬门禁** 以下规则为**不可协商**的执行约束: 1. **文件必须落盘**:所有带编号(功能ID / 缺陷ID)的文档,必须作为实际文件写入磁盘,不可仅在对话中输出。 2. **阶段必须落章**:每个阶段完成后必须在状态机 `docs/.pdlc-state/<feature-id>.json` 追加 history,不可跳过。 3. **测试必须存在**:进入 `/pdlc-implement` 前,对应测试必须存在且处于红灯状态。违反则中止。 4. **自检必须执行**:段二自检为强制步骤,不得以"已经很好了"为由跳过。 5. **防循环**:段三修复为单次,不递归。无法自动修复的问题记录到报告,继续往下走。 6. **状态必推进**:成功执行某 phase 后 `current_stage` 必须变更。收尾时若发现 `current_stage` 未推进,视为失败并报错,**不得静默返回**(防止外层循环拿滞后的状态空转烧额度)。唯一例外:命中人工点主动 block 时,`current_stage` 保持不变但必须写 `last_phase_result.ok=false` + `blocked_reason`。 **违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。** <!-- @include-end templates/prompts/iron-law.md --> <!-- @include templates/prompts/noninteractive.md(已内联于下方,无需另读) --> ## 非交互模式(`--autonomous`) 若本命令的参数含 `--autonomous`,本命令进入**无人值守**模式,按以下规则处理原本需要人应答的交互点。**参数是唯一真源**:不带 `--autonomous` 即为交互模式,一切照旧正常询问用户;绝不回读状态机 `run_mode` 兜底(「掉出 autonomous」是安全的失败方向)。 1. **流程性确认**(如「测试已绿是否继续」「是否覆盖已有文件」)→ **不询问**,按预设默认前进,并把决策追加到状态机 `history[].auto_decisions[]`: ```json { "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" } ``` 2. **真需人判断**(PRD 关键取舍、评审「需人工确认」项、真实循环依赖等无法安全默认的点)→ **不猜**: - `current_stage` 保持不变(不推进) - 写 `last_phase_result.ok = false` 且 `blocked_reason = "<原因>"` - 末行输出哨兵:`<<<PDLC blocked reason="<原因>">>>` - 立即结束命令,交还人类 3. **破坏性操作**(发布 / 部署 / 打 tag / 触发 CI / DROP / force-push 等不可逆·外发操作)→ `--autonomous` **无效**,仍必须人工显式确认。 4. **顺手的 sidecar 产物**(如缺失时创建 `CHANGELOG.md`、补全文档 PDLC-TRACE 的创建时间等本阶段职责内、可安全默认的辅助改动)→ 视为流程性默认,**直接做并记入 `auto_decisions[]`**;这类改动不新增外部副作用,不属破坏性操作。 > 进入 autonomous 模式时,在状态机顶层写 `run_mode: "autonomous"` 仅供留痕(复盘区分人工 vs 循环产出)。 <!-- @include-end templates/prompts/noninteractive.md --> ## 为什么需要它 pdlc 的命门是「`checks` 只认命令退出码,绝不用模型自评」——`pdlc-tdd` / `pdlc-implement` / `pdlc-review` 与外层循环全都从 `docs/00_standards/test-commands.yml` 取命令。但**在此之前没有任何东西帮你把这个文件立起来**, 没有它,整条客观化链路就是空的。本命令把这块地基变成 turnkey。 > ⛔ **本命令最重要的一条纪律**:**写进 `test-commands.yml` 的每条命令,必须先被真跑过一次、亲眼看到退出码。** > 一条"看起来对但跑不了"的命令**比留空更坏**——它会让下游每个阶段都拿到假的 `checks`, > 而整个 pdlc 的可信度正建立在这些 checks 是真的之上。**猜出来的命令一律不写。** ## `--refresh`:让这份 yml 跟上项目的演进 项目会漂移——脚本改名、runner 换代、工具从依赖里移除、子项目增删。这份 yml 一旦过期, 下游所有 `checks` 就开始失真。`--refresh` 是**重新探测 + 给出 diff**,而不是从头再来: 1. **逐条复跑现有命令**,按 `check-commands.md` 的三态判定谁还活着(跑不通 ≠ 检查没过)。 2. **重新探测候选**,与现状对比,得出变更清单。 3. **按方向决定自不自动**(这条是安全底线): <!-- @include templates/prompts/check-commands.md(已内联于下方,无需另读) --> ## 跑 check 命令:退出码的三态语义 命令取自 `docs/00_standards/test-commands.yml`(唯一真源)。逐条真跑,**按退出码分三态**—— 不是两态。这是 IRON LAW「checks 只认客观事实」在执行层的落法: | 观察到的 | 含义 | 写进 `checks` | |---|---|---| | 退出码 `0` | 通过 | 对应键 = `true` | | 退出码非 0(命令**跑起来了**,只是没过) | 未通过 | 对应键 = `false` | | 退出码 `127` / `command not found` / 脚本文件不存在 / 该项为空字符串 | **无法判定** | **省略该键,或写 `null`**——**绝不能是 `false`** | > ⛔ **唯一的红线是不许写 `false`**:那是**会误导人的虚报**——它说的是"检查失败了", > 于是有人去查代码,但真正的问题是**配置过期**,代码可能完全没毛病。 > > **省略键与 `null` 等价,两种都可以**:对消费方而言无法区分(`jq '.checks.lint_clean'` > 在两种情况下都返回 `null`)。`null` 甚至更明确——省略是歧义的("没看"还是"看了判不出"), > `null` 明说"看了,判不出"。**别在这上面纠结,力气花在不写 `false` 上。** > > 这与「没有检查命令可跑的阶段 → `checks: {}`」同源。 ## 「跑不了」= `test-commands.yml` 过期信号(顺带检测,零额外成本) 命令跑不起来,几乎总意味着**这份 yml 已经跟不上项目了**——脚本改名、runner 换了、 工具从依赖里移除、子项目路径调整。真实项目里这类漂移是常态(例如某前端框架升级后 移除了内置 lint 子命令,而 yml 里那条命令还在)。 由于**各阶段本来就在跑这些命令**,这个信号是白捡的。检测到时: 1. 在本阶段的报告里单列一条:**「`test-commands.yml` 疑似过期」**,写明是哪一项、 观察到什么(退出码 / 报错原文)、以及为什么判定为"跑不了"而非"没通过"。 2. 提示补救:`/pdlc-test-setup --refresh`(重新探测并给出 diff)。 3. **不要自作主张改 yml**——本阶段的职责是干活,不是改配置;只报告,不动手。 ## 变更方向决定自动化程度(`--refresh` 时适用) 更新这份 yml 等于**改变"通过"的定义**,所以按**方向**区别对待: | 方向 | 例子 | 处理 | |---|---|---| | **让闸门变严** | 空着的 `e2e` 现在能跑了、覆盖率阈值上调 | **可自动应用**,报告留痕 | | **平移替换** | 命令改名但语义相同,且新命令**已验证能跑** | **可自动应用**,报告留痕 | | **让闸门变松** | 删掉某条 check、把命令改成空、下调阈值 | **必须人确认**,绝不自动 | > ⚠️ 这条方向规则是防「自动修复把闸门修没了」:lint 命令坏掉时,**把它留空**是最省事的 > "修法",结果闸门悄悄松了、报告还是绿的——比不更新更危险。 > **变严可以自动,变松必须由人签字。** <!-- @include-end templates/prompts/check-commands.md --> 4. **不管自动与否,全部变更都要在报告里列出**:改了什么、为什么、依据是哪次真跑的退出码。 自动应用的也要能一眼看出来,便于事后 `git diff` 复核。 > ⚠️ **最危险的"自动修复"是把坏掉的 check 留空**——闸门瞬间松了,报告还是绿的。 > 所以留空 / 删除 / 降阈值 / **替换**一律走人工确认,`--autonomous` 也不豁免。 > > **替换也算**,哪怕换上去的命令能跑、看起来更严:原命令的规则往往已不可考,没法证明新命令与它等价—— > 新命令可能在这一维更严、在另一维更松。**方向不可判,就不能当作变严自动应用**(与三态「查不了 ≠ 没问题」 > 同一条纪律)。能自动应用的只有一种:原来**留空**的项,这次探测到命令并真跑验证通过——那是从「没测」到 > 「测了」,方向确定。 **从哪来的过期信号**:不用你盯着——`pdlc-tdd` / `pdlc-implement` / `pdlc-review` 每次跑 check 时遇到"命令跑不了"都会提示,`/pdlc-quality` 的报告里还有专门的「配置健康度」一节。 看到提示再来 `--refresh` 即可。 ## 段一:探测与验证 ### 1.1 技术栈探测 扫描特征文件,识别语言 / 包管理器 / 测试框架: | 特征文件 | 栈 | 典型 unit | 典型 coverage | 典型 lint | |---|---|---|---|---| | `Cargo.toml` | Rust | `cargo test` | `cargo llvm-cov --fail-under-lines <阈值>` | `cargo clippy -- -D warnings` | | `package.json` | Node | `pnpm test` / `npm test` | `vitest run --coverage.thresholds.lines=<阈值>` | `npx eslint .` | | `pyproject.toml` / `requirements.txt` | Python | `pytest` | `pytest --cov --cov-fail-under=<阈值>` | `ruff check .` | | `go.mod` | Go | `go test ./...` | `go test ./... -cover` | `golangci-lint run` | | `pom.xml` / `build.gradle` | JVM | `mvn test` / `./gradlew test` | jacoco check | `mvn checkstyle:check` | | 仅 `*.sh` | Shell | 项目自有测试脚本 | —(通常无) | `shellcheck <文件>` | **多语言 / monorepo**:逐个子项目探测;`test-commands.yml` 只能有一组命令,所以要么用能覆盖全仓的聚合命令 (如 `pnpm -r test`),要么与用户确认以哪个子项目为准。**探测不到唯一答案时不要自己拍板**(见 §1.3)。 ### 1.2 逐条验证(不可跳过) 对每个候选命令**真的跑一次**,按退出码归类: | 观察到的结果 | 结论 | 动作 | |---|---|---| | 退出码 0 | 命令可用且当前通过 | **采纳** | | 退出码非 0、非 127,且输出像测试/lint 报告 | 命令可用,只是当前有失败项 | **采纳**(地基是"命令能跑",不是"当前全绿") | | 退出码 127 / `command not found` / 工具未安装 | 命令不可用 | **留空**,在报告里写明缺什么 | | 无对应配置(如没配覆盖率工具) | 该项本项目暂无 | **留空** | | 命令挂起 / 需要交互 | 不适合做自动 check | **留空**,报告里说明 | > ⚠️ **留空是合法且诚实的结果**,与状态机里「没有检查命令可跑的阶段 → `checks: {}` 留空」同一条纪律。 > 宁可空着并在报告里提示怎么补,也不要写一条没验证过的命令。 **覆盖率达标线写死在命令参数里**(如 `--cov-fail-under=85`),不做二次解释——这样"达标"就是退出码本身, 不需要任何一方去解析百分比数字。默认阈值 **85%**;项目已有更高要求则沿用已有。 ### 1.3 需要人拍板的点(`--autonomous` 下 block,不猜) 以下属判断题而非流程题,**不得自动选**,须写明原因交还人类: - 探测到**多个**并列候选(如同时有 `jest` 和 `vitest` 配置),无法判定以哪个为准 - **零候选**(项目还没有任何测试框架)——装哪个框架是技术选型,必须人定 - monorepo 里以哪个子项目 / 哪条聚合命令为准 - 覆盖率阈值定多少(若项目无既有约定) 探测到**唯一**候选且验证通过 → 属流程性确认,`--autonomous` 下自动采纳并在报告里留痕。 ## 段二:落地 ### 2.1 写 `docs/00_standards/test-commands.yml` 以 本 skill 目录下的 `assets/test-commands-template.yml` 为骨架。**这是 surface 型产物**——就地编辑,不做 `-v2` 累积。 - **文件已存在** → **不覆盖**。改为逐条校验现有命令是否仍能跑: - 仍能跑 → 保持原样(用户的选择优先于探测结果) - 已跑不通(工具改名 / 脚本删了)→ 报告里列出,**建议**改法,等人确认。`--autonomous` 下同样**不改原值**—— 建议的替换命令哪怕已真跑通过,也只写进报告的「⚠️ 待人工」,yml 里保持原样 - 缺失的项(空字符串)→ 若这次探测到可用命令,提议补上 - **文件不存在** → 用本次验证通过的命令生成;未验证通过的项留空字符串。 ### 2.2 脚手架测试目录(已有则不动) 按栈惯例建空目录 + 一个占位说明,**不生成业务测试用例**: - Rust `tests/`、Node `src/__tests__/` 或 `tests/`、Python `tests/`、Go 同包 `*_test.go`、JVM `src/test/java/` - **跟随项目既有布局**,不新造平行目录。守卫侧(`/pdlc-tdd`、`/pdlc-implement`)的测试定位规则 是布局无关的,所以这里不必迁就任何预设结构 ### 2.3 接本地钩子(不进 CI) 在**本地** git 钩子里跑基础 check(`husky` / `lefthook` / `pre-commit` / 原生 `.git/hooks`,按项目已有的来): - **pre-commit**:`lint`(快,秒级) - **pre-push**:`unit`(+ `coverage` 若已配) > **不新建 CI workflow**:这些 check 本地秒级可得,放 CI 只会让每次迭代都烧配额。 > 已有 CI 的项目也不改它的触发条件——那需要项目所有者单独授权。 ### 2.4 老项目:可选的轻量底线回填 仅当用户要求:为**当前覆盖率最低**的若干核心模块补特征化测试(characterization test,锁住现有行为), 把覆盖率抬到阈值线。**这不是补齐测试**,只是让地基能立住。深度用例仍走 `/pdlc-tdd`。 ## 段三:自检(强制) <!-- @include templates/prompts/self-audit.md(已内联于下方,无需另读) --> ## 段二:自检(强制) 重新阅读本次产出物,按质量关卡清单逐项检查。勾选已通过,标注未通过原因。 > **注意**:自检清单的具体内容由各命令自行定义,本片段只规定结构。 ## 段三:修复(单次,不递归) 针对自检段标注为未通过的项: - **可自动修复**:直接修复(如补缺字段、修正格式、补齐缺失段落) - **修复后回验**:再次运行自检,确认被修复项现在通过 - **无法自动修复**:记录到自审报告,不再尝试,流程继续 ⚠️ 单次修复原则:若一轮修复后仍有项未通过,**不再递归修复**,防止死循环。 <!-- @include-end templates/prompts/self-audit.md --> ### 自检清单(必须全部检查) - [ ] `test-commands.yml` 里**每一条非空命令**,都在本次会话中被真跑过、看到过退出码 - [ ] **收尾复跑一遍**:从写好的 yml 里逐条读命令再跑一次,确认与写入时的结论一致(防止写错路径 / 引号) - [ ] 留空的项,报告里都写明了「为什么空」和「怎么补」 - [ ] 覆盖率阈值已写死在命令参数里,不依赖任何一方解析百分比 - [ ] 测试目录跟随项目既有布局;若写了 `test-commands.yml`,其 `unit` 命令能定位到这些测试 - [ ] 钩子是**本地**的,没有新建或修改任何 CI workflow - [ ] 已存在的 `test-commands.yml` 没有被静默覆盖 - [ ] (`--refresh` 时)所有变更已在报告里列出;**没有任何"让闸门变松"的改动被自动应用** - [ ] (`--refresh` 时)已存在但跑不通的命令**原值未动**;替换方案只写在报告里等人确认,没有以「变严」为由自动替换 ## 段四:修复(单次,不递归) <!-- @include templates/prompts/loop-prevention.md(已内联于下方,无需另读) --> ## 防循环规则 本命令所有的自检-修复循环均受以下约束: 1. **单次检查**:同一个自检清单在本次命令执行中只跑一次(起始 + 修复后验证共两次读) 2. **单次修复**:发现的问题只尝试修复一轮 3. **不递归**:修复后不再重新触发自检的全量重跑 4. **失败降级**:无法自动修复的问题 → 记录到自审报告 → 流程继续 → 最终报告标注待人工处理 这是为了防止 agent 在"修完再查、查完再修"的往返中陷入死循环。 <!-- @include-end templates/prompts/loop-prevention.md --> - 复跑发现某条命令与写入时结论不一致 → 修正或改为留空 - 无法自动修复 → 记入报告,交还人类 ## 段五:交接 <!-- @include templates/prompts/handoff.md(已内联于下方,无需另读) --> ## 段四:交接(Handoff) 命令完成后必须输出以下格式的最终消息: ``` ✅ <阶段名> 完成:<主要产出物路径> 📊 自检:<通过数>/<总数> 通过(若有未通过,附要点) 📦 状态快照:docs/.pdlc-state/<feature-id>.json 👉 下一步:/pdlc-<next_step> (如果有分叉)或 /pdlc-<alt>(条件:<选择依据>) ``` **规则:** - 主流程命令(写状态机的命令;下一跳见正文里「本命令的状态机取值」)必须显式输出"下一步",不可省略 - 工具型命令(Layer 3)可以没有 `next_step`,此时输出 `👉 下一步:(本次流程结束,无后续)` - 分叉场景必须说明**选择条件**,例如"若需补充测试用例 → `/pdlc-tdd`;若测试已齐 → `/pdlc-review`" <!-- @include-end templates/prompts/handoff.md --> **本命令的 handoff 输出:** ``` ✅ 测试地基已立:docs/00_standards/test-commands.yml unit : <命令> (退出码 <N>,已验证) coverage : <命令 | 留空> (<验证结论 | 为什么空>) lint : <命令> (退出码 <N>,已验证) e2e : <命令 | 留空> (<验证结论 | 为什么空>) 🪝 本地钩子:pre-commit → lint · pre-push → unit 📁 测试目录:<路径列表> ⚠️ 待人工:<留空项怎么补 / 需要拍板的选型> 👉 下一步:/pdlc-tdd <功能描述> —— 本命令只立地基,深度用例走 TDD ``` ## 诚实边界(务必如实说明,不要夸大) - 本命令**只立地基 + 可选补底线**,**不生成完整测试套件**。AI 生成的测试容易浅、容易只测 happy path, 真正的用例设计仍走 `/pdlc-tdd`(测试先行、红灯门)。 - 留空的项就是**当前没有**,不要为了让输出好看而填一条没验证过的命令。 - 覆盖率阈值只是一条线,**过线不等于测得好**——它挡的是"几乎没测",不保证用例有效。 --- **目标项目**: $ARGUMENTS
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.