/pdlc-test-setup
立测试地基(探测技术栈 → 验证并生成 test-commands.yml → 脚手架测试目录 → 接本地钩子)
Claude Code
$ npx -y skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup --agent claude-code
Codex
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a codex -y
Cursor
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a cursor -y
Gemini CLI
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a gemini-cli -y
GitHub Copilot
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a github-copilot -y
OpenCode
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a opencode -y
Windsurf
$ npx skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup -a windsurf -y
SKILL.md
pdlc-test-setup.SKILL.mdname: 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-tool.md(已内联于下方,无需另读) --> ⛔ **IRON LAW · 不可违反的硬门禁(工具型命令)**
本命令不是主链路上的阶段:**不追加 `history`,不改 `current_stage` / `next_step` / `last_phase_result`**,也不为此分配功能ID。以下规则不可协商:
1. **文件必须落盘**:正文要求创建的文件必须作为实际文件写入磁盘,不可仅在对话中输出(只要求在对话里输出的,照正文办)。 2. **自检必须执行**:产出后按本命令的自检项逐项核对,不得以"已经很好了"为由跳过。 3. **防循环**:修复只做一次,不递归。修不了的问题记进报告,继续往下走。
**违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。** <!-- @include-end templates/prompts/iron-law-tool.md --> <!-- @include templates/prompts/noninteractive.md(已内联于下方,无需另读) -->
非交互模式(`--autonomous`)
若本命令的参数含 `--autonomous`,本命令进入**无人值守**模式,按以下规则处理原本需要人应答的交互点。**参数是唯一真源**:不带 `--autonomous` 即为交互模式,一切照旧正常询问用户;绝不回读状态机 `run_mode` 兜底(「掉出 autonomous」是安全的失败方向)。
1. **流程性确认**(如「测试已绿是否继续」「是否覆盖已有文件」)→ **不询问**,按预设默认前进,并把决策追加到状态机 `history[].auto_decisions[]` (写状态机的命令:推进时给状态推进脚本传 `--decision "<确认点描述>=<所选默认>"`,可重复;脚本写成下面的样子):
{ "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" }2. **真需人判断**(PRD 关键取舍、评审「需人工确认」项、真实循环依赖、**测试与设计 / PRD 矛盾**等无法安全默认的点)→ **不猜**。 改测试、改检查命令让它变绿,永远不是「流程性默认」,不能记成 `--decision`:
- `current_stage` 保持不变(不推进)
- 写 `last_phase_result.ok = false` 且 `blocked_reason = "<原因>"`(写状态机的命令:给状态推进脚本传 `--blocked "<原因>"`)
- 末行输出哨兵:`<<<PDLC blocked reason="<原因>">>>`
- 立即结束命令,交还人类
- **守卫拦截也走这一条**:前置守卫(缺设计文档、缺测试、测试没红等)在 `--autonomous` 下拦住本命令时,
不能只输出 ⛔ 就停——同样写 `ok=false` + `blocked_reason`(写明哪条守卫、缺什么)并输出哨兵。 否则状态机里仍是上一阶段的 `ok=true`,循环驱动只看到 `current_stage` 没动,会把「被拦」误报成「卡住」,真实原因丢失。 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 命令:退出码的三态语义
**用脚本跑,不要手写 `checks`**:
bash scripts/pdlc-checks.sh --only <本阶段要的项,如 unit,lint> <项目根>
脚本是本 skill 自带的 `scripts/pdlc-checks.sh`。它读 `docs/00_standards/test-commands.yml`(唯一真源),逐条真跑,按下表映射,**stdout 只有一行 JSON**—— **原样**写进 `last_phase_result.checks`,不改键名、不改值、不补不删。每条命令的退出码与输出末尾回显在 stderr,报告里引用那几行即可。脚本退出码 `2`(没有 yml 等)= 没有可跑的 check → `checks: {}`。
> 为什么不手写:键名、布尔类型、三态这几件事是确定性的,交给模型写,真机上同一份项目轮与轮之间就换一种错法 > (键名照抄 yml 的 `unit`、值写成 `"4 passed, 1 failed"`、`127` 写成 `false`)。 > 环境里没有 `bash` 时才按下表手工映射。
脚本按退出码分三态——不是两态。这是 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 | `py
Read more
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-tool.md(已内联于下方,无需另读) --> ⛔ **IRON LAW · 不可违反的硬门禁(工具型命令)**
本命令不是主链路上的阶段:**不追加 `history`,不改 `current_stage` / `next_step` / `last_phase_result`**,也不为此分配功能ID。以下规则不可协商:
1. **文件必须落盘**:正文要求创建的文件必须作为实际文件写入磁盘,不可仅在对话中输出(只要求在对话里输出的,照正文办)。 2. **自检必须执行**:产出后按本命令的自检项逐项核对,不得以"已经很好了"为由跳过。 3. **防循环**:修复只做一次,不递归。修不了的问题记进报告,继续往下走。
**违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。** <!-- @include-end templates/prompts/iron-law-tool.md --> <!-- @include templates/prompts/noninteractive.md(已内联于下方,无需另读) -->
非交互模式(`--autonomous`)
若本命令的参数含 `--autonomous`,本命令进入**无人值守**模式,按以下规则处理原本需要人应答的交互点。**参数是唯一真源**:不带 `--autonomous` 即为交互模式,一切照旧正常询问用户;绝不回读状态机 `run_mode` 兜底(「掉出 autonomous」是安全的失败方向)。
1. **流程性确认**(如「测试已绿是否继续」「是否覆盖已有文件」)→ **不询问**,按预设默认前进,并把决策追加到状态机 `history[].auto_decisions[]` (写状态机的命令:推进时给状态推进脚本传 `--decision "<确认点描述>=<所选默认>"`,可重复;脚本写成下面的样子):
{ "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" }2. **真需人判断**(PRD 关键取舍、评审「需人工确认」项、真实循环依赖、**测试与设计 / PRD 矛盾**等无法安全默认的点)→ **不猜**。 改测试、改检查命令让它变绿,永远不是「流程性默认」,不能记成 `--decision`:
- `current_stage` 保持不变(不推进)
- 写 `last_phase_result.ok = false` 且 `blocked_reason = "<原因>"`(写状态机的命令:给状态推进脚本传 `--blocked "<原因>"`)
- 末行输出哨兵:`<<<PDLC blocked reason="<原因>">>>`
- 立即结束命令,交还人类
- **守卫拦截也走这一条**:前置守卫(缺设计文档、缺测试、测试没红等)在 `--autonomous` 下拦住本命令时,
不能只输出 ⛔ 就停——同样写 `ok=false` + `blocked_reason`(写明哪条守卫、缺什么)并输出哨兵。 否则状态机里仍是上一阶段的 `ok=true`,循环驱动只看到 `current_stage` 没动,会把「被拦」误报成「卡住」,真实原因丢失。 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 命令:退出码的三态语义
**用脚本跑,不要手写 `checks`**:
bash scripts/pdlc-checks.sh --only <本阶段要的项,如 unit,lint> <项目根>
脚本是本 skill 自带的 `scripts/pdlc-checks.sh`。它读 `docs/00_standards/test-commands.yml`(唯一真源),逐条真跑,按下表映射,**stdout 只有一行 JSON**—— **原样**写进 `last_phase_result.checks`,不改键名、不改值、不补不删。每条命令的退出码与输出末尾回显在 stderr,报告里引用那几行即可。脚本退出码 `2`(没有 yml 等)= 没有可跑的 check → `checks: {}`。
> 为什么不手写:键名、布尔类型、三态这几件事是确定性的,交给模型写,真机上同一份项目轮与轮之间就换一种错法 > (键名照抄 yml 的 `unit`、值写成 `"4 passed, 1 failed"`、`127` 写成 `false`)。 > 环境里没有 `bash` 时才按下表手工映射。
脚本按退出码分三态——不是两态。这是 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 | `py
Author: kanfu-panda Repo: github.com/kanfu-panda/pdlc-skills License: MIT pdlc-skills is built on one rule: an AI saying "done" doesn't count — exit codes do.
Other skills on pdlc.
pdlc-adopt
已有代码、还没有 PDLC 文档的老项目接入——只读扫描后逆向生成基线文档并做健康检查,不改代码;空目录新项目用 /pdlc-bootstrap
pdlc-bootstrap
空目录从一句话描述初始化新项目(服务 / 应用拆分、技术栈、目录骨架与 PDLC 文档目录);已有代码的项目用 /pdlc-adopt
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about pdlc-test-setup.
- Which coding agents can use pdlc-test-setup?
- pdlc-test-setup installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

