Claude Skill

pdlc-quality

质量闸门——跑真实 check、对照质量目标、出可核对报告,由人签字放行

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-quality-3cd2f02.zip · 20 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-quality
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 → 对照质量目标 → 出可核对的报告 → 人签字放行

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 循环产出)。

这个命令的立身之本

一切判定来自客观数据:覆盖率数字来自覆盖率工具、E2E 覆盖来自 flow→test 映射 + 真跑结果、lint 来自退出码。 AI 只负责把这些数据整理成报告,不参与"达标与否"的判定,放行由人。

⛔ 绝不允许出现的行为:用"我看了一下代码,测试挺全的"这类判断替代真实数据; 用上一次的结果冒充本次;命令没跑通却按通过处理;覆盖率没测量却写一个数字。 量不到就如实写"未测量"——这与状态机里「无命令可跑 → checks: {}」是同一条纪律。

前置:两份真源

文件 作用 缺失时
docs/00_standards/test-commands.yml 怎么量(命令) 中止,提示先跑 /pdlc-test-setup
docs/00_standards/quality-targets.yml 量到多少算达标 --init 交互创建(见下)
docs/00_standards/e2e-flow-map.yml 核心流 → E2E 测试 的映射 若 targets 里声明了 core_flows必须有,否则 E2E 判定无法机械化

--init:首次建立目标声明

  1. 读 本 skill 目录下的 assets/quality-targets-template.yml 作骨架。
  2. 从 PRD 自动抽 core_flows 草稿:扫 docs/01_requirements/prd/,提取标记为 P0 / P1 的功能流程, 生成候选清单(含来源 PRD 路径)供人确认——降低首次声明的摩擦,但最终清单必须人确认,不自动落盘。
  3. 覆盖率达标线:默认与 test-commands.yml 的 coverage 命令参数对齐;两者不一致要提示人对齐 (以命令参数为准——那才是真正的强制点)。
  4. 同时生成 e2e-flow-map.yml 骨架(每条 flow 一个空 tests 列表待填)。

段一:跑真实 check

test-commands.yml 逐条真跑 coverage / e2e / lint,记录命令原文 + 退出码 + 关键输出。 退出码的三态语义、以及「命令跑不了 = yml 过期信号」按下面的规则处理:

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

补充两条本命令特有的:

  • 命令为空字符串(项目未配置该项)→ 如实记为「留空,未测量」,不得因此判为通过
  • 覆盖率数字从工具输出中摘取原文,不重新计算、不四舍五入到好看的数

段二:三项机械核对

2.1 覆盖率

拿实测数字对 quality-targets.yml 的达标线。真正的强制点是命令参数里的阈值(如 --cov-fail-under=85)—— 退出码就是判定;yml 里的数字用于报告展示与趋势。两处不一致 → 报告里提示对齐。

2.2 E2E 覆盖矩阵(B2 的第一个地基)

对每条 core_flow,按 e2e-flow-map.yml 找到映射的测试标识,再到本次真跑的 E2E 结果里核对:

情况 判定
映射存在 且 对应测试本次通过
映射存在 但 测试本次失败
映射存在 但 该测试在本次结果里找不到 映射腐烂(指向了不存在的测试)
core_flow 在映射文件里没有条目 ❌ 缺映射
映射里有 core_flows 中不存在的 id ⚠️ 黄:孤儿映射,建议清理

缺一条 = 红。 绝不用"我觉得这条流程被别的测试覆盖了"来补空缺——那正是要消灭的主观判断。

2.3 配置健康度(顺带检测,零额外成本)

段一已经把每条命令真跑了一遍,过期信号是白捡的。汇总成报告的一节:

状态 判据 报告里怎么写
✅ 健康 命令跑起来了(退出码 0 或非 0 皆可) 正常
⚠️ 已失效 127 / command not found / 脚本不存在 <项> 的命令已跑不通——yml 过期」+ 报错原文
💡 可收紧 该项当前留空,但本次探测到已可用的命令 <项> 现在可以填了:<候选命令>(已验证退出码 <N>)」
— 留空 该项留空且确无可用命令 「留空,未测量」(不得判为通过)

本节只报告、不改 yml——改配置等于改"通过"的定义,要走 /pdlc-test-setup --refresh(严向自动、松向人确认)。

2.4 PRD ↔ core_flows 对账(B2 的第二个地基,防 false-green)⭐

