kld-sdd 2.7.8-2 → 2.7.8-3

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.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: opsx-consistency-check
3
- description: "Spec 双向一致性校验 - 验证代码实现与 spec 文档的双向一致性"
4
- argument-hint: "[change-name] [file...]"
3
+ description: "根据实际代码和 Spec 检查接口、业务规则与错误处理,按输入指纹复用检查回执"
4
+ argument-hint: "[change-name]"
5
5
  license: MIT
6
6
  metadata:
7
7
  author: sdd-team
8
- version: "3.0"
8
+ version: "4.0"
9
9
  allowed-tools:
10
10
  - Bash
11
11
  - Read
@@ -13,141 +13,63 @@ allowed-tools:
13
13
  - Edit
14
14
  ---
15
15
 
16
- > **路径约定**:`<skill-dir>` = 本文件所在目录;`<spec-root>` = spec 仓根目录(含 `skywalk-sdd/git-hooks/`)。辅助脚本:`<skill-dir>/scripts/scripts.cjs`;hook 同款采集器:`<spec-root>/skywalk-sdd/git-hooks/consistency-check-core.cjs`。
16
+ 检查实现是否满足本次业务约定,留下可复核的证据。`opsx-check` 检查文档及其关系;本技能检查代码与 Spec,两者不能互相替代。日常 Git 提交不要求用户手动运行本技能;Apply 在实施收尾时自动调用。
17
17
 
18
- 你是 SDD 一致性校验专家。任务只有一件事:**对比 diff 变更代码与 spec 的接口契约是否一致**(API 路径、HTTP 方法、请求参数、响应字段、错误码),生成 git hook 可消费的报告。除此之外的一切(业务规则深挖、关联文件扫描、多维度深度校验)都不做。
18
+ ## 1. 确定输入并尝试复用
19
19
 
20
- ## 硬约束(违反会导致 hook 死循环或误拦,不可协商)
21
-
22
- 1. **采集必须用 hook 同款命令**(步骤 1),禁止手工拼 git diff——两边采集不一致会导致报告盖不住 diff → hook 拦截 → 重新生成仍被拦的死循环
23
- 2. **coveredFiles 必须与实际校验的变更文件完全一致**:path 为工作区相对路径,size/sha256 直接复用采集器输出(LF 归一化已内置),禁止估算、禁止用平台命令重算
24
- 3. **confidence 只能 `high`/`medium`/`low`**(其他值被 hook 判非法拦截);**generatedAt 必须本机本地时间** `YYYY-MM-DD HH:mm:ss`(禁止 UTC,hook 按本地时区做过期判断)
25
- 4. **md + json 成对写入** `openspec/changes/<change-name>/`,单边存在会被判报告不完整并拦截
26
- 5. **只读变更文件本身**,不扩展读 DTO/Model/配置/SQL 等关联文件;缺证据标"待人工核查",不中断补读
27
- 6. **无业务代码变更 → 不生成报告**,输出一句提示即结束(hook 对无业务代码提交直接放行)
28
- 7. 生成前必须先删旧报告(步骤 2c 已内置),全量重新校验,不读旧报告做增量复用
29
- 8. **多仓布局下 `repositories` 必填**:工作区根存在 `.sdd-workspace.yaml` 时,JSON 报告必须含 `repositories` 数组,`path` 为该 change 覆盖的代码仓名(与 `.sdd-workspace.yaml` 的 `code_repos` 条目一致)。单仓布局可省略或留空数组。hook 用此字段剥离 coveredFiles 路径前缀以对齐 diff 路径,缺失会导致路径不匹配 → 误判"无交集" → 报告形同虚设
30
-
31
- ## 执行流程(4 步)
32
-
33
- ### 1. 采集变更文件(脚本,秒级)
34
-
35
- > **禁止环境预探测**:不要 Test-Path 探测 git 仓、不要列目录确认脚本存在——采集器自动处理单仓/多仓布局(多仓读 `.sdd-workspace.yaml` 的 code_repos 逐仓 diff),命令失败自会报错。首轮回复直接并行发起:① 采集命令 ② `list-changes`(两者无依赖)。
20
+ 从当前实施上下文取得明确的 Change 和代码仓。多个活动 Change 不等于本次都要检查;不要按分支名猜测。多仓逐个实际涉及的代码仓运行,使用同一个 Spec 根和 Change;回执按仓库身份分别存储,互不覆盖。
36
21
 
