{"slug":"pdlc-fix","title":"pdlc-fix","summary":"全自动 Bug 修复（定位→复现→修复→测试→文档）","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-23T18:54:23.453936Z","repo":{"url":"https://github.com/kanfu-panda/pdlc-skills","stars":17,"forks":2,"license":"MIT","updatedAt":"2026-09-25T12:29:01Z"},"bodyHtml":"<hr>\n<p>name: pdlc-fix\ndescription: 全自动 Bug 修复（定位→复现→修复→测试→文档）\nargument-hint: &lt;Bug 描述&gt;\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash\nlayer: 1\nstage: fix\nproduces:</p>\n<ul>\n<li>docs/04_testing/defects/</li>\n</ul>\n<hr>\n<h1>全自动 Bug 修复</h1>\n\n<p>⛔ <strong>IRON LAW · 不可违反的硬门禁</strong></p>\n<p>以下规则为<strong>不可协商</strong>的执行约束：</p>\n<ol>\n<li><strong>文件必须落盘</strong>：所有带编号（功能ID / 缺陷ID）的文档，必须作为实际文件写入磁盘，不可仅在对话中输出。</li>\n<li><strong>阶段必须落章</strong>：每个阶段完成后必须在状态机 <code>docs/.pdlc-state/&lt;feature-id&gt;.json</code> 追加 history，不可跳过。</li>\n<li><strong>测试必须存在</strong>：进入 <code>/pdlc-implement</code> 前，对应测试必须存在且处于红灯状态。违反则中止。</li>\n<li><strong>自检必须执行</strong>：段二自检为强制步骤，不得以\"已经很好了\"为由跳过。</li>\n<li><strong>防循环</strong>：段三修复为单次，不递归。无法自动修复的问题记录到报告，继续往下走。</li>\n<li><strong>状态必推进</strong>：成功执行某 phase 后 <code>current_stage</code> 必须变更。收尾时若发现 <code>current_stage</code> 未推进，视为失败并报错，<strong>不得静默返回</strong>（防止外层循环拿滞后的状态空转烧额度）。唯一例外：命中人工点主动 block 时，<code>current_stage</code> 保持不变但必须写 <code>last_phase_result.ok=false</code> + <code>blocked_reason</code>。</li>\n</ol>\n<p><strong>违反任一条 = 立即中止当前命令，输出违规详情，等待人工介入。</strong></p>\n\n<p>接收一句话 Bug 描述，全自动完成定位、复现、修复、测试、文档更新，中途不暂停、不询问用户。</p>\n<h2>执行规则</h2>\n<ul>\n<li><strong>全程自动</strong>：不在任何步骤暂停等待确认，遇到歧义自行做合理假设并在最终报告中说明</li>\n<li><strong>严格顺序</strong>：必须按阶段一→二→三→四→五顺序执行，不得跳过</li>\n<li><strong>最小改动原则</strong>：只修复 Bug 本身，不顺手重构或优化不相关代码</li>\n<li><strong>测试验证才结束</strong>：全量测试通过后才输出最终报告</li>\n</ul>\n<hr>\n\n<h2>缺陷ID分配（必须执行）</h2>\n<ol>\n<li>获取当前日期与时分秒：<code>date +%Y%m%d</code>、<code>date +%H%M%S</code></li>\n<li>生成缺陷ID：<code>B&lt;YYYYMMDD&gt;-&lt;HHMMSS&gt;</code>（示例形如 <code>B20260717-122801</code>；用执行时的真实日期与时分秒）</li>\n<li><strong>本地防撞</strong>：若该 ID 已被占用（<code>docs/</code> 或 <code>docs/.pdlc-state/</code> 下已有同名前缀），重新读取 <code>date +%H%M%S</code> 重取（生成本身有耗时、通常已跨秒；若仍同秒则 <code>sleep 1</code> 后再读一次，<strong>不手算时分秒</strong>，天然处理跨天边界）</li>\n<li>从 Bug 描述中提取功能名关键词（英文小写+连字符）</li>\n</ol>\n<p><strong>为什么用时分秒而非序号</strong>：多人 / 多 AI 并行时各自独立取「当日序号 max+1」会分到相同编号、合并冲突。改用创建时刻 <code>HHMMSS</code> 后各副本零协调也几乎不撞（仅同一秒才可能），文件名互异、git 自动合并。旧的 <code>B&lt;日期&gt;-&lt;NN&gt;</code> ID 仍有效、可解析。</p>\n<p><strong>注意</strong>：日期与时分秒使用执行时的实际值。</p>\n\n<hr>\n<h2>阶段一：问题定位</h2>\n<ol>\n<li>根据 Bug 描述，搜索相关代码文件，阅读并理解涉及的逻辑</li>\n<li>查阅 <code>docs/02_design/</code> 对应子目录下的设计文档，确认<strong>预期正确行为</strong></li>\n<li>明确根因：\n<ul>\n<li>是代码逻辑错误、边界未处理、还是设计文档遗漏？</li>\n<li>影响范围：哪些模块/接口/数据受影响？</li>\n</ul>\n</li>\n<li>若 Bug 描述不足以定位，根据描述做最合理推断，在报告中说明</li>\n</ol>\n<h2>阶段二：回归测试（红灯）</h2>\n<ol>\n<li>编写一个<strong>能精确复现该 Bug 的测试用例</strong>，写到项目既有的测试布局里（定位规则见下）：</li>\n</ol>\n\n<h2>测试代码在哪（布局无关的定位规则）</h2>\n<blockquote>\n<p>⚠️ <strong>这条规则的要害</strong>：红灯守卫必须区分「<strong>项目没有测试</strong>」和「<strong>测试不在我预期的位置</strong>」。\n前者才该拦；后者拦了就是误伤——真实项目的测试布局千差万别（单体 <code>backend/tests/</code>、\n根级 <code>tests/</code>、Go 同包 <code>*_test.go</code>、Node 与源码同目录的 <code>*.test.tsx</code>…），\n按一份写死的路径清单去找、找不到就拦，会让 pdlc 在大量正常项目上直接卡死。</p>\n</blockquote>\n<p><strong>核心原则：优先问 runner，其次翻文件。</strong> 测试框架自己最清楚有哪些测试——\n让它报比我们去猜文件位置准得多，也和 pdlc「信退出码、不信目视检查」的哲学一致。\n尤其<strong>测试写在源文件里</strong>的语言（见下），翻文件根本找不到。</p>\n<p>按下列顺序定位，<strong>命中即停</strong>：</p>\n<h3>1. 项目自己的声明 + 向 runner 查询（最高优先级）</h3>\n<p>若存在 <code>docs/00_standards/test-commands.yml</code>，它的 <code>unit</code> / <code>e2e</code> 命令<strong>就是权威</strong>——\n项目已经明确告诉你测试怎么跑。<strong>用它去问 runner</strong>，而不是去翻目录：</p>\n<table>\n<thead>\n<tr>\n<th>runner</th>\n<th>列出全部测试</th>\n<th>只查某功能相关</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cargo (Rust)</td>\n<td><code>cargo test -- --list</code></td>\n<td><code>cargo test &lt;关键词&gt; -- --list</code></td>\n</tr>\n<tr>\n<td>pytest</td>\n<td><code>pytest --collect-only -q</code></td>\n<td><code>pytest --collect-only -q -k &lt;关键词&gt;</code></td>\n</tr>\n<tr>\n<td>go test</td>\n<td><code>go test -list '.*' ./...</code></td>\n<td><code>go test -list '&lt;关键词&gt;' ./...</code></td>\n</tr>\n<tr>\n<td>vitest / jest</td>\n<td><code>npx vitest list</code> / <code>--listTests</code></td>\n<td><code>npx vitest list -t &lt;关键词&gt;</code></td>\n</tr>\n<tr>\n<td>gradle / maven</td>\n<td><code>--tests '*'</code> 干跑</td>\n<td><code>--tests '*&lt;关键词&gt;*'</code></td>\n</tr>\n</tbody>\n</table>\n<p><strong>查询结果为空 = 该功能没有测试</strong>（这是行为证据，比\"我没找到文件\"可靠得多）。</p>\n<h3>2. 测试写在源文件里的语言（<strong>必须靠内容匹配，文件名扫描无效</strong>）⭐</h3>\n<p>这类语言没有独立测试文件，只能按<strong>代码内标记</strong>搜：</p>\n<table>\n<thead>\n<tr>\n<th>语言 / 框架</th>\n<th>in-source 测试标记</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Rust</strong></td>\n<td><code>#[cfg(test)]</code>、<code>#[test]</code>、<code>mod tests</code></td>\n</tr>\n<tr>\n<td><strong>Vitest</strong>（in-source testing）</td>\n<td><code>import.meta.vitest</code></td>\n</tr>\n<tr>\n<td><strong>Python</strong> doctest</td>\n<td>docstring 里的 <code>&gt;&gt;&gt; </code></td>\n</tr>\n<tr>\n<td><strong>Elixir</strong> doctest</td>\n<td><code>@doc</code> 里的 <code>iex&gt;</code></td>\n</tr>\n<tr>\n<td><strong>Go</strong>（同包但独立文件）</td>\n<td><code>*_test.go</code> + <code>func Test</code></td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>⚠️ <strong>Rust 尤其要注意</strong>：单元测试几乎总在源文件的 <code>#[cfg(test)] mod tests</code> 里，\n<code>tests/</code> 目录按 Cargo 约定只放<strong>集成测试</strong>。所以「<code>tests/</code> 目录不存在」在 Rust 项目里\n<strong>完全不能推出「没有单元测试」</strong>——照文件清单判红会稳定误伤所有 Rust 项目。</p>\n</blockquote>\n<h3>3. 常见布局约定（按项目实际技术栈挑，不要全试）</h3>\n<table>\n<thead>\n<tr>\n<th>生态</th>\n<th>常见测试位置</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Python</td>\n<td><code>tests/</code>、<code>backend/tests/</code>、<code>test/</code>、与源码同目录的 <code>test_*.py</code></td>\n</tr>\n<tr>\n<td>Node / TS</td>\n<td><code>tests/</code>、<code>__tests__/</code>、<code>src/**/__tests__/</code>、与源码同目录的 <code>*.test.ts(x)</code> / <code>*.spec.ts(x)</code></td>\n</tr>\n<tr>\n<td>Rust</td>\n<td>源文件内 <code>#[cfg(test)]</code>（单测）+ <code>tests/</code>（集成测试）</td>\n</tr>\n<tr>\n<td>Go</td>\n<td>与源码同包的 <code>*_test.go</code></td>\n</tr>\n<tr>\n<td>JVM</td>\n<td><code>src/test/java/</code>、<code>src/test/kotlin/</code></td>\n</tr>\n<tr>\n<td>Ruby</td>\n<td><code>spec/</code>、<code>test/</code></td>\n</tr>\n<tr>\n<td>微服务 / 单体仓</td>\n<td>上述任一可能出现在 <code>backend/</code>、<code>backend/services/&lt;名&gt;/</code>、<code>frontend/&lt;应用&gt;/</code> 之下</td>\n</tr>\n</tbody>\n</table>\n<h3>4. 文件名兜底扫描</h3>\n<p>前三步都没命中时，按文件名模式全仓搜（<code>*test*</code> / <code>*spec*</code>，排除 <code>node_modules</code>、\n<code>.venv</code>、<code>vendor</code>、<code>dist</code>、<code>build</code>、<code>target</code>、<code>.git</code>），再按功能关键词筛。</p>\n<h3>5. 判定</h3>\n<ul>\n<li><strong>任一步找到相关测试</strong> → 通过，进入下一步（<code>pdlc-implement</code> 还需确认红灯）</li>\n<li><strong>四步走完确实找不到任何测试</strong> → 这才是真红灯，按各命令的守卫规则中止</li>\n<li><strong>找到测试但与本功能无关</strong> → 按「本功能无测试」处理（同样是真红灯），但报告里要说明\n「项目有测试，只是没覆盖本功能」，别让用户以为项目裸奔</li>\n<li><strong>无法判定</strong>（如 runner 装不上、语言不认识）→ <strong>不要默认放行，也不要假装找到了</strong>：\n如实报「无法确认本功能是否有测试」并交还人类。<strong>「我判断不了」绝不等于「没问题」。</strong></li>\n</ul>\n<blockquote>\n<p>写测试时（<code>pdlc-tdd</code>）同样按本规则决定<strong>写到哪</strong>：跟随项目既有布局与惯例——\nRust 单测就写进源文件的 <code>#[cfg(test)] mod tests</code>，<strong>不要</strong>为了迎合某种预设结构\n新造一套平行的测试目录。</p>\n</blockquote>\n\n<ol start=\"2\">\n<li>测试命名格式：<code>应该_当&lt;触发条件&gt;时_&lt;预期行为&gt;</code></li>\n<li>运行该测试，<strong>确认测试失败（红灯）</strong>，记录失败输出</li>\n<li>若已有相关测试但未覆盖该场景，在原测试文件中追加用例</li>\n</ol>\n<h2>阶段三：代码修复（绿灯）</h2>\n<ol>\n<li>用<strong>最小改动</strong>修复 Bug，不改动与 Bug 无关的代码</li>\n<li>运行步骤二编写的回归测试，<strong>确认通过（绿灯）</strong></li>\n<li>若修复过程中发现设计文档有遗漏或错误，同步更新对应设计文档</li>\n</ol>\n<h2>阶段四：全量测试验证</h2>\n<ol>\n<li>运行该服务/应用的<strong>完整测试套件</strong>，确认没有引入新的失败</li>\n<li>若有 E2E 测试，运行相关 E2E 用例验证端到端行为正常</li>\n<li>如有测试因本次修复而需要更新（如快照测试、预期值变更），同步更新</li>\n</ol>\n<h2>阶段五：文档更新与最终报告</h2>\n<blockquote>\n<p>⚠️ <strong>必须创建文件，不可仅在对话中输出。</strong> 以下每份文档都必须作为实际文件写入磁盘。</p>\n</blockquote>\n<ol>\n<li>更新对应服务的 <code>CHANGELOG.md</code>，在 <code>[未发布]</code> 下新增 fix 条目：\n<ul>\n<li>格式：<code>- 修复 &lt;简要描述&gt;（&lt;触发条件&gt;）</code></li>\n</ul>\n</li>\n<li>若涉及设计文档变更，同步更新 <code>docs/02_design/</code> 下对应文档</li>\n<li><strong>【必须创建文件】</strong> 在 <code>docs/04_testing/defects/</code> 下创建缺陷记录：<code>&lt;缺陷ID&gt;-&lt;功能名&gt;-defect.md</code>\n<ul>\n<li><strong>文档顶部必须包含 PDLC 追溯头</strong>（格式见下方片段）：</li>\n</ul>\n</li>\n</ol>\n\n<h2>PDLC 追溯头（文档顶部必须包含）</h2>\n<p>每个带编号的文档必须以下列注释开头：</p>\n<pre><code>&lt;!-- PDLC-TRACE --&gt;\n&lt;!-- 功能ID: &lt;F/B 开头的 ID&gt; --&gt;\n&lt;!-- 功能名称: &lt;kebab-case 名&gt; --&gt;\n&lt;!-- 阶段: &lt;requirements | design | tdd | impl | review | e2e | ship | deploy | retro&gt; --&gt;\n&lt;!-- 前置文档: &lt;上一阶段文档路径 | 无&gt; --&gt;\n&lt;!-- 创建时间: &lt;执行时的实际 ISO 8601 时间戳&gt; --&gt;\n&lt;!-- 关系: &lt;type=id; ... | 整行省略&gt; --&gt;\n</code></pre>\n<p><strong>严禁</strong>把占位符（<code>&lt;...&gt;</code>）或示例日期原样写入实际文档。必须用真实值替换。</p>\n<p><strong>关系行（RFC#6，可选）</strong>：表达 feature 间关系链。无关系时<strong>整行省略</strong>（保持旧文档有效）。语法 <code>type=id</code> 对，多 id 用 <code>,</code> 分隔、多对用 <code>; </code> 分隔，例：<code>&lt;!-- 关系: extends=F20260510-100000; depends_on=F20260501-090000 --&gt;</code>。6 种类型：<code>extends</code> / <code>depends_on</code> / <code>supersedes</code> / <code>resolves</code>（有向）与 <code>conflicts_with</code> / <code>relates_to</code>（对称）。</p>\n\n<ul>\n<li>记录：根因分析、影响范围、修复方案、回归测试覆盖情况</li>\n<li><strong>创建后验证</strong>：确认文件已存在于 <code>docs/04_testing/defects/</code> 目录</li>\n</ul>\n<ol start=\"4\">\n<li>输出最终报告（同时在对话中显示，<strong>但报告内容必须已落盘到上述文件中</strong>）：</li>\n</ol>\n<pre><code>## Bug 修复报告：&lt;简要描述&gt;（&lt;缺陷ID&gt;）\n\n### 根因分析\n&lt;一段话描述根本原因&gt;\n\n### 影响范围\n- 涉及文件：\n- 涉及接口/功能：\n\n### 修复方案\n&lt;描述做了什么改动，为什么这样改&gt;\n\n### 测试结果\n- 回归测试：通过\n- 全量测试：X 个通过 / 0 个失败\n\n### 文档变更\n- CHANGELOG.md：已更新\n- 设计文档：已更新 / 无需更新\n\n### 假设与说明\n（记录执行过程中自行做出的关键假设）\n\n### 上线前待办\n（如有需要人工处理的事项，如数据修复脚本、缓存清理等）\n</code></pre>\n<hr>\n<h2>产出物清单（每项必须作为文件创建）</h2>\n<table>\n<thead>\n<tr>\n<th>产出物</th>\n<th>路径</th>\n<th>必须</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>缺陷记录</td>\n<td><code>docs/04_testing/defects/&lt;缺陷ID&gt;-&lt;功能名&gt;-defect.md</code></td>\n<td>✅ 必须</td>\n</tr>\n<tr>\n<td>回归测试</td>\n<td>对应服务测试目录</td>\n<td>✅ 必须</td>\n</tr>\n<tr>\n<td>CHANGELOG</td>\n<td>对应服务 <code>CHANGELOG.md</code></td>\n<td>✅ 必须</td>\n</tr>\n<tr>\n<td>设计文档更新</td>\n<td><code>docs/02_design/</code> 下对应文档</td>\n<td>按需</td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p><strong>文件落盘规则</strong>：所有带编号（缺陷ID）的文档必须作为实际文件写入，不可仅在对话中显示。最终报告完成前，必须确认上述文件均已创建。</p>\n</blockquote>\n<h2>要求</h2>\n\n<p>\uD83C\uDF10 <strong>Output language for generated artifacts</strong></p>\n<p>All generated artifacts (PRDs, design docs, code comments, review reports,\ntest plans, deployment manuals, changelog entries, etc.) follow this policy:</p>\n<ol>\n<li><p><strong>Default — match the conversation language exactly</strong>:</p>\n<ul>\n<li>用户用中文与 Claude 对话 → 产中文文档、中文代码注释、中文报告</li>\n<li>User talks to Claude in English → produce English artifacts</li>\n<li>User talks in another language → produce artifacts in that language</li>\n<li><strong>Never silently default to a fixed language regardless of the user's input.</strong></li>\n</ul>\n</li>\n<li><p><strong>Explicit override always wins</strong>: when the user specifies a language for\nan artifact (e.g. \"write the PRD in English\", \"用英文写 API 设计文档\",\n\"output the deploy doc in Japanese\"), use that language for that artifact,\nregardless of conversation language.</p>\n</li>\n<li><p><strong>Mixed-language requirements</strong>: if the user wants some artifacts in one\nlanguage and others in a different language (common: Chinese PRD + English\nAPI docs for partners), honour each per-artifact instruction.</p>\n</li>\n<li><p><strong>Uncertain</strong>: if you cannot reliably detect the conversation language,\nask once before producing the first artifact.</p>\n</li>\n</ol>\n<p>This policy applies to <strong>content</strong> (prose, comments, headings). It does\n<strong>not</strong> override technical conventions like English variable names, English\ngit commit subjects, or English error codes when the project's conventions\nrequire them.</p>\n\n<ul>\n<li>只修复 Bug，不做额外优化或重构</li>\n<li>回归测试用例必须保留在代码库中，不得删除</li>\n<li>日期使用执行当天实际日期，格式 YYYYMMDD</li>\n</ul>\n<p>Bug 描述: $ARGUMENTS</p>\n\n<blockquote>\n<p><strong>本命令的状态机取值</strong>：阶段短名 <code>fix</code>（写进 <code>history[].stage</code> 与 <code>last_phase_result.stage</code>）；下一跳 <code>pdlc-review</code>（写进 <code>next_step</code>，交接时提示）。</p>\n</blockquote>\n\n\n<h2>状态机更新（段四必须执行）</h2>\n<p>本命令完成主产出后，必须更新状态机文件 <code>docs/.pdlc-state/&lt;feature-id&gt;.json</code>。</p>\n<h3>文件格式</h3>\n<pre><code>{\n  \"feature_id\": \"&lt;F/B ID&gt;\",\n  \"feature_name\": \"&lt;kebab-case&gt;\",\n  \"created_at\": \"&lt;首次创建时间 ISO 8601&gt;\",\n  \"current_stage\": \"&lt;当前阶段名&gt;\",\n  \"run_mode\": \"interactive | autonomous\",\n  \"history\": [\n    {\n      \"stage\": \"&lt;阶段名&gt;\",\n      \"done_at\": \"&lt;ISO 8601&gt;\",\n      \"produced\": [\"&lt;相对路径 1&gt;\", \"&lt;相对路径 2&gt;\"],\n      \"self_audit\": { \"passed\": &lt;N&gt;, \"failed\": &lt;N&gt;, \"manual\": &lt;N&gt; },\n      \"auto_decisions\": [\n        { \"point\": \"&lt;autonomous 下自动前进的确认点&gt;\", \"chose\": \"&lt;所选默认&gt;\", \"at\": \"&lt;ISO 8601&gt;\" }\n      ]\n    }\n  ],\n  \"last_phase_result\": {\n    \"stage\": \"&lt;本次阶段名&gt;\",\n    \"ok\": true,\n    \"advanced_to\": \"&lt;推进到的下一阶段 | null&gt;\",\n    \"checks\": {},\n    \"self_audit\": { \"failed\": 0 },\n    \"blocked_reason\": null,\n    \"run_mode\": \"interactive | autonomous\",\n    \"at\": \"&lt;ISO 8601&gt;\"\n  },\n  \"relations\": {\n    \"extends\": [],\n    \"depends_on\": [],\n    \"supersedes\": [],\n    \"resolves\": [],\n    \"conflicts_with\": [],\n    \"relates_to\": [],\n    \"_updated_at\": \"&lt;ISO 8601 | 省略&gt;\"\n  },\n  \"next_step\": \"&lt;下一跳命令名，如 pdlc-design；若流程结束则为 null&gt;\"\n}\n</code></pre>\n<blockquote>\n<p>⛔ 示例里的 <code>\"checks\": {}</code> 是「本阶段没有命令可跑」的样子，<strong>不是键名示范</strong>——键名与取值见下方 §1。</p>\n</blockquote>\n<blockquote>\n<p><strong><code>relations</code> 块（RFC#6，Phase 1 可选，Phase 2 推荐）</strong>：6 个 key 对应 6 种关系类型，各为 ID 数组，存<strong>出边</strong>。其中 <code>conflicts_with</code> / <code>relates_to</code> 是对称类型，两端都要写；其余四种有向，只写在源 feature 上。拿不准时用 <code>/pdlc-relate set</code> 写入，它会按规则校验。旧状态文件无此块时视为全空，向后兼容。入边由 <code>/pdlc-relate rebuild</code> 派生到 <code>_relations.json</code>，不在此块手维护。</p>\n</blockquote>\n<blockquote>\n<p>⛔ <strong>写状态机的四条硬约束</strong>——读侧（<code>/pdlc-status</code>、<code>/pdlc-retro</code>、<code>/pdlc-relate</code>）会逐条体检，\n违反的每一处都会出现在它们输出的最前面：</p>\n<ol>\n<li><strong>实例里不写 <code>terminal_state</code></strong>。skill frontmatter 的 <code>terminal_state:</code> 是「这个命令走完后应到达的终态名」，\n不是状态字段。判终态只看 <code>current_stage</code> 是否以 <code>_done</code> 结尾。</li>\n<li><strong><code>history[].stage</code> 写本命令的阶段短名</strong>（见本命令正文里「本命令的状态机取值」）——<code>pdlc-implement</code> 写 <code>impl</code>，\n不写 <code>implement</code> / <code>implementation</code>；<code>pdlc-prd</code> 写 <code>requirements</code>，不写 <code>prd</code>。</li>\n<li><strong>时间戳必须带时刻</strong>：<code>created_at</code> / <code>done_at</code> / <code>at</code> 一律写完整 ISO 8601（如 <code>2026-07-28T10:40:00+08:00</code>）。\n只写日期，同一天内的阶段耗时就全部算成 0——读侧只能记「不可测」。</li>\n<li><strong><code>next_step</code> 只写命令名或 <code>null</code></strong>，不附说明文字（如「pdlc-ship（等评审通过）」）。\n要说明原因，阻塞时写进 <code>last_phase_result.blocked_reason</code>。</li>\n</ol>\n</blockquote>\n<blockquote>\n<p>⛔ <strong><code>_done</code> 的含义是「已发布」，只由 <code>/pdlc-ship</code>（写 <code>ship_done</code>）与 <code>/pdlc-deploy</code>（写 <code>deploy_done</code>）写入。</strong>\n其它命令的 <code>current_stage</code> 一律写本命令的阶段短名，走完整条链路的编排命令（<code>/pdlc-feature</code>）也一样——\n它收尾时 <code>current_stage</code> 是最后一个阶段的短名，<code>next_step</code> 是 <code>pdlc-ship</code>。</p>\n<ul>\n<li>「评审通过、等待发布」就是 <code>current_stage</code> 为 <code>review</code>（或 <code>e2e</code> 等）且 <code>next_step</code> 为 <code>pdlc-ship</code>。\n循环相关文档里说的 <code>review_done</code> 指的就是这个状态，<strong>不是</strong>要写进 <code>current_stage</code> 的值。</li>\n<li>为什么：读侧判「已抵达终态」只看 <code>current_stage</code> 是否以 <code>_done</code> 结尾。评审通过就写 <code>_done</code>，\n<code>/pdlc-ship</code> 就分不清哪些功能已经发布过，发布说明会重复或漏收。</li>\n<li>旧版本写入的 <code>feature_done</code> / <code>fix_done</code> / <code>review_done</code> 分不清是否已发布，<code>/pdlc-ship</code> 会列出来请人确认。</li>\n</ul>\n</blockquote>\n<h3>更新流程</h3>\n<ol>\n<li><strong>文件不存在</strong> → 创建文件，写入初始结构（<code>history</code> 为含当前阶段的数组）</li>\n<li><strong>文件存在</strong> → 读取 JSON，追加当前阶段到 <code>history</code>，更新 <code>current_stage</code> 和 <code>next_step</code></li>\n<li><strong>写回文件</strong>：用 <code>jq</code> 或等效工具保持格式化</li>\n</ol>\n<p>⚠️ 若更新失败（文件损坏/权限问题），必须中止命令并在最终报告中报错。状态机不可跳过。</p>\n<h3><code>last_phase_result</code>（机器可读阶段结果，每个 phase 收尾必写）</h3>\n<p>顶层 <code>last_phase_result</code> 是循环判停的<strong>唯一真源</strong>，外层只需 <code>jq '.last_phase_result.ok'</code> 即可决定 继续 / 停止 / 交还人类。规则：</p>\n<ol>\n<li><p><strong><code>checks</code> 必须客观、真跑得来</strong>：只放<strong>真跑命令的退出码</strong>结果（命令取自 <code>docs/00_standards/test-commands.yml</code>，见 <code>test-commands-template.yml</code>），<strong>绝不用模型自评、绝不填占位</strong>。有测试的阶段用 <code>tests_pass</code> / <code>coverage_pass</code> / <code>lint_clean</code>（退出码 0 → <code>true</code>，非 0 → <code>false</code>）；stage 语义不同用对应键（如 tdd 段 <code>{ \"red_verified\": true }</code> 表示红灯已验证）。</p>\n<blockquote>\n<p>⛔ <strong>键名与类型都是契约的一部分</strong>：键名只能是 <code>tests_pass</code> / <code>coverage_pass</code> /\n<code>lint_clean</code> / <code>e2e_pass</code>（tdd 段 <code>red_verified</code>），值只能是<strong>布尔</strong>。\n最常见的两种错法：① 照抄 <code>test-commands.yml</code> 的 <code>unit</code> / <code>coverage</code> / <code>lint</code> / <code>e2e</code>\n——那是<strong>命令表</strong>的字段名，不是状态机的（跑 <code>unit</code> 得到的结论写进 <code>tests_pass</code>）；\n② 写成 <code>\"4 passed, 1 failed\"</code> 这类字符串摘要。两种都会让 <code>jq '.checks.tests_pass'</code>\n读回 <code>null</code>，消费方（发布闸门、质量报告、自主循环）只看到「无法判定」——\n<strong>你诚实跑出来的结果等于没写</strong>。三态怎么分见本命令正文里「跑 check 命令：退出码的三态语义」一节；正文里没有这一节的命令不跑 check 命令，<code>checks</code> 写 <code>{}</code>。</p>\n<p>⚠️ <strong>没有检查命令可跑的阶段（如 requirements/design 只产文档，或项目无 <code>test-commands.yml</code>）→ <code>checks: {}</code> 留空。绝不因为「本阶段成功」就把 <code>tests_pass</code>/<code>lint_clean</code> 等填 <code>true</code>——那是虚报，会污染跨工具共用的状态机、误导自主循环判停。</strong> 上面 schema 示例里 <code>checks</code> 之所以是空的，正是这个原因——<strong>空是\"没跑\"的意思，不是键名的示范</strong>。</p>\n</blockquote>\n</li>\n<li><p><strong><code>self_audit</code> 单列</strong>：只放自检未通过数，<strong>仅供参考，不作循环判停依据</strong>。</p>\n</li>\n<li><p><strong><code>ok</code> 的定义</strong>：本阶段全部 <code>checks</code> 通过且未命中 <code>blocked_reason</code> → <code>true</code>；否则 <code>false</code>。</p>\n</li>\n<li><p><strong>命名空间</strong>：<code>advanced_to</code> = <strong>下一阶段的短名</strong>，<strong>不是命令名、也不是本阶段的 <code>current_stage</code></strong>。三者关系：<code>stage</code>=本阶段短名、<code>current_stage</code>=本阶段完成后的当前短名、<code>advanced_to</code>=下一阶段短名、<code>next_step</code>=下一跳命令名。</p>\n<p>⛔ <strong>短名不是「命令名去掉 <code>pdlc-</code> 前缀」</strong>——<code>pdlc-implement</code> 的短名是 <strong><code>impl</code></strong>，不是 <code>implement</code>。别推导，查下表：</p>\n</li>\n</ol>\n\n<table>\n<thead>\n<tr>\n<th><code>next_step</code>（下一跳命令名）</th>\n<th><code>advanced_to</code>（下一阶段短名）</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>pdlc-tdd</code></td>\n<td><code>tdd</code></td>\n</tr>\n<tr>\n<td><code>pdlc-implement</code></td>\n<td><code>impl</code></td>\n</tr>\n<tr>\n<td><code>pdlc-review</code></td>\n<td><code>review</code></td>\n</tr>\n<tr>\n<td><code>pdlc-design</code></td>\n<td><code>design</code></td>\n</tr>\n<tr>\n<td><code>pdlc-ship</code></td>\n<td><code>ship</code></td>\n</tr>\n<tr>\n<td><code>pdlc-deploy</code></td>\n<td><code>deploy</code></td>\n</tr>\n</tbody>\n</table>\n\n<p><code>next_step</code> 为 <code>null</code>（终态或无后续）时 <code>advanced_to</code> 也是 <code>null</code>。</p>\n<blockquote>\n<p>\uD83D\uDCCC <strong>本表是唯一真源，且是被断言钉住的</strong>：每行的短名必须等于该 skill 自己 frontmatter\n里声明的 <code>stage:</code>，且任何 skill 的非 <code>null</code> <code>next_step</code> 都必须在表里有行——两个方向\n都由 <code>tests/frontmatter-check.sh</code> 检查，所以表不会和实现各自漂移。</p>\n<p>写错短名的后果与键名写错同类：消费方按契约名匹配，认不出就当没这个阶段。</p>\n</blockquote>\n<ol start=\"5\">\n<li><strong>推进一致</strong>：<code>ok=true</code> 时本阶段必须真的推进了 <code>current_stage</code>（与第 6 条 IRON LAW 呼应）；到达终态或无后续时 <code>advanced_to=null</code>。<code>ok=false</code>（含 blocked）时 <code>current_stage</code> 不变、<code>advanced_to=null</code>、<code>blocked_reason</code> 写明原因。</li>\n<li><strong><code>run_mode</code></strong>：镜像本次调用是否带 <code>--autonomous</code>（带了写 <code>autonomous</code>，没带写 <code>interactive</code>）。</li>\n</ol>\n\n\n<h2>段四：交接（Handoff）</h2>\n<p>命令完成后必须输出以下格式的最终消息：</p>\n<pre><code>✅ &lt;阶段名&gt; 完成：&lt;主要产出物路径&gt;\n\uD83D\uDCCA 自检：&lt;通过数&gt;/&lt;总数&gt; 通过（若有未通过，附要点）\n\uD83D\uDCE6 状态快照：docs/.pdlc-state/&lt;feature-id&gt;.json\n\uD83D\uDC49 下一步：/pdlc-&lt;next_step&gt;\n   （如果有分叉）或 /pdlc-&lt;alt&gt;（条件：&lt;选择依据&gt;）\n</code></pre>\n<p><strong>规则：</strong></p>\n<ul>\n<li>主流程命令（写状态机的命令；下一跳见正文里「本命令的状态机取值」）必须显式输出\"下一步\"，不可省略</li>\n<li>工具型命令（Layer 3）可以没有 <code>next_step</code>，此时输出 <code>\uD83D\uDC49 下一步：（本次流程结束，无后续）</code></li>\n<li>分叉场景必须说明<strong>选择条件</strong>，例如\"若需补充测试用例 → <code>/pdlc-tdd</code>；若测试已齐 → <code>/pdlc-review</code>\"</li>\n</ul>\n\n","files":[{"path":"SKILL.md","sizeBytes":23945,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-23T18:54:42.616591Z","sha256":"AAFDF26389678DD64338153BBFDF314DA965CBEAC7652CF1C8D49ED8F57489E8","sizeBytes":11327},"review":null,"source":{"repositoryUrl":"https://github.com/kanfu-panda/pdlc-skills","path":"skills/pdlc-fix","license":"MIT","commit":"181ecf1fddc46f1e5f3ab04f64f93ae42d35fa34","subtreeSha":"B283FA7B9F7AD2DB3449F50668547C702088B7586D3CAC2C8FDE4DA7B26F6CD7","lastSyncedAt":"2026-10-01T15:23:29.701635Z"},"reviewedAt":"2026-09-23T18:55:30.163436Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/kanfu-panda/pdlc-skills/tree/main/skills/pdlc-fix"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kanfu-panda-pdlc-skills@llmmart"},{"target":"git","command":"git clone https://github.com/kanfu-panda/pdlc-skills.git"}]}