清单靠"有人记得改"维护必然腐烂;腐烂的清单产出 false-green——新增的核心流没进清单,矩阵照样全绿, 把"我们不知道"伪装成"我们覆盖了",比没有闸门更坏。所以每次运行都强制对账:

  1. docs/01_requirements/prd/ 所有 PRD,提取 P0 / P1 流程。
  2. quality-targets.ymlcore_flows 做 diff。
  3. 漂移即红灯,不是温柔提示:
    • PRD 有、core_flows 无 → ❌「PRD 流程 X 未进 core_flows」
    • core_flows 有、映射无 → ❌「core_flow Y 尚无映射的 E2E」
    • 映射有、core_flows 无 → ⚠️ 孤儿映射

"不可判"绝不能被当成"没问题"(对账自身的 false-green,真项目上实测踩到过): 若某份 PRD 不含任何 P0/P1 标记,它提取出的就是空集,于是不产生任何漂移条目—— 报告若就此显示"对账通过",等于宣称"这份 PRD 里的流程都覆盖了",而事实是它整份都没进闸门视野。 已上线的主链路最容易栽在这里(老 PRD 常只写"已上线/待开发",不标优先级)。

规则:统计"因无优先级标记而未参与对账"的 PRD,在报告里单列告警,并且 对账项不得判为 ✅——写成 ⚠️ 无漂移,但另有 N 份 PRD 不可判。 处理建议:给这些 PRD 补优先级标记,或在 quality-targets.yml 里显式声明豁免(写明理由)。

这样清单维护就从"靠自觉"变成被产物纪律接管——PRD 本就被 pdlc 逼着落盘并保持最新, 让它当 core_flows 的唯一上游真源,与「状态外化到磁盘」是同一个哲学。

段三:出报告(真源 .md + 视图 .html

① 先出 .md(真源):按 本 skill 目录下的 assets/quality-report-template.md 生成 docs/07_reviews/quality/<YYYY-MM-DD>.mdledger 型:一次一份,可 git diff、可看趋势; 同日重跑则覆盖当日文件)。必须包含:

  1. 结论红绿表 2. 实测证据(命令 + 退出码 + 关键输出) 3. E2E 覆盖矩阵
  2. 配置健康度(§2.3) 5. PRD 对账结果 6. 趋势(首次则写「无趋势基线」) 7. 人工确认签字栏

② 再出 .html(视图):按 本 skill 目录下的 assets/quality-report-template.html 生成同目录同名 <YYYY-MM-DD>.html——给人看的那一份,可直接双击打开、可打印签字、可发给同事。

.md 是唯一真源/pdlc-ship 的发布闸门读它、git diff 看它、趋势对比取它。 .html 只是同一份数据的另一种呈现,没有任何独立信息

铁律:HTML 里的每个数字都从 .md 抄,不得重新计算、不得另行判断。 双写最容易出的错就是「两份报告各说各话」——那比没有 HTML 更糟:读者不知道该信哪份, 而错的那份通常更好看。两处不一致时一律以 .md 为准并改正 .html

填写要求:只替换 {{...}} 占位符与各表格的 <tbody> 内容;不要改 <style> (排版由模板负责,改了就失去一致性);删掉模板顶部的「填写说明」注释块; 保持自包含——不得引入任何外部 URL(css / js / 字体 / 图片一律不许外链: 报告含项目内部数据,一张远程图片就够把打开时间、IP、referer 送给第三方, 而且离线打不开)。确需图片内联成 data: URI。

三态 class 必须对号入座:st-pass 达标 / st-fail 未达标 / st-warn 无法判定 / st-na 留空未测量。st-warn 绝不能写成 st-pass——这是 §6.5 那条反模式 (「把我判断不了当成没问题」)在报告上的最后一道落地面。页头的 {{VERDICT_SHORT}} 只填裸词 pass / fail / warn,一处占位符同时驱动顶条 与徽章,顶条不会和总判定说两套话。

段四:自检(强制)

段二:自检(强制)

重新阅读本次产出物,按质量关卡清单逐项检查。勾选已通过,标注未通过原因。

注意:自检清单的具体内容由各命令自行定义,本片段只规定结构。

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

针对自检段标注为未通过的项:

  • 可自动修复:直接修复(如补缺字段、修正格式、补齐缺失段落)
  • 修复后回验:再次运行自检,确认被修复项现在通过
  • 无法自动修复:记录到自审报告,不再尝试,流程继续