37
22
  ```bash
38
- node <spec-root>/skywalk-sdd/git-hooks/consistency-check-core.cjs --collect --business-only --hash --project=<工作区根或代码仓根>
39
- node <skill-dir>/scripts/scripts.cjs list-changes
23
+ node "<spec-root>/skywalk-sdd/git-hooks/delivery-check.cjs" prepare --project="<代码仓>" --change=<change> --base=<实施前提交SHA>
40
24
  ```
41
25
 
42
- 输出 `businessFiles`(对象数组,含 path/size/sha256,机械筛选已内置)+ `deletedFiles`。命令失败(退出码非 0)= git diff 采集失败,如实报错终止,禁止降级为"无变更"。
43
-
44
- - **无业务文件** → 提示"无代码变更,跳过校验"并结束
45
- - **有业务文件但无活跃 change** → 报错终止,提示先执行 opsx-propose
46
- - **多 change 且未指定 change-name** → 按 spec 覆盖范围把文件归属到各 change,每个 change 独立出报告;同一文件被多个 change 归属时只在首个 change 完整校验、其余复用结论(coveredFiles 仍完整包含)。**单 change 或已指定 change-name** → 全部文件归入,跳过归属逻辑
47
- - **用户指定了具体文件** → 交给 `consistency-check-core.cjs --filter` 机械筛选,哈希用 `prepare-report --files-from=<列表文件>` 补算
48
-
49
- ### 2. 读上下文(a/b/c 同一回复并行发起,禁止分批等待)
50
-
51
- a. **定位并读 spec**:`node <skill-dir>/scripts/scripts.cjs find-specs <change-name>` 拿文件列表,**禁止全量读**(近百个 spec 全读既慢又撑爆上下文)。API 路径由 AI 从已读 Controller 原生提取(任何语言的路由写法都直接认识,禁止用正则 grep 代码提取),再用**字面量片段**(如 `/api/user`;禁止含 `|`、`"`、`\`、`()` 的正则——Windows cmd 会拆解管道符与引号导致 0 命中)grep spec 目录:
52
-
53
- ```bash
54
- node <skill-dir>/scripts/scripts.cjs grep-code "/api/user" <openspec-dir> --glob=spec.md -i
55
- ```
56
-
57
- **grep 省略条件(省一轮往返)**:`find-specs` 命中的变更级 spec 文件总数 ≤ 5 个时,跳过 grep 直接读这些文件(变更级 spec 本来就是该 change 的契约文档,命中概率极高);命中 > 5 个或需在全局 spec 中定位时才 grep。只读命中文件;命中 0 → 回退读变更级 spec(`changes/<name>/specs/`),仍不读全局 spec 全集。
58
-
59
- b. **读代码(按契约角色分层,同一回复并行发起)**:首轮把全部变更文件按契约角色分三层,**全文 Read 与 grep 混排在同一回复一次发齐**(不产生额外往返;禁止的是"grep 定位后再补读"的两段式,不是 grep 本身):
60
- - **契约核心(全文 Read)**:Controller、DTO/VO/QueryDTO、全局异常处理器(`*Controller`、`*DTO`、`*VO`、`*ExceptionHandler` 等,按语言现场识别)——接口契约的直接载体,必须逐字段比对
61
- - **契约相关(grep 取证,不读全文)**:Service/ServiceImpl、Repository——只 grep 方法签名 + 响应构造 + 错误码,模式按语言现场定(Java `return`/`Map.of`、Python `return {`/`jsonify`、Go `c.JSON`)
62
- - **无契约面(grep 确认后跳过)**:Application 启动类、`*Config`/`*Properties`、`*Util`、`BusinessException`、`*Mapper` 接口等——grep 端点声明(路由注解/路径常量)+ 业务错误码字面量,**零命中 → 结论"无契约面",不进入步骤 3 逐字段比对**;有命中 → 升级按上一条 grep 取证处理
63
- - **前端**(.ts/.tsx/.vue/.js):只 grep API 调用路径 + HTTP 方法,与 spec 路径比对
64
- - **coveredFiles 不受分层影响**:全部变更文件照常列入报告(硬约束 2),分层只决定"读多深",不决定"盖不盖"
65
- - **例外**:契约核心文件单文件 ≥ 800 行时,先 grep 定位端点区域,再分段读相关区间
26
+ `--base` 优先使用 Apply 的实施前基线;首次新仓无 HEAD 时可省略,由工具选择空树。后续命令复用 prepare 保存的基线。默认读取工作区;显式检查暂存内容时加 `--scope=index`,不能把工作区内容当作暂存内容。
66
27
 
67
- c. **prepare-report**:`node <skill-dir>/scripts/scripts.cjs prepare-report openspec/changes/<change-name>` —— 清理旧报告 + 输出本地时间戳(`timestamp` 直接作为 generatedAt;该命令绝不读 stdin,不会挂起)。
28
+ 输出包括 `requiredFiles`、`inputs`、`inputFingerprint`、`preparedFile`、`reviewFile`、`reuseReview`。快照同时绑定代码、Spec、仓库身份与规则版本。指纹相同且覆盖完整时,直接复用,不删除旧报告、不重新调用模型。旧版没有输入指纹的报告需升级生成一次。
68
29
 
69
- > **报告落点(固定,无需确认)**:报告必须写在 **spec 根**下的 `openspec/changes/<change-name>/`(hook 的 `findReportJson` 只在 `<spec-root>/openspec/changes/<name>/` 找报告,写到别处 = 报告缺失 = 拦截)。`prepare-report` 已自动按 spec 根解析相对路径,输出 JSON 的 **`changeDir` 字段就是解析后的实际目录**——写报告直接用它拼接文件名,禁止自行按 cwd 推导路径,也无需读脚本源码确认。coveredFiles.path 由采集器原样输出(多仓布局带仓前缀如 `user-info-mgmt/src/...`)。hook 在代码仓根运行、diff 路径无前缀,靠 `repositories[].path` 剥离 coveredFiles 前缀才能对齐——故多仓布局下 `repositories` 必填(见硬约束 8)。
30
+ 当前版本保守地对本仓跟踪文件及非忽略的未跟踪文件取指纹(排除生成目录、SDD 运行时),并包含本 Change 与全局规格;它不是跨仓依赖的自动发现器。检查需要其他仓的依赖时,应在该仓单独验证,并在交付摘要列出其提交。
70
31
 
71
- > **子代理分派规则(按活跃 change 数机械判定,禁止裁量)**:
72
- > - **多 change(≥2 个)→ 必须并行**:每个 change 一个子代理,同一回复全部分派(上限 2 个,两 change 恰好一批)。**禁止**主线程逐 change 串行校验,**禁止**以"宿主并发能力不确定"为由跳过——派发在最坏情况(宿主排队串行)下也只是无收益,结果照常回收,无损害;实测多 change 串行是步骤 3 最大耗时来源(两仓各 5 分钟级推理串行叠加),并行后总耗时取单 change 最大值
73
- > - **单 change → 按文件量分组**(仅适用于恰好 1 个活跃 change 的场景,不得套用到多 change 的单个 change 上):≤ 15 个文件主线程直接校验(无需子代理);> 15 个按 8-10 个/组分派子代理,逐组执行
74
- > - **禁止**:主线程自己串行逐文件校验(36 文件 = 36 轮往返,是最大耗时来源);一次回复分派超过 2 个子代理(并发过高导致宿主排队 + 结果交错,实际更慢)
75
- > - **派发提示必须自带分层读取规则**:子代理不读 SKILL.md,只知派发提示里写的内容——分派时必须把步骤 2b 的三层策略(契约核心全文读 / Service·Repository grep 取证 / 无契约面 grep 确认后跳过)原文写入提示。漏写则子代理退回全量读全文(实测发生:子代理全文读 ServiceImpl/Mapper/Config,上下文膨胀拖慢推理与汇总生成)
76
- > - 每个子代理完成"读 spec 命中集 + 按契约角色分层读本组代码 + 步骤 3 校验",返回差异清单 + 计数,主线程汇总生成报告;公共文件只在首个归属 change 的子代理中完整校验,其余复用
32
+ ## 2. 定向取证
77
33
 
78
- ### 3. 校验(AI 推理,唯一核心工作)
34
+ `reuseReview=false` 时,读取 `requiredFiles` 对应的业务实现和相关 Spec。可以比较旧回执与新 `inputs` 确定变化位置,但不能把失效的旧结论直接认作本轮通过。
79
35
 
80
- 对每个变更文件,对照 spec 回答一个问题:**接口契约是否一致?**
36
+ - 接口:路径、方法、请求与响应字段、错误码逐项对照。
37
+ - 业务规则:本次验收行为、边界条件、状态变化是否实现。
38
+ - 校验规则:必要的参数校验、拒绝条件是否与约定相符。
39
+ - 错误处理:失败路径是否满足调用方约定。
81
40
 
82
- **检查项(双向)**:
83
- - **spec → code**:spec 定义的路径/方法/参数/响应字段/错误码,代码是否实现且一致
84
- - **code → spec**:代码中的端点、响应字段 key、业务错误码是否已在 spec 记录(**仅枚举业务契约面**;工具方法、日志、防御性代码、DTO 转换等纯技术实现不枚举、不降级)
41
+ 先读变更文件与对应规格,再按实际引用补读 DTO、配置、SQL 或调用方。一次批量读取独立文件;不为凑检查项全量扫描全文,也不禁止必要的依赖阅读。删除文件同样需要核对受影响的契约。
85
42
 
86
- **要求**:
87
- - 响应结构**逐字段**对比,字段名精确匹配(`token` ≠ `accessToken`);`Map.of()`/字典/结构体构造必须提取所有 key 逐一比对
88
- - 每条差异定位到 `文件:行号 + spec 章节`,不贴代码原文片段
89
- - 找不到证据的标 ❌/⚠️,不假设一致
43
+ 每个结论记录 `代码路径:行号 / Spec路径:章节`。缺依据使用 `not_checked`;有实际冲突使用 `fail`;无该维度使用 `na` 并给出理由。不用 high confidence 代替事实,不把未检查维度固定写成 pass。
90
44
 
91
- **置信度判定**:
92
- - **high**:无差异,或仅技术实现未记录
93
- - **medium**:辅助性差异(非关键端点/字段缺失或不一致)
94
- - **low**:关键契约不一致、spec 有关键功能代码没有、代码有业务功能 spec 没有
45
+ ## 3. 保存真实检查结果
95
46
 
96
- ### 4. 生成报告(纯模板填充,零推理)
97
-
98
- 结论确定后直接写文件,**禁止**重新核对证据或推演差异。md + json 在**同一回复并行写入**(多 change 的全部报告同批写)。**禁止任何计时打点**(步骤起止时长、执行统计、额外的 `now` 调用)——报告 `generatedAt` 已由步骤 2c 的 prepare-report 输出,流程内不需要其他时间戳;执行时长统计只在用户显式要求时做,默认不做。
99
-
100
- **JSON**(`consistency-report-result.json`,hook 唯一消费源,schema 固定):
47
+ 把检查结果写到 `preparedFile` 同目录的 `review-input.json`,避免临时结果进入代码快照。`inputFingerprint` 原样复制,`reviewedFiles` 只列实际检查过的文件,至少覆盖本次 `requiredFiles`。
101
48
 
102
49
  ```json
103
50
  {
104
- "changeName": "<change-name>",
105
- "confidence": "high | medium | low",
106
- "generatedAt": "<YYYY-MM-DD HH:mm:ss 本地时间>",
107
- "repositories": [
108
- { "path": "<代码仓名>" }
109
- ],
110
- "coveredFiles": [
111
- { "path": "<工作区相对路径>", "size": 2341, "sha256": "<64位十六进制>" }
112
- ],
51
+ "inputFingerprint": "<prepare 输出>",
52
+ "reviewedFiles": ["src/order.js"],
113
53
  "dimensions": {
114
- "interfaceContract": { "status": "pass | warning | fail", "issues": 0 },
115
- "businessRules": { "status": "pass", "issues": 0 },
116
- "validationRules": { "status": "pass", "issues": 0 },
117
- "errorCodes": { "status": "pass | warning | fail", "issues": 0 }
54
+ "interfaceContract": {"status": "pass", "evidence": ["src/order.js:12 / spec.md §接口"]},
55
+ "businessRules": {"status": "pass", "evidence": ["src/order.js:28 / spec.md §金额边界"]},
56
+ "validationRules": {"status": "not_checked", "evidence": []},
57
+ "errorCodes": {"status": "na", "reason": "纯计算函数,本次无错误码协议"}
118
58
  },
119
- "summary": {
120
- "totalChecks": 0, "passed": 0, "failed": 0, "warnings": 0,
121
- "undocumentedBusiness": 0, "undocumentedTechnical": 0, "specMissing": 0, "inconsistent": 0
122
- }
59
+ "issues": []
123
60
  }
124
61
  ```
125
62
 
126
- `businessRules`/`validationRules` 固定 `pass:0`(简化校验不做深度检查);`summary` 按差异清单逐条归类(一条差异只计一次),`failed + warnings` 与各维度 `issues` 合计一致。
127
-
128
- **MD**(`consistency-report.md`,极简,禁止凑数空章节):
129
-
130
- ```markdown
131
- # Spec 一致性校验报告
132
-
133
- **变更**:<change-name> **生成时间**:<YYYY-MM-DD HH:mm:ss>
134
- **校验范围**:<N 个文件> **置信度**:<high/medium/low>
63
+ 只有无业务代码变化且四个维度都有理由判为 `na` 的文档或非实施变更,才可在回执中追加 `tests: {"status":"na","reason":"具体不适用理由"}`;工具显示测试不适用,不能说测试已通过。
135
64
 
136
- ## 结果
65
+ 该例包含未检查项,不能通过交付验证。依据实际取证补完,不能只改状态。
137
66
 
138
- <全部通过时只写一行:接口契约与错误码全部一致,无差异。>
139
-
140
- <有差异时列下表:>
141
-
142
- | # | 类型 | 风险 | 位置 | 描述 | 建议 |
143
- |---|------|------|------|------|------|
144
- | 1 | 不一致 | P0 | File.java:42 / spec §2.1 | <差异描述> | <改代码或改 spec> |
67
+ ```bash
68
+ node "<spec-root>/skywalk-sdd/git-hooks/delivery-check.cjs" record-review --project="<代码仓>" --change=<change> --input="<review-input.json>"
145
69
  ```
146
70
 
147
- 类型四选一:`代码有 spec 没有(业务)` / `代码有 spec 没有(技术)` / `spec 有代码没有` / `不一致`。
148
-
149
- ---
71
+ 工具重算指纹,输入变化时拒绝旧结论。失败或未检查的结果仍会保存并返回非零退出码,便于修复后复核;成功回执写到输出 `reviewFile`,由脚本管理哈希,不手填。回执是 Agent 的可追溯检查记录,不是对业务正确性的数学证明。
150
72
 
151
- ## 渐进披露
73
+ ## 4. 返回实施收尾
152
74
 
153
- - Read `references/reference.md` 仅在遇到置信度摘要未覆盖的边界案例时(如技术实现与业务实现难以区分、关键性拿不准)— **禁止预防性读取**(主文档摘要已覆盖 90% 场景);含问题分类、置信度判定规则、关键性判断标准、维度判定参考、示例
75
+ 报告发现的问题、检查范围与未检查部分。由 Apply 在同一轮继续执行真实测试及 `delivery-check verify`;本技能通过只表示检查回执有效,不表示测试、知识库入库或正式交付已经完成。
@@ -439,21 +439,8 @@ function prepareReport(changeDir) {
439
439
  const specRoot = resolveSpecRoot();
440
440
  if (specRoot) dir = path.resolve(specRoot, changeDir);
441
441
  }
442
- // 1. 清理旧报告(失败不静默:残留旧报告会导致后续 Write 要求先 Read)
443
- const reportFiles = ['consistency-report.md', 'consistency-report-result.json'];
442
+ // Keep previous evidence for comparison/reuse. Explicit clean-reports remains available.
444
443
  const cleaned = [];
445
- for (const f of reportFiles) {
446
- const fp = path.join(dir, f);
447
- try {
448
- if (fs.existsSync(fp)) {
449
- fs.unlinkSync(fp);
450
- cleaned.push(fp.replace(/\\/g, '/'));
451
- }
452
- } catch (e) {
453
- console.error('ERROR: failed to delete ' + fp + ': ' + e.message);
454
- process.exit(1);
455
- }
456
- }
457
444
  // 2. 批量哈希(可选):仅当显式提供 --files-from=/--files= 时执行,绝不读 stdin(防交互式终端挂起)。
458
445
  // 无输入源 = "只清理 + 取时间戳"合法模式(哈希已由 --collect --hash 提供时无需重复传入),hashes 输出空数组
459
446
  const hashes = [];