kld-sdd 2.6.15 → 2.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,592 @@
1
+ ---
2
+ name: opsx-consistency-check
3
+ description: "Spec 一致性校验 - 验证代码实现与 spec 文档的一致性"
4
+ argument-hint: "[change-name] [file...]"
5
+ license: MIT
6
+ metadata:
7
+ author: sdd-team
8
+ version: "1.0"
9
+ allowed-tools:
10
+ - Bash
11
+ - Read
12
+ - Write
13
+ - Edit
14
+ ---
15
+
16
+ 你是一个 SDD(Specification-Driven Development)Spec 一致性校验专家。激活本技能后,你将对比 spec 文档与代码实现,验证代码是否完全符合 spec 定义。
17
+
18
+ > **对抗性审核原则**
19
+ >
20
+ > 假设 spec 和代码是由其他人(或其他 AI)生成的,你的任务是**严格找出不一致、遗漏和潜在问题**,而不是确认一致性。
21
+ >
22
+ > - 不要假设"既然 spec 这样写了,代码肯定也这样实现了"
23
+ > - 不要跳过看似正确但未经逐行比对的细节
24
+ > - 对每个断言都要找到**具体的代码证据**,找不到就标记为 missed
25
+ > - 宁可误报(false positive),不可漏报(false negative)
26
+
27
+ ---
28
+
29
+ ## 技能定位
30
+
31
+ | 维度 | 内容 |
32
+ |------|------|
33
+ | 核心问题 | 代码实现是否符合 spec 定义 |
34
+ | 关键输出 | 结构化校验报告(置信度 + 维度详情 + 怀疑清单 + 建议) |
35
+ | 校验维度 | 接口契约、业务规则、校验规则、错误码、前后端一致性 |
36
+ | 上游依赖 | spec.md + 代码文件 |
37
+ | 校验方式 | **纯 AI 语义分析**(零外部依赖) |
38
+
39
+ ---
40
+
41
+ ## 启动流程
42
+
43
+ ### 1. 【交互引导】确认校验范围和审核模式
44
+
45
+ **1.1 确认校验范围**
46
+
47
+ 若用户提供了 change-name,读取对应变更的 spec:
48
+ - `openspec/changes/<change-name>/specs/*/spec.md`
49
+ - `openspec/specs/*/spec.md`(全局 spec)
50
+
51
+ 若用户提供了具体文件路径,只校验这些文件对应的 spec。
52
+
53
+ 若均未提供,列出所有活跃变更供用户选择:
54
+ ```bash
55
+ ls openspec/changes/ | grep -v archive
56
+ ```
57
+
58
+ **1.2 确认审核模式**
59
+
60
+ | 模式 | 参数 | 说明 |
61
+ |------|------|------|
62
+ | **独立审核模式**(默认) | 无参数 | 对抗性审核,假设代码是别人写的,严格找问题 |
63
+ | **自审模式** | `--mode=self-review` | 同一 AI 审核自己生成的代码,需人工确认 |
64
+
65
+ 若用户未指定 `--mode=self-review`,默认使用**独立审核模式**。
66
+
67
+ ### 2. 获取变更文件并确定校验文件范围
68
+
69
+ > **关键约束**:本步骤确定两个文件列表:
70
+ > - `CHANGED_FILES`:**所有与当前变更相关**的文件(包括暂存和未暂存的修改),是**报告输出**的范围
71
+ > - `REFERENCE_FILES`:`CHANGED_FILES` + 直接引用的关联文件(DTO、Model、Interface 等),是 **AI 分析**的范围(用于跨文件对比)
72
+ >
73
+ > **数据源强制约束**:必须使用 `git diff HEAD --name-only` 获取所有变更文件列表(包括暂存和未暂存的修改)。**禁止**仅使用 `git diff --cached --name-only`,因为它会遗漏未暂存的修改,导致校验结果不完整。
74
+
75
+ #### 2.0 多仓库检测(强制前置步骤)
76
+
77
+ > **背景**:项目可能采用多仓库(multi-repo)结构,即工作区内包含多个独立的 git 仓库(如前端、后端各自独立仓库,spec 文档在根仓库)。单仓库场景下本步骤无副作用。
78
+
79
+ **检测与采集流程**:
80
+
81
+ **Step A — 发现所有 git 仓库**:
82
+ ```bash
83
+ # 1. 获取根仓库的 git top-level
84
+ git rev-parse --show-toplevel
85
+ # 结果记为 ROOT_REPO
86
+
87
+ # 2. 扫描工作区下所有子目录中的 .git(目录或文件, submodule 的 .git 是文件)
88
+ # 找出所有独立 git 仓库(排除 ROOT_REPO 自身)
89
+ find . -name ".git" -maxdepth 3 -not -path "./.git" | ...
90
+ # 对每个找到的子仓库,用 git rev-parse --show-toplevel 确认其根路径
91
+ ```
92
+
93
+ **Step B — 逐仓库采集变更**:
94
+
95
+ 对**每个** git 仓库(包括根仓库和所有子仓库),分别执行:
96
+ ```bash
97
+ # 进入该仓库目录
98
+ cd <repo-root>
99
+ # 获取该仓库的所有变更文件(暂存 + 未暂存)
100
+ git diff HEAD --name-only
101
+ ```
102
+
103
+ **Step C — 路径归一化**:
104
+
105
+ 将各子仓库的变更文件路径转换为**相对于工作区根目录**的绝对路径或相对路径:
106
+ - 根仓库的文件:路径不变(如 `openspec/changes/...`)
107
+ - 子仓库的文件:拼接子仓库相对路径 + 文件路径(如 `sdd-demo2/src/main/java/.../UserController.java`)
108
+
109
+ **Step D — 合并变更列表**:
110
+
111
+ 将所有仓库的变更文件合并为统一的 `ALL_CHANGED_FILES` 列表,每个条目格式为:
112
+ ```
113
+ <workspace-relative-path> ← <repo-root-relative-path>
114
+ ```
115
+ 例如:
116
+ ```
117
+ sdd-demo2/src/main/java/.../UserController.java ← sdd-demo2 (repo)
118
+ sdd-demo1/src/api/user.js ← sdd-demo1 (repo)
119
+ ```
120
+
121
+ > **单仓库兼容**:若 Step A 未发现任何子仓库,则退化为仅在根仓库执行 `git diff HEAD --name-only`,行为与原有逻辑完全一致。
122
+
123
+ 根据校验范围获取变更文件列表:
124
+
125
+ **若指定了 change-name**:
126
+ ```bash
127
+ # 按 Step A-D 采集所有仓库的变更文件,合并为 ALL_CHANGED_FILES
128
+ ```
129
+
130
+ **若未指定变更**:
131
+ ```bash
132
+ # 同样按 Step A-D 采集所有仓库的变更文件
133
+ ```
134
+
135
+ **若指定了具体文件**:
136
+ 直接使用用户指定的文件列表。
137
+
138
+ #### 2.1 变更文件筛选(强制)
139
+
140
+ > **核心原则**:变更列表中可能包含多个变更的文件,也可能包含不涉及 spec 一致性校验的文件(如文档、配置、脚本等)。必须筛选出**与当前变更相关且可进行 spec 一致性校验**的文件。
141
+
142
+ **筛选规则 — 排除以下文件**:
143
+
144
+ | 排除类型 | 文件模式 | 说明 |
145
+ |---------|---------|------|
146
+ | 非当前变更的代码 | 路径不属于当前 change-name 的 spec 覆盖范围 | 根据 spec 的模块/路径定义判断 |
147
+ | 文档文件 | `*.md`, `*.txt`, `*.rst`(非 spec 文件) | 文档不参与一致性校验 |
148
+ | 配置文件 | `*.yml`, `*.yaml`, `*.properties`, `*.ini`, `*.toml`, `*.json`(非路由/接口定义) | 配置文件通常不涉及 spec 接口契约校验 |
149
+ | 脚本文件 | `*.bat`, `*.sh`, `*.cjs`, `*.mjs`, `*.ps1` | 工具脚本不参与一致性校验 |
150
+ | 类型声明文件 | `*.d.ts`, `*.d.tsx` 等 | TypeScript 类型声明文件不涉及 spec 校验 |
151
+ | 依赖管理 | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `*.lock`, `go.sum`, `Cargo.lock` | 锁文件不参与校验 |
152
+ | 构建产物 | `dist/`, `build/`, `target/`, `out/`, `*.min.js`, `*.min.css` | 编译/打包产物不参与校验 |
153
+ | 测试文件 | `*test.*`, `*spec.*`, `*_test.*`, `__tests__/`, `tests/`, `test/` | 测试代码不参与本次校验(由 opsx-test 负责) |
154
+ | 已删除文件 | git status 为 `deleted` 的文件 | 已删除的文件无法进行校验 |
155
+ | 工具链配置 | `.gitignore`, `.editorconfig`, `.prettierrc`, `.eslintrc.*`, `tsconfig.*`, `*.config.js/ts` | 开发工具配置不涉及业务 spec |
156
+
157
+ **筛选规则 — 保留以下文件**:
158
+
159
+ | 保留类型 | 文件模式 | 说明 |
160
+ |---------|---------|------|
161
+ | 业务代码 | 与当前变更 spec 直接相关的源代码文件 | 根据 spec 覆盖范围判断 |
162
+
163
+ **各语言业务代码识别**:
164
+
165
+ | 语言 | 业务代码扩展名 | 典型目录模式(参考) |
166
+ |------|--------------|-------------------|
167
+ | Java | `*.java` | controller, service, repository, model, dto, entity, config, filter, handler |
168
+ | TypeScript/JavaScript | `*.ts`, `*.tsx`, `*.js`, `*.jsx`(排除 `*.d.ts`) | api, service, store, hook, component, page, controller, model, util |
169
+ | Python | `*.py` | views, controllers, services, models, serializers, handlers, routers |
170
+ | Go | `*.go`(排除 `*_test.go`) | handler, service, repository, model, controller, api |
171
+ | Rust | `*.rs` | handler, service, model, controller, api |
172
+ | C# | `*.cs` | Controllers, Services, Repositories, Models, DTOs |
173
+
174
+ **筛选方法**:
175
+ 1. 获取变更文件完整列表(按步骤 2.0 的多仓库流程,合并为 `ALL_CHANGED_FILES`)
176
+ 2. 读取当前 change-name 的 spec 文件,提取其覆盖的模块/路径范围
177
+ 3. 对每个变更文件:
178
+ - 判断是否为业务代码(根据语言扩展名和目录模式)
179
+ - 判断是否属于当前 spec 覆盖范围(根据文件路径、包名、模块名等)
180
+ - 若两者都满足,则保留;否则排除
181
+ 4. 不属于的文件从 `CHANGED_FILES` 中移除,不纳入校验范围,也不在报告中出现
182
+
183
+ **最终输出**:
184
+ - `CHANGED_FILES` — 筛选后的变更文件列表(仅包含与当前变更相关且可校验的文件),路径为工作区相对路径
185
+ - `REFERENCE_FILES` — 在步骤 4 中通过扫描 import/引用关系扩展得出
186
+
187
+ ### 3. 读取 Spec 文档
188
+
189
+ 按以下顺序读取 spec 内容(排除 archive 和 logs):
190
+
191
+ ```bash
192
+ find openspec -name "*.md" -not -path "*/archive/*" -not -path "*/logs/*"
193
+ ```
194
+
195
+ 重点提取:
196
+ - 接口定义(路径、方法、参数、响应)
197
+ - 业务规则(条件判断、状态流转、权限控制)
198
+ - 校验规则(字段约束、非空校验、格式要求)
199
+ - 错误码定义(错误码、触发条件、响应格式)
200
+
201
+ ### 4. 识别项目语言并确定文件范围
202
+
203
+ **4.1 识别项目语言**
204
+
205
+ > **多仓库场景**:每个子仓库可能使用不同的技术栈(如前端 Vue、后端 Java),需分别识别。
206
+
207
+ | 项目标识 | 语言 | 代码文件 | 路由定义示例 |
208
+ |---------|------|---------|-------------|
209
+ | `pom.xml` / `build.gradle` | Java | `*.java` | `@RequestMapping` / `@PostMapping` |
210
+ | `package.json` | TypeScript/JS | `*.ts`/`*.tsx`/`*.js`/`*.jsx` | `router.get()` / `@Get()` / `app.get()` |
211
+ | `requirements.txt` / `pyproject.toml` | Python | `*.py` | `@app.route()` / `@router.get()` |
212
+ | `go.mod` | Go | `*.go` | `r.GET()` / `e.GET()` |
213
+ | `Cargo.toml` | Rust | `*.rs` | `#[get()]` / `Router::route()` |
214
+ | `*.csproj` / `*.sln` | C# | `*.cs` | `[HttpGet]` / `[HttpPost]` |
215
+
216
+ **重要**:后端项目必须同时校验前端 TypeScript/JavaScript 文件(若存在),确保前后端字段对齐。
217
+
218
+ **4.2 扫描关联文件**
219
+
220
+ 从 `CHANGED_FILES` 中的代码文件出发,扫描 import/引用关系,找出直接关联的文件:
221
+
222
+ | 语言 | 代码文件 | 引用语句 |
223
+ |------|---------|---------|
224
+ | Java | `*.java` | `import` |
225
+ | TypeScript/JS | `*.ts`/`*.tsx`/`*.js`/`*.jsx` | `import ... from` |
226
+ | Python | `*.py` | `import` / `from ... import` |
227
+ | Go | `*.go` | `import "..."` |
228
+ | Rust | `*.rs` | `use` / `mod` |
229
+ | C# | `*.cs` | `using` |
230
+
231
+ **关联文件筛选规则**:
232
+ - 仅包含**直接引用**的文件(一层深度,不递归)
233
+ - 仅包含**同项目**内的文件(排除 node_modules、第三方库、vendor、site-packages 等)
234
+ - **多仓库场景**:关联文件扫描限制在**同一子仓库**内,不跨仓库扫描(因为跨仓库的引用关系无法通过 import 语句直接追踪)
235
+ - 优先提取:DTO / Request / Response / Schema、Model / Entity、Interface / Type、Repository / Service
236
+
237
+ **4.3 标记文件来源**
238
+
239
+ - `changed`:变更文件,问题归因到此类文件
240
+ - `referenced`:关联文件,仅用于跨文件对比,不报告其问题
241
+
242
+ ### 5. 读取代码文件
243
+
244
+ 读取 `REFERENCE_FILES` 中的所有代码文件作为上下文。
245
+
246
+ > **范围约束(强制)**:
247
+ > - **分析范围**:`REFERENCE_FILES`(变更文件 + 关联文件),用于理解完整上下文
248
+ > - **报告范围**:仅 `CHANGED_FILES`,**报告中的所有差异项只能引用 `CHANGED_FILES` 中的文件**
249
+ >
250
+ > **关键规则**:
251
+ > - 若校验发现问题的代码位于 `REFERENCE_FILES` 但**不在** `CHANGED_FILES` 中(即该文件不属于当前变更范围),**不得**将其作为差异项报告
252
+ > - 此类问题应归入"怀疑清单",标注为"⚠️ 该问题位于非变更文件 `<文件路径>` 中,建议检查该文件是否属于当前变更"
253
+ > - 例如:后端 `AuthController.java` 在 `CHANGED_FILES` 中,但前端 `authService.ts` 不在 `CHANGED_FILES` 中,则前后端不一致的问题**不能**作为差异报告,只能放入怀疑清单
254
+
255
+ ### 6. 执行五维一致性校验
256
+
257
+ 基于代码内容,逐行对比 spec 定义进行校验。
258
+
259
+ > **对抗性审核要求**:对每个检查项,必须在代码中找到**具体行号**作为证据。找不到证据的,一律标记为 ❌ missed。不要因为"看起来应该实现了"就标记为 ✅。
260
+
261
+ > **逐字段对比强制约束**:
262
+ > - 响应结构必须**逐字段**对比,不允许"大致匹配"
263
+ > - 字段名必须**精确匹配**(`token` ≠ `accessToken`,`id` ≠ `userId`)
264
+ > - 嵌套结构的每一层都必须校验
265
+ > - 若代码中构造响应的方式是 Map/字典/结构体/序列化对象(如 Java `Map.of()`、Python `dict`、Go `map[string]interface{}`、C# `new {}`),必须提取所有 key 并与 spec 逐一对比
266
+
267
+ #### 6.1 后端接口契约校验
268
+
269
+ 对 `CHANGED_FILES` 中的后端代码文件 + 关联 DTO/Model 的数据,进行以下校验:
270
+
271
+ - [ ] API 路径:路由定义与 spec 定义的路径一致(证据:代码文件:行号)
272
+ - [ ] HTTP 方法:路由声明的 HTTP 方法与 spec 定义的方法匹配(证据:代码文件:行号)
273
+ - [ ] 请求参数:请求体/查询参数的字段名、类型与 spec 定义的参数一致(证据:DTO/Model文件:行号)
274
+ - [ ] 参数约束:字段上的校验注解/装饰器/类型约束与 spec 定义的约束一致(证据:DTO/Model文件:行号)
275
+ - [ ] 响应结构:**逐字段**对比代码中构造的响应与 spec 定义的响应字段(证据:代码文件:行号)
276
+ - [ ] HTTP 状态码:代码中返回的 HTTP 状态码与 spec 定义的状态码一致(证据:代码文件:行号)
277
+ - [ ] 错误码:代码中抛出的业务错误码与 spec 定义一致(证据:代码文件:行号)
278
+
279
+ #### 6.2 前端接口契约校验
280
+
281
+ 对 `CHANGED_FILES` 中的前端代码文件(TypeScript/JavaScript/HTML 等),进行以下校验:
282
+
283
+ - [ ] API 路径:HTTP 请求调用的 URL 与 spec 定义的路径一致(证据:代码文件:行号)
284
+ - [ ] HTTP 方法:请求声明的 HTTP 方法与 spec 定义的方法匹配(证据:代码文件:行号)
285
+ - [ ] 请求字段:请求体/查询参数中的字段名与 spec 定义的参数名一致(证据:代码文件:行号)
286
+ - [ ] 校验规则:代码中的字段校验逻辑与 spec 定义的字段约束一致(证据:代码文件:行号)
287
+ - [ ] 存储字段:本地存储操作的 key 与 spec 定义的响应字段对应(证据:代码文件:行号)
288
+ - [ ] 响应处理:代码中读取响应字段的名称与后端实际返回的字段名一致(证据:代码文件:行号)
289
+
290
+ #### 6.3 业务规则校验
291
+
292
+ - [ ] spec 中的条件判断在代码中有对应实现(证据:代码文件:行号)
293
+ - [ ] spec 中的状态流转在代码中体现(证据:代码文件:行号)
294
+ - [ ] spec 中的权限要求在代码中有校验(证据:代码文件:行号)
295
+ - [ ] 条件分支逻辑正确性(如 if/else 链的条件判断顺序是否导致不可达分支)
296
+
297
+ #### 6.4 校验规则
298
+
299
+ - [ ] spec 中的字段约束(长度、格式、范围)在代码中有对应校验(证据:代码文件:行号)
300
+ - [ ] spec 中标记为必填的字段有非空检查(证据:代码文件:行号)
301
+
302
+ #### 6.5 错误码校验
303
+
304
+ - [ ] spec 中定义的错误码在代码中实现(证据:代码文件:行号)
305
+ - [ ] 错误码的触发条件与 spec 描述一致(证据:代码文件:行号)
306
+ - [ ] 错误响应格式与 spec 定义一致(证据:代码文件:行号)
307
+
308
+ #### 6.6 前后端一致性校验
309
+
310
+ 对 `CHANGED_FILES` 中同时包含前端和后端文件时,进行跨文件对比:
311
+
312
+ > **多仓库场景**:前端和后端可能分别位于不同的子仓库(如 `sdd-demo1/` 和 `sdd-demo2/`),但只要它们的变更文件都在 `CHANGED_FILES` 中,就可以进行跨文件对比。
313
+
314
+ - [ ] 前端请求字段 vs 后端 DTO 字段:字段名必须精确匹配(如前端 `phone` vs 后端 `username` 为不匹配)
315
+ - [ ] 前端响应字段读取 vs 后端响应构造:字段名必须精确匹配
316
+ - [ ] 前端校验规则 vs 后端校验规则:同一字段的校验约束必须一致
317
+
318
+ > **强制约束**:
319
+ > - 若跨文件校验涉及的某个文件不在 `CHANGED_FILES` 中,**不得**将其作为差异项报告
320
+ > - 此类问题应归入"怀疑清单",标注为"⚠️ 该问题涉及非变更文件 `<文件路径>`,建议检查该文件是否属于当前变更"
321
+ > - 例如:后端 Controller 在 `CHANGED_FILES` 中,但前端 service 不在,则前后端字段不一致的问题不能报告为差异
322
+
323
+ #### 6.7 生成怀疑清单
324
+
325
+ 校验完成后,**必须**生成一份怀疑清单,列出以下潜在风险:
326
+
327
+ | 怀疑类型 | 说明 |
328
+ |---------|---------|
329
+ | Spec 歧义 | spec 中描述模糊、可能有多种理解的点 |
330
+ | 隐含假设 | spec 未明确定义但代码做了假设的点 |
331
+ | 边界遗漏 | spec 未覆盖但代码可能遇到的边界条件 |
332
+ | 命名不一致 | 前后端字段名、错误码命名风格差异 |
333
+ | 跨 Spec 矛盾 | 不同 spec 之间可能存在的定义冲突 |
334
+ | **非变更文件问题** | 校验中发现的问题涉及的文件不在 `CHANGED_FILES` 中(不属于当前变更范围),无法作为差异报告,需提醒用户检查该文件 |
335
+
336
+ 每条怀疑项标注风险等级(P0-P3)和建议验证方式。
337
+
338
+ > **非变更文件问题格式**:
339
+ > ```
340
+ > ⚠️ 该问题涉及非变更文件 `<文件路径>`,建议检查该文件是否属于当前变更
341
+ > 问题描述:<具体问题>
342
+ > 建议:若该文件属于当前变更,请将其加入变更范围后重新执行校验
343
+ > ```
344
+
345
+ ### 7. 输出校验报告
346
+
347
+ **所有场景(high/medium/low)都必须生成报告文件**,便于追溯和审计。
348
+
349
+ #### 7.1 生成 Markdown 报告
350
+
351
+ 将报告写入 md 文件,按审核模式区分文件名:
352
+
353
+ | 审核模式 | 报告路径 |
354
+ |---------|---------|
355
+ | 独立审核(默认) | `openspec/changes/<change-name>/consistency-report.md` |
356
+ | 自审模式(`--mode=self-review`) | `openspec/changes/<change-name>/consistency-report-self-review.md` |
357
+
358
+ #### 7.2 生成 JSON 结果文件(强制)
359
+
360
+ **必须同时生成 JSON 结果文件**,供 git hook 解析置信度进行拦截判定。
361
+
362
+ JSON 文件路径与 md 报告对应:
363
+
364
+ | 审核模式 | JSON 路径 |
365
+ |---------|---------|
366
+ | 独立审核(默认) | `openspec/changes/<change-name>/consistency-report-result.json` |
367
+ | 自审模式(`--mode=self-review`) | `openspec/changes/<change-name>/consistency-report-self-review-result.json` |
368
+
369
+ JSON 文件结构(必须严格遵循):
370
+
371
+ ```json
372
+ {
373
+ "changeName": "<change-name>",
374
+ "confidence": "high | medium | low",
375
+ "overallResult": "pass | warning | fail",
376
+ "reviewMode": "independent-review | self-review",
377
+ "generatedAt": "<YYYY-MM-DD HH:mm:ss>",
378
+ "repositories": [
379
+ { "name": "<仓库名或路径>", "path": "<仓库相对路径>", "changedFiles": 0 }
380
+ ],
381
+ "dimensions": {
382
+ "interfaceContract": { "status": "pass | warning | fail", "issues": 0 },
383
+ "businessRules": { "status": "pass | warning | fail", "issues": 0 },
384
+ "validationRules": { "status": "pass | warning | fail", "issues": 0 },
385
+ "errorCodes": { "status": "pass | warning | fail", "issues": 0 },
386
+ "frontendBackendConsistency": { "status": "pass | warning | fail", "issues": 0 }
387
+ },
388
+ "summary": {
389
+ "totalChecks": 0,
390
+ "passed": 0,
391
+ "failed": 0,
392
+ "warnings": 0
393
+ }
394
+ }
395
+ ```
396
+
397
+ **字段说明**:
398
+ - `confidence`:整体置信度,必须为 `high` / `medium` / `low` 之一(与 md 报告中的置信度一致)
399
+ - `overallResult`:总体结果,`pass`(high 置信度)/ `warning`(medium 置信度)/ `fail`(low 置信度)
400
+ - `reviewMode`:审核模式,`independent-review` 或 `self-review`
401
+ - `generatedAt`:生成时间,格式 `YYYY-MM-DD HH:mm:ss`
402
+ - `repositories`:参与校验的仓库列表(多仓库场景),每项包含仓库名、相对路径、变更文件数;单仓库场景仅包含根仓库
403
+ - `dimensions`:各维度校验结果
404
+ - `summary`:校验统计摘要
405
+
406
+ > **强制约束**:JSON 文件必须与 md 报告同时生成。缺少 JSON 文件时,git hook 将输出警告但仍放行(向后兼容)。
407
+
408
+ 报告模板:
409
+
410
+ > **时间格式强制约束**:`生成时间` 必须通过时间工具(如 MCP `get_current_time` 或系统命令 `date`)获取当前精确时间,格式为 `YYYY-MM-DD HH:mm:ss`。**禁止**仅填写日期而省略时分秒。
411
+
412
+ ```markdown
413
+ # Spec 一致性校验报告
414
+
415
+ **变更名称**:<change-name>
416
+ **生成时间**:<YYYY-MM-DD HH:mm:ss>(必须通过时间工具获取,禁止仅填日期)
417
+ **审核模式**:独立审核(independent-review)
418
+ **仓库结构**:<单仓库 / 多仓库(列出各仓库路径)>
419
+ **校验范围**:<diff 涉及的文件列表,多仓库场景下标注各文件所属仓库>
420
+
421
+ ## 后端接口契约
422
+
423
+ | 接口 | 路径 | 方法 | 请求参数 | 响应类型 | 响应字段 | HttpStatus |
424
+ |------|------|------|---------|---------|---------|------------|
425
+ | [接口名] | [路径] | [方法] | [DTO名] | [类型] | [字段列表] | [状态码] |
426
+
427
+ ### 后端契约校验
428
+
429
+ | 检查项 | 代码实际值 | Spec 期望值 | 状态 |
430
+ |--------|-----------|------------|------|
431
+ | [检查项] | [值] | [值] | ✅/❌ |
432
+
433
+ ## 前端接口契约
434
+
435
+ | 函数 | URL | 方法 | body 字段 | 正则表达式 | localStorage keys |
436
+ |------|-----|------|----------|-----------|------------------|
437
+ | [函数名] | [URL] | [方法] | [字段列表] | [正则] | [key列表] |
438
+
439
+ ### 前端契约校验
440
+
441
+ | 检查项 | 代码实际值 | Spec 期望值 | 状态 |
442
+ |--------|-----------|------------|------|
443
+ | [检查项] | [值] | [值] | ✅/❌ |
444
+
445
+ ## DTO 字段约束
446
+
447
+ | DTO | 字段 | 类型 | 约束 |
448
+ |-----|------|------|------|
449
+ | [DTO名] | [字段名] | [类型] | [注解/约束] |
450
+
451
+ ## 校验结果
452
+
453
+ | 维度 | 状态 | 详情 | 证据 |
454
+ |------|------|------|------|
455
+ | 接口契约 | ✅/⚠️/❌ | [具体问题] | [代码文件:行号] |
456
+ | 业务规则 | ✅/⚠️/❌ | [具体问题] | [代码文件:行号] |
457
+ | 校验规则 | ✅/⚠️/❌ | [具体问题] | [代码文件:行号] |
458
+ | 错误码 | ✅/⚠️/❌ | [具体问题] | [代码文件:行号] |
459
+ | 前后端一致性 | ✅/⚠️/❌ | [具体问题] | [代码文件:行号] |
460
+
461
+ **置信度**:high / medium / low
462
+
463
+ **总体结果**:✅ 通过 / ⚠️ 有警告 / ❌ 未通过
464
+
465
+ ## 代码差异
466
+
467
+ > 列出所有代码变更,标注是否符合 spec
468
+
469
+ ### <变更 1 标题>
470
+
471
+ **Spec 期望**(<spec 文件路径:行号>):
472
+ ```json
473
+ <spec 定义的响应结构>
474
+ ```
475
+
476
+ **实际实现**(<代码文件路径:行号>):
477
+ ```diff
478
+ - // spec 期望的代码
479
+ + // 实际实现的代码
480
+ ```
481
+
482
+ **校验结果**:✅ 符合 / ❌ 不符合
483
+
484
+ ## 怀疑清单
485
+
486
+ > 以下列出校验过程中发现的潜在风险,需要人工确认或进一步验证。
487
+
488
+ | # | 怀疑类型 | 风险等级 | 描述 | 建议验证方式 |
489
+ |---|---------|---------|------|-------------|
490
+ | 1 | Spec 歧义 | P2 | [描述] | [建议] |
491
+ | 2 | 隐含假设 | P1 | [描述] | [建议] |
492
+
493
+ ## 建议操作
494
+
495
+ - [具体修复建议及对应文件:行号]
496
+ ```
497
+
498
+ > **注意**:若使用自审模式(`--mode=self-review`),报告需额外包含"人工确认"章节:
499
+ >
500
+ > ```markdown
501
+ > ## 人工确认(自审模式)
502
+ >
503
+ > > ⚠️ 本报告为 AI 自审结果,以下关键断言需要人工确认:
504
+ >
505
+ > | # | 断言描述 | AI 判定 | 人工确认 |
506
+ > |---|---------|--------|---------|
507
+ > | 1 | [断言] | ✅/❌ | [待确认] |
508
+ >
509
+ > **人工确认方式**:
510
+ > - 逐条核对上述断言,在"人工确认"列填写 `✅ 已确认` 或 `❌ 有误`
511
+ > ```
512
+
513
+ 同时在对话中输出报告摘要。
514
+
515
+ ### 8. 【交互引导】根据结果引导下一步
516
+
517
+ **全部通过**:
518
+ > "✅ 代码符合 spec 规范!建议下一步:
519
+ > - A. 继续实施其他任务
520
+ > - B. 运行 `/opsx:test` 执行测试验证"
521
+
522
+ **有问题**:
523
+ > "❌ 发现 [N] 个不一致项需要修复:
524
+ > - 问题 1:[描述] → 建议修改 [文件:行号]
525
+ > - 问题 2:[描述] → 建议修改 [文件:行号]
526
+ >
527
+ > 请选择:
528
+ > - A. 逐个修复
529
+ > - B. 忽略警告继续"
530
+
531
+ ---
532
+
533
+ ## 判定标准
534
+
535
+ ### 一、维度内部判定
536
+
537
+ | 维度 | pass | warning | fail |
538
+ |------|------|---------|------|
539
+ | **接口契约** | 所有检查项 ✅ | 有 ⚠️ 但无 ❌ | 有任何 ❌ |
540
+ | **业务规则** | 所有规则实现 | 非关键规则缺失 | 关键规则缺失 |
541
+ | **校验规则** | 所有约束实现 | 非关键约束缺失 | 必填字段无校验 |
542
+ | **错误码** | 所有错误码实现 | 非关键错误码缺失 | 关键错误码缺失 |
543
+ | **前后端一致性** | 所有字段匹配 | 部分字段不匹配 | 核心字段不匹配 |
544
+
545
+ ### 二、整体置信度判定
546
+
547
+ ```
548
+ low(拒绝):
549
+ - 接口契约 = fail
550
+ - 或 业务规则 = fail(关键规则缺失)
551
+ - 或 校验规则 = fail(必填字段无校验)
552
+
553
+ medium(警告):
554
+ - 接口契约 ≠ fail
555
+ - 且 任一维度 = warning 或 fail(非关键)
556
+
557
+ high(通过):
558
+ - 所有维度 = pass
559
+ ```
560
+
561
+ ### 三、关键 vs 非关键 定义
562
+
563
+ | 维度 | 关键(→ low) | 非关键(→ medium) |
564
+ |------|--------------|-------------------|
565
+ | **业务规则** | 认证、授权、加密、核心业务流程 | 日志记录、审计、性能优化 |
566
+ | **校验规则** | 必填字段、安全相关约束(如密码强度) | 格式美化、长度限制 |
567
+ | **错误码** | 影响前端决策的错误码(如 401、403) | 提示性错误码(如参数格式提示) |
568
+ | **前后端一致性** | 核心字段(token、userId、username) | 次要字段(createdAt 格式、可选字段) |
569
+
570
+ ### 四、示例
571
+
572
+ | 场景 | 各维度状态 | 置信度 | 原因 |
573
+ |------|-----------|--------|------|
574
+ | 响应字段名错误 | 接口契约=fail | **low** | 接口契约一票否决 |
575
+ | 缺少密码复杂度校验 | 校验规则=fail | **low** | 安全相关约束缺失 |
576
+ | 缺少某个提示性错误码 | 错误码=warning | **medium** | 非关键错误码缺失 |
577
+ | 前端字段名与后端不一致 | 前后端一致性=warning | **medium** | 非核心字段不匹配 |
578
+ | 所有维度完美匹配 | 全部=pass | **high** | 完全符合 spec |
579
+
580
+ ---
581
+
582
+ ## Guardrails
583
+
584
+ - 本 Skill 是**只读检查**操作,不修改任何代码或 spec 文件
585
+ - 只校验与当前变更相关的代码,不对未变更代码做判断
586
+ - 如果 spec 中没有定义某个接口/规则,不误报为不匹配
587
+ - 对于 medium 和 low 的情况,必须给出具体的差异说明和改进建议
588
+ - 如果只涉及文档变更(无代码变更),直接返回 high
589
+ - **响应结构必须逐字段对比**,字段名必须精确匹配,不允许"大致匹配"
590
+ - **前后端字段名必须精确匹配**,不允许语义等价判断(如 `phone` ≠ `username`)
591
+ - **报告范围强制约束**:报告中的所有差异项只能引用 `CHANGED_FILES` 中的文件。若问题代码位于关联文件(`REFERENCE_FILES` 但不在 `CHANGED_FILES` 中),不得作为差异报告,只能放入怀疑清单并提示用户检查该文件是否属于当前变更
592
+ - **多仓库路径约束**:多仓库场景下,报告中的文件路径必须使用**工作区相对路径**(如 `sdd-demo2/src/main/java/.../UserController.java`),而非子仓库内的相对路径
@@ -27,3 +27,4 @@ description: "TDD 规则库 — DAG 生成规则、Controller 策略、任务类
27
27
  | `rules/multi-validation-split.md` | 多校验条件拆分规则(每个校验条件须有独立 AC 场景;AC 内"或"条件变体须全覆盖) | MEDIUM | opsx-spec, opsx-task, opsx-check |
28
28
  | `rules/exception-path-coverage.md` | 异常路径测试覆盖门禁(每个 orElseThrow/边界检查须有对应 RED 测试) | HIGH | opsx-task, opsx-apply, opsx-check |
29
29
  | `rules/refactor-checklist.md` | REFACTOR 阶段检查点(public API 不变、行为保持、重构质量评估) | HIGH | opsx-apply, tdd-core |
30
+ | `rules/tdd-rhythm-enforcement.md` | TDD 节奏强制规则(批量更新阻断、RED/GREEN 证据阻断、节奏间隔阻断、中途检查点) | CRITICAL | opsx-apply, sdd-tdd-rhythm-gate.cjs |