pdlc-feature — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited pdlc-feature (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<!-- @include templates/prompts/iron-law.md(已内联于下方,无需另读) --> ⛔ IRON LAW · 不可违反的硬门禁
以下规则为不可协商的执行约束:
docs/.pdlc-state/<feature-id>.json 追加 history,不可跳过。/pdlc-implement 前,对应测试必须存在且处于红灯状态。违反则中止。current_stage 必须变更。收尾时若发现 current_stage 未推进,视为失败并报错,不得静默返回(防止外层循环拿滞后的状态空转烧额度)。唯一例外:命中人工点主动 block 时,current_stage 保持不变但必须写 last_phase_result.ok=false + blocked_reason。违反任一条 = 立即中止当前命令,输出违规详情,等待人工介入。 <!-- @include-end templates/prompts/iron-law.md -->
接收功能描述或已有需求文档,全自动走完 PDLC 所有阶段,直到产出可上线状态。唯一的提问点在开工前:文字描述太单薄时问一轮澄清(带 --autonomous 则一个都不问);开工后中途不暂停、不询问用户。
从本命令的参数中判断输入类型:
/、./、../、~ 开头的路径.md、.txt、.docx、.pdf、.doc 结尾docs/ 或 requirements/ 路径片段<!-- 来源文档: <原始文件路径> -->--autonomous 下不问),再从描述自动推断docs/01_requirements/prd/ 下已有的 PRD 文件,则跳过阶段一,直接从阶段一-B(任务拆解)或阶段二(技术设计)开始;带 --lite 时直接从阶段三开始。功能ID 沿用该 PRD 追溯头里的 `功能ID`,不另分配(PRD 没有追溯头时才按下文分配新 ID)
F<日期>-<时分秒>)且 docs/.pdlc-state/<功能ID>.json 存在 → 读它的 current_stage / next_step,从下一个未完成的阶段继续(requirements 完成 → 阶段二,但 next_step 是 pdlc-tdd(轻量模式)→ 阶段三;design → 阶段三;tdd → 阶段四;impl → 阶段五;review 且 next_step=pdlc-ship → 已走完,只出最终报告)。 上次停在阻塞(last_phase_result.ok=false)时,先读 blocked_reason:原因已消除才继续,否则报告原因后停止
<!-- @include templates/prompts/clarify.md(已内联于下方,无需另读) -->
只对文字输入生效。输入是文件、已有 PRD 或功能ID 时跳过本节——已有文档按原文结构化,不再追问。
参数里的 --autonomous 是开关,不是需求的一部分:先把它剥掉,剩下的才是需求描述。
逐项判断用户的描述里有没有明说——看的是「写了没有」,不是「能不能猜出来」:
| 项 | 算「明说」的样子 |
|---|---|
| 给谁用 | 点出了用户角色或使用场景 |
| 要做什么 | 点出了具体行为,不只是一个名词(「登录」不算,「用手机号收验证码登录」算) |
| 明确不做什么 | 划了边界 |
| 怎样算做完 | 给了可观察的结果或验收口径 |
| 有什么限制 | 技术栈、兼容、性能、时间等约束 |
缺 3 项及以上 = 描述太单薄,按下面第 2 或第 3 步处理;缺得更少就不问,直接写 PRD(缺的项按推断补,照常写进 PRD)。
--autonomous:不问,把默认摆出来auto_decisions[] 每项追加一条:{ "point": "需求澄清:<项>", "chose": "<采用的默认>", "at": "<ISO 8601>" }——推进时给状态推进脚本传 --decision "需求澄清:<项>=<采用的默认>",每项一个
末行输出哨兵 <<<PDLC blocked reason="需求描述没有说要做什么">>> 后结束,交还人类 <!-- @include-end templates/prompts/clarify.md -->
--lite 按下文「轻量模式」跳过的阶段除外)/pdlc-retro 也看得到每个阶段):每个阶段收尾各写一次,规则见文末「状态机更新」。每个阶段收尾运行一次 bash scripts/pdlc-advance.sh <功能ID> --stage <下表短名> --next <下表下一跳> --produced <本阶段产物> <项目根> (阶段一新建状态文件时加 --name <功能名>;--autonomous 照传)。检查由脚本按阶段自己跑,不手写 checks:
| 阶段 | --stage | --next | 脚本跑的检查 |
|---|---|---|---|
| 一(PRD + 任务拆解 + PRD 评审)完成时创建状态文件 | requirements | pdlc-design | 不跑({}) |
| 二(技术设计) | design | pdlc-tdd | 不跑({}) |
| 三(TDD 红灯) | tdd | pdlc-implement | 红灯验证;--produced 要列出写的测试文件,脚本记下指纹 |
| 四(编码实现) | impl | pdlc-review | unit、coverage、lint;tdd 写的测试被改过就不推进 |
| 五(自查评审) | review | pdlc-ship | unit、coverage、lint、e2e |
脚本退出码 3 = 没推进(检查没过 / 主动 block):停在这里,不往下一阶段走。 某阶段失败或卡在需人判断的点(后者传 --blocked "<原因>")→ 状态停在上一阶段,最终报告说明可用 /pdlc-feature <功能ID> 续跑。 旧版本只在最后写一条 stage: feature,读侧仍认这个短名,但本版起不再写它
有子代理工具(Claude Code 的 Task / Agent)时,阶段二~五各交给一个全新的子代理执行,主会话只做编排与核对。 为什么:子代理只看得到磁盘上的文档和代码,看不到前一阶段的推理过程——写实现的子代理只能对着测试和设计干活, 不会顺着「写测试时怎么想的」去迁就;主会话的上下文也不会随阶段一路膨胀。
--lite 跳过的阶段照样不派派发时把下面的提示词填好交给子代理(尖括号都换成实际值;路径一律写绝对路径):
你在执行 PDLC /pdlc-feature 的「阶段<N>:<阶段名>」。功能ID <功能ID>,项目根 <项目根绝对路径>,轻量模式:<是 | 否>。
产出语言:<主会话判定的语言>(你看不到用户的对话,按这里写)。
1. 读 <本 skill 基础目录>/SKILL.md:通读「执行规则」「防循环规则」「测试代码在哪」「状态机更新」,
然后只执行「阶段<N>」一节及其质量关卡,不做别的阶段。
2. 需求和上游产物只从磁盘读:状态文件 <项目根>/docs/.pdlc-state/<功能ID>.json,以及其中 history[].produced 列出的文件。
3. 收尾运行 bash <本 skill 基础目录>/scripts/pdlc-advance.sh <功能ID> --stage <短名> --next <下一跳> --produced <本阶段产物> <项目根>
<主会话带了 --autonomous 就加上 --autonomous>。脚本退出码 3 = 没推进:停下,不要自己补救到下一阶段。
4. 你不能和用户对话:遇到必须由人判断的点,带 --blocked "<原因>" 运行上面的脚本后结束。
5. 结束时用不超过 15 行回报:产物路径、脚本最后输出的那行 JSON、自行做出的假设。子代理回来后,主会话自己读状态文件核对,不信子代理的文字:last_phase_result.stage 是本阶段短名、last_phase_result.ok 为 true、 current_stage 等于本阶段短名,三条都满足才派下一个阶段。
blocked_reason 是什么,提示用 /pdlc-feature <功能ID> 续跑(续跑同样按本节派发)不要在主会话里替它把这一阶段做完——那样隔离就没了,还会让主会话带着半截上下文往下走
--lite)给小功能用:保留 PDLC 的核心约束,砍掉文档开销。参数里的 --lite 是开关,和 --autonomous 一样先剥掉,剩下的才是需求。
| 完整模式 | 轻量模式 | |
|---|---|---|
| 阶段一 PRD | 模板全部章节 | 只写「背景与目标」「功能需求」(功能列表含验收标准、用户故事)「待确认问题」三章,其余章节删掉,不留空壳 |
| 阶段一-B 任务拆解 | 做 | 跳过 |
| 阶段一-C PRD 评审 | 做 | 跳过 |
| 阶段二 技术设计 + 设计评审 | 做 | 跳过 |
| 阶段三 ~ 五 | 照常 | 照常:先有红灯测试才写实现,checks 由脚本按退出码生成,做代码评审 |
--stage requirements --next pdlc-tdd(完整模式是 pdlc-design),脚本据此写 advanced_to: tdd;之后各阶段与上表一致。下一跳直接是 pdlc-tdd,所以 PRD 写完后也可以交给 /pdlc-loop-run 接着推。docs/01_requirements/prd/、docs/.pdlc-state/、docs/04_testing/unit-tests/、docs/07_reviews/code/ 四处(外加测试代码与实现代码),不创建其它 docs/ 子目录。/pdlc-design <功能ID>。<!-- @include templates/prompts/loop-prevention.md(已内联于下方,无需另读) -->
本命令所有的自检-修复循环均受以下约束:
这是为了防止 agent 在"修完再查、查完再修"的往返中陷入死循环。 <!-- @include-end templates/prompts/loop-prevention.md -->
date +%Y%m%d、date +%H%M%SF<YYYYMMDD>-<HHMMSS>(示例形如 F20260717-122801;用执行时的真实值)docs/ 或 docs/.pdlc-state/ 下已有同名前缀),重新读取 date +%H%M%S 重取(生成本身有耗时、通常已跨秒;若仍同秒则 sleep 1 后再读一次,不手算时分秒,天然处理跨天边界)user-auth)用时分秒而非当日序号,是为了多人 / 多 AI 并行时零协调也不撞号、合并零冲突。旧 F<日期>-<NN> ID 仍可解析。分配 ID 后,扫描 docs/.pdlc-state/*.json 列出已有 feature 名,结合用户描述判断本功能与既有 feature 的关系:
extends Xdepends_on Xsupersedes Xrelations 块写入。类型语义与方向性见本命令正文里的「Feature 关系链(6 种类型)」一节docs/01_requirements/prd/ 下创建文件,命名格式:<功能ID>-<功能名>-prd.mdassets/prd-template.md 作为模板 <!-- PDLC-TRACE -->
<!-- 功能ID: F20260326-090000 -->
<!-- 功能名称: user-auth -->
<!-- 阶段: 需求 -->
<!-- 前置文档: 无 -->
<!-- 创建时间: 2026-03-26T10:30:00 -->PRD 创建后、任务拆解前,立即执行自审(轻量模式按「轻量模式」一节放宽):
docs/06_tasks/ 目录,查找是否已存在该功能的任务文件pdlc-task plan 的逻辑自动执行任务拆解:T<功能ID的日期-时分秒>-<NN>-<type>,前缀嵌入本功能ID的时分秒段,NN 为本功能内递增序号)docs/06_tasks/<功能ID>-<功能名>-tasks.mdpdlc-task plan 的输出规范(每条任务含 ID、标题、类型、状态 ⬜、前置依赖)/pdlc-review 的文档评审段落逻辑,对 PRD 执行正式文档评审(聚焦格式规范性、模板符合度、交叉引用)assets/prd-template.md 检查格式规范性docs/07_reviews/doc/ 下创建评审记录:<功能ID>-<功能名>-prd-doc-review.md<!-- @include templates/prompts/design-options.md(已内联于下方,无需另读) -->
先判断这次设计有没有值得比较的技术取舍——存储怎么选、同步还是异步、新建服务还是扩展现有模块、接口怎么切、数据模型拆不拆等。
| 方案 | 做法(一句话) | 改动面 | 主要风险 | 对 PRD 验收标准 / 非功能需求 | 与现有代码和规范的契合 |
|---|
推荐写明理由,理由要落到 PRD 条目或项目规范上,不写「更优雅」「更灵活」这类说不出依据的话。
谁来选:
--autonomous,或在 /pdlc-feature 里——开工后不再提问,阶段二还可能跑在子代理里)→ 选推荐的方案,推进状态时传--decision "设计方案=<方案名>(<一句理由>)";候选之间的差别取决于 PRD 没交代的业务取舍(比如成本和一致性二选一)时,也照推荐选, 但把这个假设写进设计文档的「方案比较」节,让人在评审时看得到
落盘:设计文档开头加「方案比较」一节——比较表、选定的方案、怎么选的(人选 / 按推荐自动选);没比较的就是上面那一句。 后面的接口、表结构、架构都按选定的方案写,不要在正文里又混进落选方案的做法。 <!-- @include-end templates/prompts/design-options.md -->
根据 PRD 和上面选定的方案,自动判断需要哪些设计文档,按需创建(不需要的跳过):
API 设计(如涉及接口变更):
docs/02_design/api/<功能ID>-<功能名>-api.mdassets/api-design-template.md数据库设计(如涉及数据存储):
docs/02_design/database/<功能ID>-<功能名>-db.mdassets/db-design-template.md架构设计(如涉及新服务或重大架构变更):
docs/02_design/architecture/<功能ID>-<功能名>-arch.mdassets/arch-design-template.md所有设计文档顶部必须包含 PDLC 追溯头:
<!-- PDLC-TRACE -->
<!-- 功能ID: F20260326-090000 -->
<!-- 功能名称: user-auth -->
<!-- 阶段: 设计 -->
<!-- 前置文档: docs/01_requirements/prd/F20260326-090000-user-auth-prd.md -->每份设计文档创建后立即执行自审:
docs/07_reviews/doc/ 下创建设计评审记录:<功能ID>-<功能名>-design-doc-review.mddocs/04_testing/unit-tests/ 下创建测试计划:<功能ID>-<功能名>-test-plan.md<!-- @include templates/prompts/test-location.md(已内联于下方,无需另读) -->
⚠️ 这条规则的要害:红灯守卫必须区分「项目没有测试」和「测试不在我预期的位置」。 前者才该拦;后者拦了就是误伤——真实项目的测试布局千差万别(单体backend/tests/、 根级tests/、Go 同包*_test.go、Node 与源码同目录的*.test.tsx…), 按一份写死的路径清单去找、找不到就拦,会让 pdlc 在大量正常项目上直接卡死。
核心原则:优先问 runner,其次翻文件。 测试框架自己最清楚有哪些测试—— 让它报比我们去猜文件位置准得多,也和 pdlc「信退出码、不信目视检查」的哲学一致。 尤其测试写在源文件里的语言(见下),翻文件根本找不到。
按下列顺序定位,命中即停:
若存在 docs/00_standards/test-commands.yml,它的 unit / e2e 命令就是权威—— 项目已经明确告诉你测试怎么跑。用它去问 runner,而不是去翻目录:
| runner | 列出全部测试 | 只查某功能相关 |
|---|---|---|
| cargo (Rust) | cargo test -- --list | cargo test <关键词> -- --list |
| pytest | pytest --collect-only -q | pytest --collect-only -q -k <关键词> |
| go test | go test -list '.*' ./... | go test -list '<关键词>' ./... |
| vitest / jest | npx vitest list / --listTests | npx vitest list -t <关键词> |
| gradle / maven | --tests '*' 干跑 | --tests '*<关键词>*' |
查询结果为空 = 该功能没有测试(这是行为证据,比"我没找到文件"可靠得多)。
这类语言没有独立测试文件,只能按代码内标记搜:
| 语言 / 框架 | in-source 测试标记 |
|---|---|
| Rust | #[cfg(test)]、#[test]、mod tests |
| Vitest(in-source testing) | import.meta.vitest |
| Python doctest | docstring 里的 >>> |
| Elixir doctest | @doc 里的 iex> |
| Go(同包但独立文件) | *_test.go + func Test |
⚠️ Rust 尤其要注意:单元测试几乎总在源文件的#[cfg(test)] mod tests里,tests/目录按 Cargo 约定只放集成测试。所以「tests/目录不存在」在 Rust 项目里 完全不能推出「没有单元测试」——照文件清单判红会稳定误伤所有 Rust 项目。
| 生态 | 常见测试位置 |
|---|---|
| Python | tests/、backend/tests/、test/、与源码同目录的 test_*.py |
| Node / TS | tests/、__tests__/、src/**/__tests__/、与源码同目录的 *.test.ts(x) / *.spec.ts(x) |
| Rust | 源文件内 #[cfg(test)](单测)+ tests/(集成测试) |
| Go | 与源码同包的 *_test.go |
| JVM | src/test/java/、src/test/kotlin/ |
| Ruby | spec/、test/ |
| 微服务 / 单体仓 | 上述任一可能出现在 backend/、backend/services/<名>/、frontend/<应用>/ 之下 |
前三步都没命中时,按文件名模式全仓搜(*test* / *spec*,排除 node_modules、 .venv、vendor、dist、build、target、.git),再按功能关键词筛。
pdlc-implement 还需确认红灯)「项目有测试,只是没覆盖本功能」,别让用户以为项目裸奔
如实报「无法确认本功能是否有测试」并交还人类。「我判断不了」绝不等于「没问题」。
写测试时(pdlc-tdd)同样按本规则决定写到哪:跟随项目既有布局与惯例—— Rust 单测就写进源文件的#[cfg(test)] mod tests,不要为了迎合某种预设结构 新造一套平行的测试目录。
<!-- @include-end templates/prompts/test-location.md -->
docs/00_standards/test-commands.yml 的 coverage 命令阈值参数(那才是强制点,退出码即判定),其次 quality-targets.yml;两者都没有时按 >= 80% 兜底。docs/04_testing/e2e-tests/<功能ID>-<功能名>-e2e.md测试代码编写完成、运行前执行自审:
<!-- @include templates/prompts/batch-review.md(已内联于下方,无需另读) -->
什么时候分批:本功能的任务清单(docs/06_tasks/<功能ID>-*-tasks.md)里有 4 个及以上 feat 任务。 少于 4 个、或没有任务清单 → 不分批,照下面的步骤一次做完,不写分批评审记录。
怎么分:按任务清单的顺序,每 2~3 个 feat 任务一批,同一模块 / 同一接口的放在同一批。每批做完下面四步再开始下一批:
安全快查(拼接 SQL、未转义输出、敏感字段进日志)。能直接修的当场修一轮并重跑单测,修不了的记下来留给最终评审。 不改 tdd 写的测试——测试和设计对不上时,按状态推进规则交给人
docs/07_reviews/code/<功能ID>-<功能名>-batch-review.md,每批一节: ## 第 <N> 批:<任务ID>、<任务ID>
- 单测:<通过数> / <总数>(本批对应的测试全部为绿)
- 发现 <X> 项,当场修复 <Y> 项
- [已修复] <问题> — <改了哪里>
- [待最终评审] <问题> — <为什么这一步修不了>所有批次做完,再按下面的自检清单把整个功能过一遍。/pdlc-review 会读这份分批评审记录:已修过的不重复报,「待最终评审」的每一项都必须在最终结论里处理掉。 <!-- @include-end templates/prompts/batch-review.md -->
docs/00_standards/coding/ 目录确认编码规范代码实现完成、测试全部通过后,执行快速自检:
按 /pdlc-review 增强版逻辑执行全面评审,发现问题直接修复:
pdlc-lint check 运行 lint 工具,存在问题则 pdlc-lint fix 自动修复docs/07_reviews/code/ 下创建评审记录:<功能ID>-<功能名>-review.mdCHANGELOG.md,在 [未发布] 下新增 feat 条目。条目格式 - <简要描述>(<功能ID 或缺陷ID>);[未发布] 里已有同一 ID 的条目就不再追加(/pdlc-ship 按 ID 去重)⚠️ 文件落盘验证:输出最终报告前,必须逐一确认以下文件均已作为实际文件创建到磁盘(不可仅在对话中显示):
| 产出物 | 路径 | 验证方式 |
|---|---|---|
| PRD 文档 | docs/01_requirements/prd/<功能ID>-*-prd.md | 确认文件存在 |
| 任务清单 | docs/06_tasks/<功能ID>-*-tasks.md | 确认文件存在 |
| PRD 评审记录 | docs/07_reviews/doc/<功能ID>-*-prd-doc-review.md | 确认文件存在 |
| 设计文档 | docs/02_design/ 下对应目录 | 确认文件存在 |
| 设计评审记录 | docs/07_reviews/doc/<功能ID>-*-design-doc-review.md | 确认文件存在 |
| 测试计划 | docs/04_testing/unit-tests/<功能ID>-*-test-plan.md | 确认文件存在 |
| 测试代码 | 对应服务测试目录 | 确认文件存在 |
| 代码评审记录 | docs/07_reviews/code/<功能ID>-*-review.md | 确认文件存在 |
轻量模式只验 PRD、测试计划、测试代码、代码评审记录四行,其余几行本来就不产出。
如有文件缺失,立即补创建,不可跳过。
所有文件确认到位后,输出一份结构化的完成报告,格式如下。最终回复就是这份报告:各节都要有(没有内容的节写「无」),不要缩写成几行总结:
## PDLC 完成报告:<功能名>(<功能ID>)
**阶段执行方式**:<各阶段由全新子代理执行 | 各阶段未隔离执行(没有子代理工具)>(必填,二选一照实写,不要删这一行)
### 产出物清单
| 类型 | 文件路径 |
|------|----------|
| PRD | docs/01_requirements/prd/<功能ID>-... |
| API 设计 | docs/02_design/api/<功能ID>-... |
| 数据库设计 | docs/02_design/database/<功能ID>-... |
| 测试计划 | docs/04_testing/unit-tests/<功能ID>-... |
| 评审记录 | docs/07_reviews/code/<功能ID>-... |
### 测试结果
- 单元测试:X 个通过 / 0 个失败
- E2E 测试:X 个通过 / 0 个失败
- 覆盖率:XX%
### 任务完成情况
- 任务文件:`docs/06_tasks/<功能ID>-<功能名>-tasks.md`
- 总任务数:X 完成:X 进行中:X 未开始:X
### 验收标准确认
- [x] 验收标准 1
- [x] 验收标准 2
### 假设与决策说明
(记录执行过程中自行做出的关键假设,含各子代理回报的假设;轻量模式写明「轻量模式:跳过了任务拆解、PRD 评审、技术设计」)
### 上线前待办
(如有需要人工处理的事项,如数据库迁移、环境变量配置等)<!-- @include templates/prompts/output-language.md(已内联于下方,无需另读) --> 🌐 Output language for generated artifacts
All generated artifacts (PRDs, design docs, code comments, review reports, test plans, deployment manuals, changelog entries, etc.) follow this policy:
an artifact (e.g. "write the PRD in English", "用英文写 API 设计文档", "output the deploy doc in Japanese"), use that language for that artifact, regardless of conversation language.
language and others in a different language (common: Chinese PRD + English API docs for partners), honour each per-artifact instruction.
ask once before producing the first artifact.
This policy applies to content (prose, comments, headings). It does not override technical conventions like English variable names, English git commit subjects, or English error codes when the project's conventions require them. <!-- @include-end templates/prompts/output-language.md -->
user-login功能描述: $ARGUMENTS
<!-- @include templates/prompts/relations.md(已内联于下方,无需另读) -->
PDLC 用扁平 feature ID 空间。关系链在 5 个位置冗余表达,本文件是类型与语法的单一真相源。
| 类型 | 语义 | 方向性 | 示例 |
|---|---|---|---|
extends | A 是 B 的增量增强 | 有向(A→B) | user-auth-otp extends user-auth-phone |
depends_on | A 需要 B 存在 | 有向(A→B) | user-profile depends_on user-base |
supersedes | A 替代 B(B 进入废弃/待机) | 有向(A→B) | auth-v2 supersedes auth-v1 |
resolves | A 修复缺陷 B | 有向(A→B) | F20260603-090000 resolves B20260520-110000 |
conflicts_with | A 与 B 互斥 | 对称 | payment-stripe conflicts_with payment-paypal |
relates_to | 弱耦合,应一起考虑 | 对称 | password-policy relates_to otp-policy |
有向 vs 对称:
在 PDLC-TRACE 头加一行(无关系时整行省略):
<!-- 关系: extends=F20260510-100000; depends_on=F20260501-090000,F20260415-110000; resolves=B20260520-110000 -->语法:type=id 对,多 id 用 , 分隔,多对用 ; 分隔。
即状态机文件里的 relations 块(六键对象,每键一个 ID 数组)。存出边;入边由 /pdlc-relate rebuild 派生到 _relations.json。只写本功能自己的状态文件——不要为了「双向」去改对方功能的 relations,那是别的功能的状态,改了会让它看起来又被动过。
_relations.json(自动生成)/pdlc-relate rebuild 扫描所有 <id>.json 关系块 + 文档头,生成正向 edges + 预计算 inbound/outbound index。
_graph.md(自动生成)mermaid 可视化。边样式按类型区分:supersedes 虚线、conflicts_with 粗线、其余实线。
见 PRD 文档的 §6.1「关系」一节。
/pdlc-relate validate)extends / depends_on 链不允许成环supersedes + depends_onconflicts_with / relates_to 两端必须互含<!-- @include-end templates/prompts/relations.md -->
<!-- pdlc:meta 由 frontmatter 生成(adapters/sync_skills.py),勿手改 -->
本命令的状态机取值:串联多个阶段,阶段短名依次为requirements→design→tdd→impl→review——每个阶段收尾各写一次history[]与last_phase_result(current_stage随之推进,next_step写下一阶段的命令);不写feature。全部走完时下一跳pdlc-ship(写进next_step,交接时提示)。
<!-- pdlc:meta-end --> <!-- @include templates/prompts/state-update.md(已内联于下方,无需另读) -->
本命令完成主产出后,必须推进状态机文件 docs/.pdlc-state/<feature-id>.json。
bash scripts/pdlc-advance.sh <功能ID> --stage <本阶段短名> --next <下一跳命令名|null> \
[--produced <产物路径,逗号分隔>] [--autonomous] [--decision "<确认点>=<所选默认>"]... \
[--name <kebab-case>(状态文件还不存在时)] <项目根>scripts/pdlc-advance.sh。阶段短名与下一跳就是本命令正文里「本命令的状态机取值」那两个值;带了 --autonomous 就照传scripts/pdlc-checks.sh(tdd 验红灯;impl / fix / refactor 跑 unit、coverage、lint;review 再加 e2e;e2e 只跑 e2e;文档类阶段不跑),据此判 ok、整段写入 history 与 last_phase_result, 写完调用 scripts/pdlc-state-lint.sh 体检。不要自己先写 checks,也不要挑检查项
--produced 必须列出写的测试文件:脚本记下它们的指纹,之后 impl / refactor / review 推进时比对--blocked "<原因>":脚本照跑检查、如实记录,但不推进,原因写进 blocked_reason/pdlc-ship 写 ship_done、/pdlc-deploy 写 deploy_done → 加 --done(只有这两个命令能用)脚本的退出码决定怎么收尾:
| 退出码 | 含义 | 你要做的 |
|---|---|---|
0 | 已推进 | 交接里引用 stdout 那行 JSON(checks、current_stage、next_step) |
3 | 未推进:检查没过 / 红灯没验证 / 主动 block / tdd 写的测试被改 | 把 stdout 里的 blocked_reason 原样写进报告,按阻塞收尾。不要为了让它推进去改测试或改检查命令 |
2 | 脚本跑不了(参数错、缺 jq、状态文件损坏) | 参数错就改正重跑;缺 jq 才按下面的格式手写,并在报告里写明「状态机未经脚本写入」 |
退出码3且原因是「tdd 阶段写的测试被改动」:实现阶段不改测试。测试是否真的写错了(与设计 / PRD 矛盾),只有人能判断: - 交互模式:先把矛盾讲清楚、问人;人同意后再带--tests-changed "<理由>"重跑,理由记进auto_decisions---autonomous:没有人可问,不要改测试,带--blocked "<矛盾在哪>"收尾。自主模式下带--tests-changed脚本也不推进
>
项目开了 PDLC 护栏(docs/00_standards/pdlc-hooks.yml)时,用 Write / Edit 直接写状态文件会被拦下。{
"feature_id": "<F/B ID>",
"feature_name": "<kebab-case>",
"created_at": "<首次创建时间 ISO 8601>",
"current_stage": "<当前阶段名>",
"run_mode": "interactive | autonomous",
"history": [
{
"stage": "<阶段名>",
"done_at": "<ISO 8601>",
"produced": ["<相对路径 1>", "<相对路径 2>"],
"self_audit": { "passed": <N>, "failed": <N>, "manual": <N> },
"auto_decisions": [
{ "point": "<autonomous 下自动前进的确认点>", "chose": "<所选默认>", "at": "<ISO 8601>" }
]
}
],
"last_phase_result": {
"stage": "<本次阶段名>",
"ok": true,
"advanced_to": "<推进到的下一阶段 | null>",
"checks": {},
"self_audit": { "failed": 0 },
"blocked_reason": null,
"run_mode": "interactive | autonomous",
"at": "<ISO 8601>"
},
"relations": {
"extends": [],
"depends_on": [],
"supersedes": [],
"resolves": [],
"conflicts_with": [],
"relates_to": [],
"_updated_at": "<ISO 8601 | 省略>"
},
"next_step": "<下一跳命令名,如 pdlc-design;若流程结束则为 null>"
}⛔ 示例里的 "checks": {} 是「本阶段没有命令可跑」的样子,不是键名示范——键名与取值见下方 §1。`relations` 块(RFC#6,Phase 1 可选,Phase 2 推荐):6 个 key 对应 6 种关系类型,各为 ID 数组,存出边。其中conflicts_with/relates_to是对称类型,两端都要写;其余四种有向,只写在源 feature 上。拿不准时用/pdlc-relate set写入,它会按规则校验。旧状态文件无此块时视为全空,向后兼容。入边由/pdlc-relate rebuild派生到_relations.json,不在此块手维护。
⛔ 写状态机的四条硬约束——读侧(/pdlc-status、/pdlc-retro、/pdlc-relate)会逐条体检, 违反的每一处都会出现在它们输出的最前面:
>
1. 实例里不写 `terminal_state`。skill frontmatter 的terminal_state:是「这个命令走完后应到达的终态名」, 不是状态字段。判终态只看current_stage是否以_done结尾。 2. `history[].stage` 写本命令的阶段短名(见本命令正文里「本命令的状态机取值」)——pdlc-implement写impl, 不写implement/implementation;pdlc-prd写requirements,不写prd。 3. 时间戳必须带时刻:created_at/done_at/at一律写完整 ISO 8601(如2026-07-28T10:40:00+08:00)。 只写日期,同一天内的阶段耗时就全部算成 0——读侧只能记「不可测」。 4. `next_step` 只写命令名或 `null`,不附说明文字(如「pdlc-ship(等评审通过)」)。 要说明原因,阻塞时写进last_phase_result.blocked_reason。
⛔ `_done` 的含义是「已发布」,只由 `/pdlc-ship`(写 `ship_done`)与 `/pdlc-deploy`(写 `deploy_done`)写入。 其它命令的current_stage一律写本命令的阶段短名,走完整条链路的编排命令(/pdlc-feature)也一样—— 它收尾时current_stage是最后一个阶段的短名,next_step是pdlc-ship。
>
- 「评审通过、等待发布」就是current_stage为review且next_step为pdlc-ship(/pdlc-e2e在评审之前,它的下一跳是pdlc-review)。 循环相关文档里说的review_done指的就是这个状态,不是要写进current_stage的值。 - 为什么:读侧判「已抵达终态」只看current_stage是否以_done结尾。评审通过就写_done,/pdlc-ship就分不清哪些功能已经发布过,发布说明会重复或漏收。 - 旧版本写入的feature_done/fix_done/review_done分不清是否已发布,/pdlc-ship会列出来请人确认。
history 为含当前阶段的数组)history,更新 current_stage 和 next_stepjq 或等效工具保持格式化bash scripts/pdlc-state-lint.sh <项目根>,属于本功能状态文件的偏差当场改正。退出码 2(缺 jq 等)= 没体检,报告里写明「未体检」,不得当作通过。⚠️ 若更新失败(文件损坏/权限问题),必须中止命令并在最终报告中报错。状态机不可跳过。
last_phase_result(机器可读阶段结果;脚本按下面的规则写,手写回退时照做)顶层 last_phase_result 是循环判停的唯一真源,外层只需 jq '.last_phase_result.ok' 即可决定 继续 / 停止 / 交还人类。规则:
scripts/pdlc-checks.sh 的 stdout 原样),键名只能是 tests_pass / coverage_pass /lint_clean / e2e_pass(tdd 段 red_verified),值只能是布尔或 null(无法判定)。不照抄 test-commands.yml 的 unit / lint, 不写 "4 passed, 1 failed" 这类摘要。没有检查可跑的阶段(文档类阶段、项目无 test-commands.yml)→ checks: {}, 绝不因为「本阶段成功」就填 true——schema 示例里 checks 是空的,正是这个意思。
blocked_reason,且 tdd 段 red_verified 为 true、其它阶段没有任何检查为 false(null 不拦)→ true;否则 false。advanced_to = 下一阶段的短名,不是命令名、也不是本阶段的 `current_stage`。三者关系:stage=本阶段短名、current_stage=本阶段完成后的当前短名、advanced_to=下一阶段短名、next_step=下一跳命令名。⛔ 短名不是「命令名去掉 `pdlc-` 前缀」——pdlc-implement 的短名是 `impl`,不是 implement。别推导,查下表:
<!-- stage-map:start -->
next_step(下一跳命令名) | advanced_to(下一阶段短名) |
|---|---|
pdlc-tdd | tdd |
pdlc-implement | impl |
pdlc-review | review |
pdlc-design | design |
pdlc-ship | ship |
pdlc-deploy | deploy |
<!-- stage-map:end -->
next_step 为 null(终态或无后续)时 advanced_to 也是 null。
📌 本表是唯一真源,且是被断言钉住的:每行的短名必须等于该 skill 自己 frontmatter 里声明的stage:,且任何 skill 的非nullnext_step都必须在表里有行——两个方向 都由tests/frontmatter-check.sh检查,所以表不会和实现各自漂移。
>
写错短名的后果与键名写错同类:消费方按契约名匹配,认不出就当没这个阶段。
ok=true 时本阶段必须真的推进了 current_stage(与第 6 条 IRON LAW 呼应);到达终态或无后续时 advanced_to=null。ok=false(含 blocked)时 current_stage 不变、advanced_to=null、blocked_reason 写明原因。--autonomous(带了写 autonomous,没带写 interactive)。<!-- @include-end templates/prompts/state-update.md --> <!-- @include templates/prompts/handoff.md(已内联于下方,无需另读) -->
命令完成后必须输出以下格式的最终消息:
✅ <阶段名> 完成:<主要产出物路径>
📊 自检:<通过数>/<总数> 通过(若有未通过,附要点)
📦 状态快照:docs/.pdlc-state/<feature-id>.json
👉 下一步:/pdlc-<next_step>
(如果有分叉)或 /pdlc-<alt>(条件:<选择依据>)规则:
next_step,此时输出 👉 下一步:(本次流程结束,无后续)/pdlc-tdd;若测试已齐 → /pdlc-review"<!-- @include-end templates/prompts/handoff.md -->
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.