⚠️ 单次修复原则:若一轮修复后仍有项未通过,不再递归修复,防止死循环。

  • 报告里每一条判定,都能追到本次真跑的退出码 / 工具输出 / 映射核对结果
  • 没有任何一项是靠"读代码觉得"得出的
  • 留空 / 无法执行的项,如实标注且未按通过处理
  • 覆盖率数字是从工具输出摘的原文
  • E2E 矩阵里每条 core_flow 都有明确判定(含"映射腐烂"这种红)
  • PRD 对账已执行,漂移项按红灯列出
  • 配置健康度已汇报:失效项 / 可收紧项都列了出来,且没有擅自改动 yml
  • 报告落盘到 docs/07_reviews/quality/,含生成时间与 commit SHA
  • 未在报告里替人做 go/no-go 决定
  • .html 已同步产出,且与 .md 逐项核对过关键数字(覆盖率、核心流 M/N、各 check 退出码、不可判份数、总判定)——不一致以 .md 为准并已改正
  • .html 里无残留 {{ 占位符、无任何外部 URL(含图片)、<style> 未被改动
  • 「无法判定」的项在 HTML 里用的是 st-warn 而非 st-pass;总判定未达标时页头顶条颜色已同步

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

防循环规则

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

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

这是为了防止 agent 在"修完再查、查完再修"的往返中陷入死循环。

可自动修复的(如 lint 可自动修的告警)→ 修完重跑该 check 并以重跑结果为准,报告里注明"已自动修复后重测"。 不可自动修复 → 如实留红,写进报告。

怎么"自动化运行"(不依赖 CI)

  • pre-push 钩子:push 前本地跑一遍质量闸,不达标就拦或告警——日常自动且零 CI 成本
  • 按需:随时 /pdlc-quality 出全量报告
  • 发布挂钩/pdlc-ship 会读最近一份质量报告,未达标不让发(除非人显式 override 并写明理由)
  • 要"每天一份":用本机 launchd / cron 跑,报告进 git;绝不用 GitHub Actions schedule

段六:交接

段四:交接(Handoff)

命令完成后必须输出以下格式的最终消息:

✅ <阶段名> 完成:<主要产出物路径>
📊 自检:<通过数>/<总数> 通过(若有未通过,附要点)
📦 状态快照:docs/.pdlc-state/<feature-id>.json
👉 下一步:/pdlc-<next_step>
   (如果有分叉)或 /pdlc-<alt>(条件:<选择依据>)

规则:

  • 主流程命令(写状态机的命令;下一跳见正文里「本命令的状态机取值」)必须显式输出"下一步",不可省略
  • 工具型命令(Layer 3)可以没有 next_step,此时输出 👉 下一步:(本次流程结束,无后续)
  • 分叉场景必须说明选择条件,例如"若需补充测试用例 → /pdlc-tdd;若测试已齐 → /pdlc-review"

本命令的 handoff 输出:

📊 质量报告:docs/07_reviews/quality/<YYYY-MM-DD>.md
  覆盖率      : <实测> / 目标 <目标>   ✅|❌
  E2E 核心流  : <M>/<N> 条已覆盖        ✅|❌
  Lint        : 退出码 <N>             ✅|❌
  PRD 对账    : <无漂移 | N 项漂移 | 另有 N 份不可判>  ✅|⚠️|❌
  配置健康度  : <全部健康 | N 项已失效 | N 项可收紧>
🧾 总判定:<达标 | 未达标>
✍️ 待人工签字:报告最后一节(go/no-go 由你拍,本命令不代劳)
👉 未达标项处理:<每条给出具体下一步>

诚实边界

  • 达标不等于质量好:覆盖率线挡的是"几乎没测",不保证用例有效;矩阵证明"每条核心流有测试跑过", 不证明"测得对"。报告要如实呈现这层含义,不要把"全绿"说成"质量有保障"。
  • 保障强度取决于 core_flows 维护得多勤——这正是 §2.4 强制对账存在的原因,但对账只能发现 "PRD 里有而清单里没有",PRD 本身漏掉的核心流谁也发现不了。这条限制要让用户知道。

参数: $ARGUMENTS

Files (pdlc-skills)
  • assets
    • quality-report-template.html 17.9 KB · in bundle
    • quality-report-template.md 4 KB
      <!-- PDLC-TRACE -->
      <!-- 产物类型: 质量报告 -->
      <!-- 生成命令: /pdlc-quality -->
      <!-- 生成时间: <ISO 8601> -->
      <!-- 仓库版本: <commit SHA> -->
      
      # 质量报告 · <YYYY-MM-DD>
      
      > 本报告的每一个判定都来自**客观数据**——真实退出码、覆盖率工具的真实数字、真跑的 E2E 结果。
      > AI 只负责把这些数据整理成报告,**不参与达标与否的判定**,放行由人。
      
      ## 1. 结论
      
      | 项 | 目标 | 实测 | 判定 |
      |---|---|---|---|
      | 单测覆盖率 | `<目标>` | `<实测数字>` | ✅ / ❌ |
      | E2E 核心流覆盖 | 全部 `<N>` 条 | `<已覆盖 M>` 条 | ✅ / ❌ |
      | Lint | zero-warnings | 退出码 `<N>` | ✅ / ❌ |
      | PRD ↔ core_flows 对账 | 无漂移 | `<漂移项数>`,另有 `<N>` 份 PRD 不可判 | ✅ / ⚠️ / ❌ |
      
      **总判定**:✅ 达标 / ❌ 未达标
      
      ## 2. 实测证据(命令与退出码)
      
      | check | 命令(取自 test-commands.yml) | 退出码 | 关键输出 |
      |---|---|---|---|
      | coverage | `<命令>` | `<N>` | `<覆盖率数字 / 未配置则写「留空,未测量」>` |
      | e2e | `<命令>` | `<N>` | `<通过 X / 失败 Y>` |
      | lint | `<命令>` | `<N>` | `<0 warnings / N warnings>` |
      
      > 命令为空(项目未配置该项)→ 如实写「留空,未测量」,**不得**因此判为通过。
      
      ## 3. E2E 覆盖矩阵
      
      `core_flows` × 是否有真跑且通过的 E2E。缺一条 = 红。
      
      | core_flow | 映射到的测试(e2e-flow-map.yml) | 本次结果 | 判定 |
      |---|---|---|---|
      | `<id>` | `<测试标识>` | 通过 / 失败 / **未找到** | ✅ / ❌ |
      
      **未找到**表示映射指向了本次 E2E 结果里不存在的测试——属**映射腐烂**,按红处理。
      
      ## 4. 配置健康度
      
      `test-commands.yml` 里每条命令本次的可用性(段一已真跑,顺带得出):
      
      | check | 命令 | 状态 | 说明 |
      |---|---|---|---|
      | unit / coverage / e2e / lint | `<命令原文 \| 留空>` | ✅ 健康 / ⚠️ 已失效 / 💡 可收紧 / — 留空 | `<报错原文 \| 候选命令及其退出码>` |
      
      - **⚠️ 已失效**(退出码 127 / `command not found` / 脚本不存在)= **`test-commands.yml` 已过期**,
        不是"检查没过"。别去查代码,去看配置。补救:`/pdlc-test-setup --refresh`
      - **💡 可收紧**:该项现在留空,但已探测到可用命令——补上会让闸门更严
      - 本节**只报告不改配置**:改命令等于改"通过"的定义,须走 `--refresh`(严向自动、松向人确认)
      
      无异常时写「全部健康」。
      
      ## 5. PRD ↔ core_flows 对账(防 false-green)
      
      清单腐烂会让矩阵全绿而现实有洞,所以每次都拿 PRD 的 P0/P1 流程与 `core_flows` 做 diff:
      
      | 漂移类型 | 条目 | 处理建议 |
      |---|---|---|
      | PRD 有、`core_flows` 无 | `<PRD 路径 · 流程名>` | **红灯**:补进 `core_flows` 并加映射 |
      | `core_flows` 有、映射无 | `<flow id>` | **红灯**:补 `e2e-flow-map.yml` |
      | 映射有、`core_flows` 无 | `<flow id>` | 黄:孤儿映射,建议清理 |
      | **不可判**:PRD 无 P0/P1 标记 | `<PRD 路径>` | ⚠️ 该 PRD **整份未参与对账**——补优先级标记,或在 targets 里显式豁免并写明理由 |
      
      无漂移且无不可判项时才写「无漂移 ✅」。**有不可判项时写「无漂移,但另有 N 份 PRD 不可判 ⚠️」,不得判为 ✅**——
      否则就是宣称"这些 PRD 的流程都覆盖了",而事实是它们根本没进闸门视野。
      
      ## 6. 趋势(对比上一份报告)
      
      | 指标 | 上次 `<日期>` | 本次 | 变化 |
      |---|---|---|---|
      | 覆盖率 | `<X%>` | `<Y%>` | ↑ / ↓ / — |
      | 核心流覆盖 | `<M/N>` | `<M/N>` | ↑ / ↓ / — |
      | Lint warnings | `<N>` | `<N>` | ↑ / ↓ / — |
      
      首次运行无上一份 → 写「首次运行,无趋势基线」。
      
      ## 7. 人工确认
      
      报告只陈述事实,**go/no-go 由人拍**——与「发布永远人工」一脉相承。
      
      - [ ] 我已阅读本报告,确认结论
      - 签字:`<姓名>` · 日期:`<YYYY-MM-DD>`
      - 备注 / 例外说明:`<若在未达标情况下仍决定放行,必须在此写明理由与后续计划>`
      
    • quality-targets-template.yml 1.8 KB
      # docs/00_standards/quality-targets.yml — 本项目质量目标的唯一真源
      #
      # 与 test-commands.yml 并列:那份说「怎么量」(命令),这份说「量到多少算达标」。
      # /pdlc-quality 读这两份,跑出真实数字与退出码,再出报告交人签字。
      #
      # ⚠️ 达标判定必须来自客观数据:覆盖率数字来自覆盖率工具、E2E 覆盖来自
      #    flow→test 映射 + 真跑结果、lint 来自退出码。**任何一项都不接受模型自评。**
      
      coverage:
        # 达标线。真正的强制点应写死在 test-commands.yml 的 coverage 命令参数里
        # (如 --cov-fail-under=85),那样「达标」就是退出码本身;这里的数字用于
        # 报告展示与趋势对比。两处不一致时以命令参数为准,报告会提示你对齐。
        unit: ">= 85%"
      
      e2e:
        # all-core-flows-covered:下面每条 core_flow 都必须有一个「真跑且通过」的
        # E2E 测试与之对应(对应关系见 e2e-flow-map.yml)。缺一条 = 红。
        policy: all-core-flows-covered
      
        # 核心业务流程清单——这是「覆盖全部核心流」这句话的唯一真源。
        #
        # ⚠️ 这份清单腐烂 = false-green:新增的核心流没进清单,矩阵照样全绿,
        #    把「我们不知道」伪装成「我们覆盖了」,比没有闸门更坏。
        #    所以 /pdlc-quality 每次都会拿 PRD 的 P0/P1 流程与本清单做 diff,
        #    **漂移即红灯**,不是温柔提示。清单由产物纪律接管,不靠人自觉。
        core_flows:
          - id: login
            desc: 用户登录
            prd: docs/01_requirements/prd/F20260101-000000-auth-prd.md   # 来源 PRD(可选但强烈建议)
          - id: checkout
            desc: 下单结算
          - id: refund
            desc: 退款
      
      lint:
        # zero-warnings:lint 命令退出码必须为 0
        policy: zero-warnings
      
  • SKILL.md 20.5 KB
    ---
    name: pdlc-quality
    description: 质量闸门——跑真实 check、对照质量目标、出可核对报告,由人签字放行
    argument-hint: [--init] [--autonomous]
    allowed-tools: Read, Write, Edit, Glob, Grep, Bash
    layer: 3
    stage: quality
    artifact_type: ledger
    produces:
      # 主产物(ledger 型,一次一份可看趋势);.md 是真源,.html 是同数据的可视化视图
      - docs/07_reviews/quality/<YYYY-MM-DD>.md
      - docs/07_reviews/quality/<YYYY-MM-DD>.html
      # 仅 --init 时创建(surface 型,就地编辑不累积)
      - docs/00_standards/quality-targets.yml
      - docs/00_standards/e2e-flow-map.yml
    requires:
      - docs/00_standards/test-commands.yml
    next_step: null
    terminal_state: null
    recommended_model: sonnet
    recommended_effort: medium
    ---
    
    # 质量闸门与报告
    
    跑真实 check → 对照质量目标 → 出可核对的报告 → **人签字放行**。
    
    <!-- @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 -->
    
    ## 这个命令的立身之本
    
    **一切判定来自客观数据**:覆盖率数字来自覆盖率工具、E2E 覆盖来自 flow→test 映射 + 真跑结果、lint 来自退出码。
    **AI 只负责把这些数据整理成报告,不参与"达标与否"的判定**,放行由人。
    
    > ⛔ 绝不允许出现的行为:用"我看了一下代码,测试挺全的"这类判断替代真实数据;
    > 用上一次的结果冒充本次;命令没跑通却按通过处理;覆盖率没测量却写一个数字。
    > **量不到就如实写"未测量"**——这与状态机里「无命令可跑 → `checks: {}`」是同一条纪律。
    
    ## 前置:两份真源
    
    | 文件 | 作用 | 缺失时 |
    |---|---|---|
    | `docs/00_standards/test-commands.yml` | 怎么量(命令) | **中止**,提示先跑 `/pdlc-test-setup` |
    | `docs/00_standards/quality-targets.yml` | 量到多少算达标 | 走 `--init` 交互创建(见下) |
    | `docs/00_standards/e2e-flow-map.yml` | 核心流 → E2E 测试 的映射 | 若 targets 里声明了 `core_flows` 则**必须有**,否则 E2E 判定无法机械化 |
    
    ### `--init`:首次建立目标声明
    
    1. 读 本 skill 目录下的 `assets/quality-targets-template.yml` 作骨架。
    2. **从 PRD 自动抽 `core_flows` 草稿**:扫 `docs/01_requirements/prd/`,提取标记为 **P0 / P1** 的功能流程,
       生成候选清单(含来源 PRD 路径)**供人确认**——降低首次声明的摩擦,但**最终清单必须人确认**,不自动落盘。
    3. 覆盖率达标线:默认与 `test-commands.yml` 的 coverage 命令参数对齐;两者不一致要提示人对齐
       (**以命令参数为准**——那才是真正的强制点)。
    4. 同时生成 `e2e-flow-map.yml` 骨架(每条 flow 一个空 `tests` 列表待填)。
    
    ## 段一:跑真实 check
    
    按 `test-commands.yml` 逐条真跑 `coverage` / `e2e` / `lint`,记录**命令原文 + 退出码 + 关键输出**。
    退出码的三态语义、以及「命令跑不了 = yml 过期信号」按下面的规则处理:
    
    <!-- @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 -->
    
    补充两条本命令特有的:
    
    - 命令为空字符串(项目未配置该项)→ 如实记为「留空,未测量」,**不得因此判为通过**
    - 覆盖率数字从工具输出中**摘取原文**,不重新计算、不四舍五入到好看的数
    
    ## 段二:三项机械核对
    
    ### 2.1 覆盖率
    
    拿实测数字对 `quality-targets.yml` 的达标线。真正的强制点是命令参数里的阈值(如 `--cov-fail-under=85`)——
    **退出码就是判定**;yml 里的数字用于报告展示与趋势。两处不一致 → 报告里提示对齐。
    
    ### 2.2 E2E 覆盖矩阵(B2 的第一个地基)
    
    对每条 `core_flow`,按 `e2e-flow-map.yml` 找到映射的测试标识,再到**本次真跑的 E2E 结果**里核对:
    
    | 情况 | 判定 |
    |---|---|
    | 映射存在 且 对应测试本次通过 | ✅ |
    | 映射存在 但 测试本次失败 | ❌ |
    | 映射存在 但 该测试在本次结果里**找不到** | ❌ **映射腐烂**(指向了不存在的测试) |
    | `core_flow` 在映射文件里**没有条目** | ❌ 缺映射 |
    | 映射里有 `core_flows` 中不存在的 id | ⚠️ 黄:孤儿映射,建议清理 |
    
    **缺一条 = 红。** 绝不用"我觉得这条流程被别的测试覆盖了"来补空缺——那正是要消灭的主观判断。
    
    ### 2.3 配置健康度(顺带检测,零额外成本)
    
    段一已经把每条命令真跑了一遍,**过期信号是白捡的**。汇总成报告的一节:
    
    | 状态 | 判据 | 报告里怎么写 |
    |---|---|---|
    | ✅ 健康 | 命令跑起来了(退出码 0 或非 0 皆可) | 正常 |
    | ⚠️ **已失效** | 127 / `command not found` / 脚本不存在 | 「`<项>` 的命令已跑不通——yml 过期」+ 报错原文 |
    | 💡 **可收紧** | 该项当前留空,但本次探测到**已可用**的命令 | 「`<项>` 现在可以填了:`<候选命令>`(已验证退出码 `<N>`)」 |
    | — 留空 | 该项留空且确无可用命令 | 「留空,未测量」(不得判为通过) |
    
    **本节只报告、不改 yml**——改配置等于改"通过"的定义,要走 `/pdlc-test-setup --refresh`(严向自动、松向人确认)。
    
    ### 2.4 PRD ↔ core_flows 对账(B2 的第二个地基,防 false-green)⭐
    
    清单靠"有人记得改"维护必然腐烂;**腐烂的清单产出 false-green**——新增的核心流没进清单,矩阵照样全绿,
    把"我们不知道"伪装成"我们覆盖了",比没有闸门更坏。所以每次运行都强制对账:
    
    1. 扫 `docs/01_requirements/prd/` 所有 PRD,提取 **P0 / P1** 流程。
    2. 与 `quality-targets.yml` 的 `core_flows` 做 diff。
    3. **漂移即红灯**,不是温柔提示:
       - PRD 有、`core_flows` 无 → ❌「PRD 流程 X 未进 core_flows」
       - `core_flows` 有、映射无 → ❌「core_flow Y 尚无映射的 E2E」
       - 映射有、`core_flows` 无 → ⚠️ 孤儿映射
    
    > ⛔ **"不可判"绝不能被当成"没问题"**(对账自身的 false-green,真项目上实测踩到过):
    > 若某份 PRD **不含任何 P0/P1 标记**,它提取出的就是空集,于是**不产生任何漂移条目**——
    > 报告若就此显示"对账通过",等于宣称"这份 PRD 里的流程都覆盖了",而事实是**它整份都没进闸门视野**。
    > 已上线的主链路最容易栽在这里(老 PRD 常只写"已上线/待开发",不标优先级)。
    >
    > **规则**:统计"因无优先级标记而未参与对账"的 PRD,**在报告里单列告警**,并且
    > **对账项不得判为 ✅**——写成 `⚠️ 无漂移,但另有 N 份 PRD 不可判`。
    > 处理建议:给这些 PRD 补优先级标记,或在 `quality-targets.yml` 里显式声明豁免(写明理由)。
    
    > 这样清单维护就从"靠自觉"变成**被产物纪律接管**——PRD 本就被 pdlc 逼着落盘并保持最新,
    > 让它当 `core_flows` 的唯一上游真源,与「状态外化到磁盘」是同一个哲学。
    
    ## 段三:出报告(真源 `.md` + 视图 `.html`)
    
    **① 先出 `.md`(真源)**:按 本 skill 目录下的 `assets/quality-report-template.md` 生成
    `docs/07_reviews/quality/<YYYY-MM-DD>.md`(**ledger 型**:一次一份,可 git diff、可看趋势;
    同日重跑则覆盖当日文件)。必须包含:
    
    1. 结论红绿表 2. 实测证据(命令 + 退出码 + 关键输出) 3. E2E 覆盖矩阵
    4. **配置健康度**(§2.3) 5. PRD 对账结果 6. 趋势(首次则写「无趋势基线」) 7. **人工确认签字栏**
    
    **② 再出 `.html`(视图)**:按 本 skill 目录下的 `assets/quality-report-template.html` 生成同目录同名
    `<YYYY-MM-DD>.html`——给人看的那一份,可直接双击打开、可打印签字、可发给同事。
    
    > **`.md` 是唯一真源**:`/pdlc-ship` 的发布闸门读它、`git diff` 看它、趋势对比取它。
    > `.html` 只是同一份数据的另一种呈现,**没有任何独立信息**。
    >
    > ⛔ **铁律:HTML 里的每个数字都从 `.md` 抄,不得重新计算、不得另行判断。**
    > 双写最容易出的错就是「两份报告各说各话」——那比没有 HTML 更糟:读者不知道该信哪份,
    > 而错的那份通常更好看。两处不一致时一律以 `.md` 为准并改正 `.html`。
    >
    > 填写要求:只替换 `{{...}}` 占位符与各表格的 `<tbody>` 内容;**不要改 `<style>`**
    > (排版由模板负责,改了就失去一致性);删掉模板顶部的「填写说明」注释块;
    > 保持自包含——**不得引入任何外部 URL**(css / js / 字体 / 图片一律不许外链:
    > 报告含项目内部数据,一张远程图片就够把打开时间、IP、referer 送给第三方,
    > 而且离线打不开)。确需图片内联成 `data:` URI。
    >
    > 三态 class 必须对号入座:`st-pass` 达标 / `st-fail` 未达标 / `st-warn` **无法判定** /
    > `st-na` 留空未测量。**`st-warn` 绝不能写成 `st-pass`**——这是 §6.5 那条反模式
    > (「把我判断不了当成没问题」)在报告上的最后一道落地面。页头的
    > `{{VERDICT_SHORT}}` 只填裸词 `pass` / `fail` / `warn`,一处占位符同时驱动顶条
    > 与徽章,顶条不会和总判定说两套话。
    
    ## 段四:自检(强制)
    
    <!-- @include templates/prompts/self-audit.md(已内联于下方,无需另读) -->
    ## 段二:自检(强制)
    
    重新阅读本次产出物,按质量关卡清单逐项检查。勾选已通过,标注未通过原因。
    
    > **注意**:自检清单的具体内容由各命令自行定义,本片段只规定结构。
    
    ## 段三:修复(单次,不递归)
    
    针对自检段标注为未通过的项:
    
    - **可自动修复**:直接修复(如补缺字段、修正格式、补齐缺失段落)
    - **修复后回验**:再次运行自检,确认被修复项现在通过
    - **无法自动修复**:记录到自审报告,不再尝试,流程继续
    
    ⚠️ 单次修复原则:若一轮修复后仍有项未通过,**不再递归修复**,防止死循环。
    <!-- @include-end templates/prompts/self-audit.md -->
    
    - [ ] 报告里每一条判定,都能追到本次真跑的退出码 / 工具输出 / 映射核对结果
    - [ ] 没有任何一项是靠"读代码觉得"得出的
    - [ ] 留空 / 无法执行的项,如实标注且**未按通过处理**
    - [ ] 覆盖率数字是从工具输出摘的原文
    - [ ] E2E 矩阵里每条 `core_flow` 都有明确判定(含"映射腐烂"这种红)
    - [ ] PRD 对账已执行,漂移项按红灯列出
    - [ ] 配置健康度已汇报:失效项 / 可收紧项都列了出来,且**没有擅自改动 yml**
    - [ ] 报告落盘到 `docs/07_reviews/quality/`,含生成时间与 commit SHA
    - [ ] 未在报告里替人做 go/no-go 决定
    - [ ] `.html` 已同步产出,且与 `.md` **逐项核对过关键数字**(覆盖率、核心流 M/N、各 check 退出码、不可判份数、总判定)——不一致以 `.md` 为准并已改正
    - [ ] `.html` 里无残留 `{{` 占位符、无任何外部 URL(含图片)、`<style>` 未被改动
    - [ ] 「无法判定」的项在 HTML 里用的是 `st-warn` 而非 `st-pass`;总判定未达标时页头顶条颜色已同步
    
    ## 段五:修复(单次,不递归)
    
    <!-- @include templates/prompts/loop-prevention.md(已内联于下方,无需另读) -->
    ## 防循环规则
    
    本命令所有的自检-修复循环均受以下约束:
    
    1. **单次检查**:同一个自检清单在本次命令执行中只跑一次(起始 + 修复后验证共两次读)
    2. **单次修复**:发现的问题只尝试修复一轮
    3. **不递归**:修复后不再重新触发自检的全量重跑
    4. **失败降级**:无法自动修复的问题 → 记录到自审报告 → 流程继续 → 最终报告标注待人工处理
    
    这是为了防止 agent 在"修完再查、查完再修"的往返中陷入死循环。
    <!-- @include-end templates/prompts/loop-prevention.md -->
    
    可自动修复的(如 lint 可自动修的告警)→ 修完**重跑该 check** 并以重跑结果为准,报告里注明"已自动修复后重测"。
    不可自动修复 → 如实留红,写进报告。
    
    ## 怎么"自动化运行"(不依赖 CI)
    
    - **pre-push 钩子**:push 前本地跑一遍质量闸,不达标就拦或告警——日常自动且**零 CI 成本**
    - **按需**:随时 `/pdlc-quality` 出全量报告
    - **发布挂钩**:`/pdlc-ship` 会读最近一份质量报告,未达标不让发(除非人显式 override 并写明理由)
    - **要"每天一份"**:用本机 launchd / cron 跑,报告进 git;**绝不**用 GitHub Actions `schedule`
    
    ## 段六:交接
    
    <!-- @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/07_reviews/quality/<YYYY-MM-DD>.md
      覆盖率      : <实测> / 目标 <目标>   ✅|❌
      E2E 核心流  : <M>/<N> 条已覆盖        ✅|❌
      Lint        : 退出码 <N>             ✅|❌
      PRD 对账    : <无漂移 | N 项漂移 | 另有 N 份不可判>  ✅|⚠️|❌
      配置健康度  : <全部健康 | N 项已失效 | N 项可收紧>
    🧾 总判定:<达标 | 未达标>
    ✍️ 待人工签字:报告最后一节(go/no-go 由你拍,本命令不代劳)
    👉 未达标项处理:<每条给出具体下一步>
    ```
    
    ## 诚实边界
    
    - **达标不等于质量好**:覆盖率线挡的是"几乎没测",不保证用例有效;矩阵证明"每条核心流有测试跑过",
      不证明"测得对"。报告要如实呈现这层含义,不要把"全绿"说成"质量有保障"。
    - **保障强度取决于 `core_flows` 维护得多勤**——这正是 §2.4 强制对账存在的原因,但对账只能发现
      "PRD 里有而清单里没有",**PRD 本身漏掉的核心流谁也发现不了**。这条限制要让用户知道。
    
    ---
    
    **参数**: $ARGUMENTS
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related