Skip to content
Back to skills

Pdlc Test Setup

ASecurity

立测试地基(探测技术栈 → 验证并生成 test-commands.yml → 脚手架测试目录 → 接本地钩子)

  • 16 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 6, 2026
ai-agentspythonrustgojavashellbashnodegit

Works with

  • terminal
  • cli

Security analysis

A100/100

Scanned September 25, 2026

npx -y skills add kanfu-panda/pdlc-skills --skill pdlc-test-setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pdlc Test Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pdlc Test Setup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kanfu-panda-pdlc-test-setup/badge)](https://www.skillsdirectory.com/skills/kanfu-panda-pdlc-test-setup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.