Claude Skill

pdlc-test-setup

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

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download kanfu-panda-pdlc-skills-skills_pdlc-test-setup-3cd2f02.zip · 9 KB
Part of kanfu-panda/pdlc-skills — 36 skills

Install

skills CLI npx skills add https://github.com/kanfu-panda/pdlc-skills/tree/main/skills/pdlc-test-setup
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kanfu-panda-pdlc-skills@llmmart
Git 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 · 不可违反的硬门禁

以下规则为不可协商的执行约束:

  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

违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。

非交互模式(--autonomous

若本命令的参数含 --autonomous,本命令进入无人值守模式,按以下规则处理原本需要人应答的交互点。参数是唯一真源:不带 --autonomous 即为交互模式,一切照旧正常询问用户;绝不回读状态机 run_mode 兜底(「掉出 autonomous」是安全的失败方向)。

  1. 流程性确认(如「测试已绿是否继续」「是否覆盖已有文件」)→ 不询问,按预设默认前进,并把决策追加到状态机 history[].auto_decisions[]
    { "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" }
    
  2. 真需人判断(PRD 关键取舍、评审「需人工确认」项、真实循环依赖等无法安全默认的点)→ 不猜
    • current_stage 保持不变(不推进)
    • last_phase_result.ok = falseblocked_reason = "<原因>"
    • 末行输出哨兵:<<<PDLC blocked reason="<原因>">>>
    • 立即结束命令,交还人类
  3. 破坏性操作(发布 / 部署 / 打 tag / 触发 CI / DROP / force-push 等不可逆·外发操作)→ --autonomous 无效,仍必须人工显式确认。
  4. 顺手的 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,而不是从头再来:

  1. 逐条复跑现有命令,按 check-commands.md 的三态判定谁还活着(跑不通 ≠ 检查没过)。
  2. 重新探测候选,与现状对比,得出变更清单。
  3. 按方向决定自不自动(这条是安全底线):

跑 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 命令坏掉时,把它留空是最省事的 "修法",结果闸门悄悄松了、报告还是绿的——比不更新更危险。 变严可以自动,变松必须由人签字。

  1. 不管自动与否,全部变更都要在报告里列出:改了什么、为什么、依据是哪次真跑的退出码。 自动应用的也要能一眼看出来,便于事后 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,不猜)

以下属判断题而非流程题,不得自动选,须写明原因交还人类:

  • 探测到多个并列候选(如同时有 jestvitest 配置),无法判定以哪个为准
  • 零候选(项目还没有任何测试框架)——装哪个框架是技术选型,必须人定
  • 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-commitlint(快,秒级)
  • pre-pushunit(+ 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 时)已存在但跑不通的命令原值未动;替换方案只写在报告里等人确认,没有以「变严」为由自动替换

段四:修复(单次,不递归)

防循环规则

本命令所有的自检-修复循环均受以下约束:

  1. 单次检查:同一个自检清单在本次命令执行中只跑一次(起始 + 修复后验证共两次读)
  2. 单次修复:发现的问题只尝试修复一轮
  3. 不递归:修复后不再重新触发自检的全量重跑
  4. 失败降级:无法自动修复的问题 → 记录到自审报告 → 流程继续 → 最终报告标注待人工处理

这是为了防止 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.

No comments yet.

Reviews (0)

No reviews yet.

Related