pdlc-review — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited pdlc-review (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 --> <!-- @include templates/prompts/noninteractive.md(已内联于下方,无需另读) -->
--autonomous)若本命令的参数含 --autonomous,本命令进入无人值守模式,按以下规则处理原本需要人应答的交互点。参数是唯一真源:不带 --autonomous 即为交互模式,一切照旧正常询问用户;绝不回读状态机 run_mode 兜底(「掉出 autonomous」是安全的失败方向)。
history[].auto_decisions[](写状态机的命令:推进时给状态推进脚本传 --decision "<确认点描述>=<所选默认>",可重复;脚本写成下面的样子):
{ "point": "<确认点描述>", "chose": "<所选默认>", "at": "<ISO 8601>" }改测试、改检查命令让它变绿,永远不是「流程性默认」,不能记成 --decision:
current_stage 保持不变(不推进)last_phase_result.ok = false 且 blocked_reason = "<原因>"(写状态机的命令:给状态推进脚本传 --blocked "<原因>")<<<PDLC blocked reason="<原因>">>>--autonomous 下拦住本命令时,不能只输出 ⛔ 就停——同样写 ok=false + blocked_reason(写明哪条守卫、缺什么)并输出哨兵。 否则状态机里仍是上一阶段的 ok=true,循环驱动只看到 current_stage 没动,会把「被拦」误报成「卡住」,真实原因丢失。
--autonomous 无效,仍必须人工显式确认。CHANGELOG.md、补全文档 PDLC-TRACE 的创建时间等本阶段职责内、可安全默认的辅助改动)→ 视为流程性默认,直接做并记入 `auto_decisions[]`;这类改动不新增外部副作用,不属破坏性操作。进入 autonomous 模式时,在状态机顶层写 run_mode: "autonomous" 仅供留痕(复盘区分人工 vs 循环产出)。<!-- @include-end templates/prompts/noninteractive.md -->
对指定的服务或应用进行全面的代码评审。
.md 等文档路径,或明说评审 PRD / 设计 / 测试计划 / 部署手册)→ 直接跳到下文「文档评审」一节。文档评审不写状态机:它是阶段内的质量关卡,不是主链路上的一个阶段——写了会把功能的 current_stage 改成 review、next_step 改成 pdlc-ship,等于宣称代码已评审。下文的代码评审守卫也不适用
B 开头,/pdlc-fix 的下一跳)时,读 docs/.pdlc-state/<缺陷ID>.json 与 docs/04_testing/defects/ 下对应的缺陷报告,评审对象是这次修复改动的代码
(如 src/、app/、lib/、backend/、frontend/、packages/*/ 等),不要只看某一种布局; 已有状态机时,优先看其 history 里 impl / fix 阶段记下的 produced 路径
⛔ PDLC 守卫:未找到与「<功能名>」相关的实现代码。
评审必须基于已有的代码实现。请先运行:
👉 /pdlc-implement <目标> ⛔ PDLC 守卫:「<功能名>」的测试未全部通过,无法进行评审。
请先确保所有测试通过后再提交评审:
👉 /pdlc-implement <目标>(修复失败的测试)docs/02_design/ 对应子目录下的相关设计文档docs/07_reviews/code/<功能ID>-*-batch-review.md,实现阶段按任务分批时写的)→ 一并读:「已修复」的不重复报;「待最终评审」的每一项都要在本次评审里处理——修掉,或写进报告的需人工处理表,不能悄悄漏掉
docs/00_standards/coding/ 目录了解编码规范(未命中 → 报告里提示 consider /pdlc-standard add coding/<topic>)/pdlc-lint check 逻辑运行项目 lint 工具/pdlc-lint fix 逻辑自动修复先判断适用性:下面的清单按 Web 服务写成,不是每个项目都有接口、数据库或页面。 开始前按项目类型(Web 服务 / CLI 工具 / 库 / 桌面应用 / 数据脚本等)与本次改动范围,逐项判断是否适用:
[x] 或记为问题[—] 不适用:<一句话理由>(如「无 HTTP 接口」「本次改动不涉及数据库」)。不适用项不算未通过,也不进自动修复{ code, message, data })docs/00_standards/test-commands.yml 的 coverage 命令阈值参数(那才是强制点,退出码即判定),其次 quality-targets.yml;两者都没有时按 >= 80% 兜底。)对以下类型的问题直接修复代码,不仅仅记录(只修适用项里发现的问题——标为不适用的检查项,一律不据此改代码; 例如项目没有列表接口,就不会去「补分页」):
- <简要描述>(<功能ID 或缺陷ID>);[未发布] 里已有同一 ID 的条目就不再追加(/pdlc-ship 按 ID 去重)不可自动修复的问题(记录到评审报告,标记为需人工处理):
⚠️ 必须创建文件,不可仅在对话中输出。
【必须创建文件】 在 docs/07_reviews/code/ 下创建评审记录:
<功能ID>-<功能名>-review.md(如 F20260326-090000-user-auth-review.md) <!-- PDLC-TRACE -->
<!-- 功能ID: F20260326-090000 -->
<!-- 功能名称: user-auth -->
<!-- 阶段: 评审 -->
<!-- 前置文档: docs/02_design/api/F20260326-090000-user-auth-api.md -->
<!-- 创建时间: 2026-03-26T10:30:00 --> ## 评审总结
- 评审时间:<ISO 8601>
- 评审范围:<涉及的文件数和代码行数>
- 问题总数:X 项(阻塞: X / 严重: X / 一般: X / 建议: X)
- 自动修复:X 项
- 需人工处理:X 项
## 自动修复记录
| # | 问题类型 | 文件 | 修复内容 |
|---|---------|------|---------|
| 1 | lint | src/xxx.ts | 修复 XX 规则违规 |
## 需人工处理
| # | 严重程度 | 问题描述 | 建议方案 |
|---|---------|---------|---------|
| 1 | 阻塞 | XXX | 建议 XXX |
## 评审检查项结论
- [x] 设计一致性:通过
- [x] 代码质量:通过(X 项已自动修复)
- [ ] 安全检查:X 项需人工确认
- [—] 性能检查:不适用(<理由>)docs/00_standards/test-commands.yml),确认修复未引入新问题scripts/pdlc-advance.sh 会跑 unit、coverage、lint、e2e 并写入(e2e 没配置时为 null,不影响 ok);退出码三态语义与「命令跑不了 = yml 过期信号」见下方 check 命令规则
--autonomous 一致——「评审通过」只有一个定义):bash scripts/pdlc-advance.sh <功能ID> --stage review --next pdlc-ship --blocked "评审存在阻塞级待人工项" <项目根>;--autonomous 下另输出 blocked 哨兵交还人类。交互模式下可以把阻塞项列给用户:用户当场明确放行的,在评审报告里记「人工放行:<项> · <理由>」后不再算阻塞
bash scripts/pdlc-advance.sh <功能ID> --stage review --next pdlc-ship --produced <评审报告> <项目根>:检查全过才推进到 review → pdlc-ship(评审通过即此状态,不写任何 _done);检查红了脚本不推进,按阻塞收尾
<!-- @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 -->
评审目标: $ARGUMENTS
对指定的文档进行质量评审,检查完整性、一致性和可操作性。发现问题直接修复,而非仅列出建议。
#### 完整性
#### 一致性
#### 可操作性
#### 规范性
不可自动修复的问题(记录到评审报告):
assets/prd-template.md、API 设计 → assets/api-design-template.md、DB 设计 → assets/db-design-template.md、架构设计 → assets/arch-design-template.md、测试计划 → assets/test-plan-template.md、部署手册 → assets/deploy-doc-template.mddocs/07_reviews/doc/ 下创建评审记录<功能ID>-<功能名>-<文档类型>-doc-review.md ## 文档评审报告
- 评审时间:<ISO 8601>
- 目标文档:<文档路径>
- 文档类型:<PRD/API设计/DB设计/...>
- 问题总数:X 项(必须修改: X / 建议修改: X / 可选: X)
- 自动修复:X 项
- 需人工确认:X 项
## 自动修复记录
| # | 问题类型 | 修复内容 |
|---|---------|---------|
| 1 | 缺失章节 | 补充了「非功能需求」章节 |
## 需人工确认
| # | 严重程度 | 问题描述 | 建议 |
|---|---------|---------|------|
| 1 | 必须修改 | XXX 需求存在歧义 | 建议与产品确认 |
## 检查项结论
- [x] 完整性:通过
- [x] 一致性:通过(X 项已修复)
- [x] 可操作性:通过
- [x] 规范性:通过<!-- @include templates/prompts/check-commands.md(已内联于下方,无需另读) -->
用脚本跑,不要手写 `checks`:
bash scripts/pdlc-checks.sh --only <本阶段要的项,如 unit,lint> <项目根>脚本是本 skill 自带的 scripts/pdlc-checks.sh。它读 docs/00_standards/test-commands.yml(唯一真源),逐条真跑,按下表映射,stdout 只有一行 JSON—— 原样写进 last_phase_result.checks,不改键名、不改值、不补不删。每条命令的退出码与输出末尾回显在 stderr,报告里引用那几行即可。脚本退出码 2(没有 yml 等)= 没有可跑的 check → checks: {}。
为什么不手写:键名、布尔类型、三态这几件事是确定性的,交给模型写,真机上同一份项目轮与轮之间就换一种错法 (键名照抄 yml 的unit、值写成"4 passed, 1 failed"、127写成false)。 环境里没有bash时才按下表手工映射。
脚本按退出码分三态——不是两态。这是 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 里那条命令还在)。
由于各阶段本来就在跑这些命令,这个信号是白捡的。检测到时:
观察到什么(退出码 / 报错原文)、以及为什么判定为"跑不了"而非"没通过"。
/pdlc-test-setup --refresh(重新探测并给出 diff)。--refresh 时适用)更新这份 yml 等于改变"通过"的定义,所以按方向区别对待:
| 方向 | 例子 | 处理 |
|---|---|---|
| 让闸门变严 | 空着的 e2e 现在能跑了、覆盖率阈值上调 | 可自动应用,报告留痕 |
| 平移替换 | 命令改名但语义相同,且新命令已验证能跑 | 可自动应用,报告留痕 |
| 让闸门变松 | 删掉某条 check、把命令改成空、下调阈值 | 必须人确认,绝不自动 |
⚠️ 这条方向规则是防「自动修复把闸门修没了」:lint 命令坏掉时,把它留空是最省事的 "修法",结果闸门悄悄松了、报告还是绿的——比不更新更危险。 变严可以自动,变松必须由人签字。
<!-- @include-end templates/prompts/check-commands.md --> <!-- pdlc:meta 由 frontmatter 生成(adapters/sync_skills.py),勿手改 -->
本命令的状态机取值:阶段短名review(写进history[].stage与last_phase_result.stage);下一跳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.