kld-sdd 2.7.8-1 → 2.7.8-2

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.
@@ -49,7 +49,7 @@ function parseActiveChangesFromYaml(content) {
49
49
  for (const raw of content.split(/\r?\n/)) {
50
50
  const line = raw.replace(/#.*$/, '').replace(/\s+$/, '');
51
51
  if (!line.trim()) continue;
52
- if (/^active_changes:\s*(\[\s*\])?\s*$/.test(line)) { inActive = true; continue; }
52
+ if (/^active_changes:\s*(\[\s*])?\s*$/.test(line)) { inActive = true; continue; }
53
53
  if (inActive) {
54
54
  const m = /^\s+-\s+change-key:\s*(\S+)/.exec(line);
55
55
  if (m) keys.push(m[1]);
@@ -144,17 +144,31 @@ function validateReportFields(report) {
144
144
  // --- 变更文件采集 ---
145
145
 
146
146
  /**
147
- * 简易 YAML 解析器:仅提取 code_repos 列表
147
+ * 简易 YAML 解析器:仅提取 code_repos 列表。
148
+ * 逐行状态机解析(无正则量词嵌套),天然免疫 ReDoS(Sonar S5852)。
149
+ * 容忍条目间注释、行尾注释;遇到下一个顶层 key(非缩进行)即停止。
148
150
  */
149
151
  function parseCodeRepos(yamlPath) {
150
152
  try {
151
153
  const content = fs.readFileSync(yamlPath, 'utf8');
152
- const match = content.match(/code_repos:\s*\n((?:[ \t]*(?:#[^\n]*\n)?[ \t]*-[ \t]*\S+[ \t]*(?:#[^\n]*)?\n?)+)/);
153
- if (!match) return null;
154
- return match[1]
155
- .split('\n')
156
- .map(line => line.replace(/#.*$/, '').replace(/^\s+-\s+/, '').trim())
157
- .filter(Boolean);
154
+ const repos = [];
155
+ let inList = false;
156
+ for (const raw of content.split(/\r?\n/)) {
157
+ const line = raw.replace(/#.*$/, '').replace(/\s+$/, '');
158
+ if (!line.trim()) continue;
159
+ if (!/^\s/.test(line)) {
160
+ // 顶层 key:进入 code_repos 列表 / 离开列表
161
+ inList = /^code_repos:\s*$/.test(line);
162
+ continue;
163
+ }
164
+ if (!inList) continue;
165
+ const item = line.trim();
166
+ if (item.startsWith('- ')) {
167
+ const value = item.slice(2).trim();
168
+ if (value) repos.push(value);
169
+ }
170
+ }
171
+ return repos.length > 0 ? repos : null;
158
172
  } catch {
159
173
  return null;
160
174
  }
@@ -185,10 +199,8 @@ function isTrivialDiffLine(line) {
185
199
  if (trimmed.startsWith('#')) {
186
200
  // 排除 C/C++ 预处理指令
187
201
  const rest = trimmed.slice(1).trimStart();
188
- if (/^(include|define|ifdef|ifndef|endif|else|elif|undef|pragma|error|warning|line|region|endregion)\b/.test(rest)) {
189
- return false;
190
- }
191
- return true; // Python/Shell/YAML 注释
202
+ return !/^(include|define|ifdef|ifndef|endif|else|elif|undef|pragma|error|warning|line|region|endregion)\b/.test(rest);
203
+ // Python/Shell/YAML 注释
192
204
  }
193
205
  if (trimmed.startsWith('/*') || trimmed.endsWith('*/')) return true; // 块注释开始/结束
194
206
  if (trimmed.startsWith('*')) return true; // 块注释续行 (* ... 或 ** ...)
@@ -252,7 +264,7 @@ function parseDiffForTrivialFiles(diffOutput) {
252
264
  if (line.startsWith('-') && !line.startsWith('---')) {
253
265
  hasAnyChange = true;
254
266
  if (!isTrivialDiffLine(line.slice(1))) hasSubstantive = true;
255
- continue;
267
+
256
268
  }
257
269
  }
258
270
  if (currentFile !== null && hasAnyChange && !hasSubstantive) {
@@ -413,7 +425,7 @@ function parseConsistencySection() {
413
425
  let inSection = false;
414
426
  for (const raw of content.split(/\r?\n/)) {
415
427
  const line = raw.trim();
416
- if (/^\[.+\]$/.test(line)) {
428
+ if (/^\[.+]$/.test(line)) {
417
429
  if (inSection) break;
418
430
  inSection = line.toLowerCase() === '[consistency]';
419
431
  continue;
@@ -493,9 +505,9 @@ function isBusinessCode(file) {
493
505
  if (/(^|\/)(__tests__|tests?|e2e|cypress|fixtures|mocks?)(\/|$)/i.test(normalized)) return false;
494
506
  if (/(^|\/)[^/]*(?:\.test|\.spec|_test)\.[a-z]+$/i.test(normalized)) return false;
495
507
  if (TOOLCHAIN_CONFIG_RE.test(base)) return false;
496
- if (DOTFILE_CONFIG_RE.test(base)) return false;
508
+ return !DOTFILE_CONFIG_RE.test(base);
509
+
497
510
 
498
- return true;
499
511
  }
500
512
 
501
513
  // --- 路径归一化:报告 coveredFiles → 代码仓相对 ---
@@ -968,7 +980,7 @@ function runConsistencyCheck(opts) {
968
980
  function runFilterMode() {
969
981
  const filesArg = readArg('files');
970
982
 
971
- let input = [];
983
+ let input;
972
984
  if (filesArg) {
973
985
  if (filesArg.startsWith('@')) {
974
986
  input = fs.readFileSync(filesArg.slice(1), 'utf8').split(/\r?\n/);
@@ -1021,11 +1033,9 @@ function runCollectMode() {
1021
1033
  const businessFiles = files.filter(isBusinessCode);
1022
1034
  const excluded = files.filter(f => !isBusinessCode(f));
1023
1035
 
1024
- let hashedBusiness = null;
1025
- let unreadableFiles = null;
1036
+ const hashedBusiness = [];
1037
+ const unreadableFiles = [];
1026
1038
  if (withHash) {
1027
- hashedBusiness = [];
1028
- unreadableFiles = [];
1029
1039
  for (const rel of businessFiles) {
1030
1040
  const content = readWorkingFile(path.join(projectRoot, rel));
1031
1041
  if (content === null) {
@@ -10,13 +10,24 @@ git_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
10
10
  exit 1
11
11
  }
12
12
  hooks_dir=""
13
+ # 统一反斜杠为正斜杠,保证 Windows 盘符路径(P:\a\b → P:/a/b)能被 case 判定为绝对路径
14
+ normalize_path() {
15
+ printf '%s' "$1" | tr '\\' '/'
16
+ }
17
+ # 绝对路径(/ 开头或盘符 X: 开头)直接使用;相对路径基于 git 根拼接
18
+ resolve_spec_dir() {
19
+ raw=$(normalize_path "$1")
20
+ case "$raw" in
21
+ /*|[A-Za-z]:*) printf '%s' "$raw" ;;
22
+ *) printf '%s' "$git_root/$raw" ;;
23
+ esac
24
+ }
13
25
  spec_path=$(git -C "$git_root" config --local sdd.specPath 2>/dev/null)
14
26
  if [ -n "$spec_path" ]; then
15
- case "$spec_path" in /*) ;; *) spec_path="$git_root/$spec_path" ;; esac
16
- hooks_dir="$spec_path/skywalk-sdd/git-hooks"
27
+ hooks_dir="$(resolve_spec_dir "$spec_path")/skywalk-sdd/git-hooks"
17
28
  elif [ -f "$git_root/.sdd-spec-root" ]; then
18
29
  spec_rel=$(head -1 "$git_root/.sdd-spec-root" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
19
- case "$spec_rel" in /*) hooks_dir="$spec_rel/skywalk-sdd/git-hooks" ;; *) hooks_dir="$git_root/$spec_rel/skywalk-sdd/git-hooks" ;; esac
30
+ hooks_dir="$(resolve_spec_dir "$spec_rel")/skywalk-sdd/git-hooks"
20
31
  fi
21
32
  if [ -n "$hooks_dir" ] && [ ! -d "$hooks_dir" ]; then
22
33
  echo "[SDD] 已配置的 Spec 路径失效:$hooks_dir"
@@ -27,12 +38,15 @@ fi
27
38
  # 方法3: 从 git 根向上搜索 skywalk-sdd/git-hooks(spec 仓本身或父级)
28
39
  if [ -z "$hooks_dir" ]; then
29
40
  search_dir="$git_root"
30
- while [ "$search_dir" != "/" ] && [ "$search_dir" != "" ]; do
41
+ while [ -n "$search_dir" ] && [ "$search_dir" != "/" ]; do
31
42
  if [ -d "$search_dir/skywalk-sdd/git-hooks" ]; then
32
43
  hooks_dir="$search_dir/skywalk-sdd/git-hooks"
33
44
  break
34
45
  fi
35
- search_dir="$(dirname "$search_dir")"
46
+ # Windows 盘符根(P:/)dirname 后不变,需显式收敛,避免死循环
47
+ parent_dir="$(dirname "$search_dir")"
48
+ [ "$parent_dir" = "$search_dir" ] && break
49
+ search_dir="$parent_dir"
36
50
  done
37
51
  fi
38
52
 
@@ -10,13 +10,24 @@ git_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
10
10
  exit 1
11
11
  }
12
12
  hooks_dir=""
13
+ # 统一反斜杠为正斜杠,保证 Windows 盘符路径(P:\a\b → P:/a/b)能被 case 判定为绝对路径
14
+ normalize_path() {
15
+ printf '%s' "$1" | tr '\\' '/'
16
+ }
17
+ # 绝对路径(/ 开头或盘符 X: 开头)直接使用;相对路径基于 git 根拼接
18
+ resolve_spec_dir() {
19
+ raw=$(normalize_path "$1")
20
+ case "$raw" in
21
+ /*|[A-Za-z]:*) printf '%s' "$raw" ;;
22
+ *) printf '%s' "$git_root/$raw" ;;
23
+ esac
24
+ }
13
25
  spec_path=$(git -C "$git_root" config --local sdd.specPath 2>/dev/null)
14
26
  if [ -n "$spec_path" ]; then
15
- case "$spec_path" in /*) ;; *) spec_path="$git_root/$spec_path" ;; esac
16
- hooks_dir="$spec_path/skywalk-sdd/git-hooks"
27
+ hooks_dir="$(resolve_spec_dir "$spec_path")/skywalk-sdd/git-hooks"
17
28
  elif [ -f "$git_root/.sdd-spec-root" ]; then
18
29
  spec_rel=$(head -1 "$git_root/.sdd-spec-root" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
19
- case "$spec_rel" in /*) hooks_dir="$spec_rel/skywalk-sdd/git-hooks" ;; *) hooks_dir="$git_root/$spec_rel/skywalk-sdd/git-hooks" ;; esac
30
+ hooks_dir="$(resolve_spec_dir "$spec_rel")/skywalk-sdd/git-hooks"
20
31
  fi
21
32
  if [ -n "$hooks_dir" ] && [ ! -d "$hooks_dir" ]; then
22
33
  echo "[SDD] 已配置的 Spec 路径失效:$hooks_dir"
@@ -27,12 +38,15 @@ fi
27
38
  # 方法3: 从 git 根向上搜索 skywalk-sdd/git-hooks(spec 仓本身或父级)
28
39
  if [ -z "$hooks_dir" ]; then
29
40
  search_dir="$git_root"
30
- while [ "$search_dir" != "/" ] && [ "$search_dir" != "" ]; do
41
+ while [ -n "$search_dir" ] && [ "$search_dir" != "/" ]; do
31
42
  if [ -d "$search_dir/skywalk-sdd/git-hooks" ]; then
32
43
  hooks_dir="$search_dir/skywalk-sdd/git-hooks"
33
44
  break
34
45
  fi
35
- search_dir="$(dirname "$search_dir")"
46
+ # Windows 盘符根(P:/)dirname 后不变,需显式收敛,避免死循环
47
+ parent_dir="$(dirname "$search_dir")"
48
+ [ "$parent_dir" = "$search_dir" ] && break
49
+ search_dir="$parent_dir"
36
50
  done
37
51
  fi
38
52
 
@@ -171,8 +171,8 @@ function recordTelemetry(projectRoot, activeStage, record, result, exitCodeKnown
171
171
  skipped: counts.skipped,
172
172
  } : {}),
173
173
  duration_ms: Number.isFinite(toolResponse.duration_ms) ? toolResponse.duration_ms : 0,
174
- failure_type: result === 'failure' ? (exitCode == null ? 'infrastructure' : 'assertion') : 'none',
175
- expected_failure: tddPhase === 'red' && result === 'failure' && exitCode != null,
174
+ failure_type: result === 'failure' ? (exitCode != null && /ERR_ASSERTION|AssertionError|AssertionFailedError/.test(output) ? 'assertion' : 'infrastructure') : 'none',
175
+ expected_failure: tddPhase === 'red' && result === 'failure' && exitCode != null && /ERR_ASSERTION|AssertionError|AssertionFailedError/.test(output),
176
176
  };
177
177
  strictTestEvent = exitCode != null && Boolean(activeStage.task_id);
178
178
  runId = stableRunId(input || {}, activeStage, details.test_results.command);
@@ -160,8 +160,8 @@ function recordTelemetry(projectRoot, activeStage, record, result, exitCodeKnown
160
160
  skipped: counts.skipped,
161
161
  } : {}),
162
162
  duration_ms: Number.isFinite(toolResponse.duration_ms) ? toolResponse.duration_ms : 0,
163
- failure_type: result === 'failure' ? (exitCode == null ? 'infrastructure' : 'assertion') : 'none',
164
- expected_failure: tddPhase === 'red' && result === 'failure' && exitCode != null,
163
+ failure_type: result === 'failure' ? (exitCode != null && /ERR_ASSERTION|AssertionError|AssertionFailedError/.test(output) ? 'assertion' : 'infrastructure') : 'none',
164
+ expected_failure: tddPhase === 'red' && result === 'failure' && exitCode != null && /ERR_ASSERTION|AssertionError|AssertionFailedError/.test(output),
165
165
  };
166
166
  strictTestEvent = exitCode != null && Boolean(activeStage.task_id);
167
167
  runId = stableRunId(input || {}, activeStage, details.test_results.command);
@@ -40,6 +40,7 @@ allowed-tools:
40
40
  > - 较大的 `--details-json` 负载可先写入文件,再通过 `--details-file="$(cat .sdd-spec-root)/skywalk-sdd/state/<变更名称>-<type>.json"` 传递(例如 `task_update` 对应 `<变更名称>-task-update.json`)。
41
41
  > - `task_update` 记录成功后会自动调用 `check-task` 更新 `tasks.md` 中的 checkbox;录入成功后,agent 可以通过 `check-task` 命令验证该 checkbox 已更新。
42
42
  > - **⚠️ P1-2 ai_adoption_review 必填 ai_diff**:记录 AI 产出快照时,`--details-json` 必须含 `ai_diff.files_changed`(即使=0)与 `ai_diff.files`(产出文件路径数组,非空),**不得只发 `assertions`**。`vcs_mode=readonly` 时 `ai_diff.added_lines` 不得为 null——用只读 `git diff --numstat HEAD` 取值(apply Git 只读策略允许);`vcs_mode=no-git` 时可填 null。工具侧已加记录时硬校验:`files` 空数组或 `readonly` 下 `added_lines=null` 将 **拒绝记录**(与 conformance_review 校验对称)。缺失 `ai_diff` 会导致 report 变更文件数为 null(工具侧已加 `assertions[].files` 兜底,但 `ai_diff` 是主数据源)。完整模板见 `./reference.md`「§5.1」。
43
+ > - **默认测试入口**:使用 reference.md 中的 test-run 执行、采集和关联真实证据;可用 completion-file 同步已验证任务。不手填退出码、测试计数和耗时。
43
44
  > - **严格证据链**:一次真实测试执行只写一条严格 `test_result`;任务完成事件只通过 `test_event_id` 引用成功的 green/refactor/regression 测试,并明确 `tdd_required=true/false`,不复制测试计数。测试不适用时必须写明原因。完整 schema 见 `./reference.md`。
44
45
  > - **任务涉及文件必记**:每条严格 `task_update` 必须在 `task_update.files` 写本任务实际修改的仓库相对路径;本任务确实没有改文件时,改填具体的 `no_file_change_reason`。两者至少有一个。
45
46
  > - **最终交付文件必记**:apply 所有文件就绪后记录严格 `final_output_snapshot`,其中 `final_output_snapshot.files` 是最终交付文件的仓库相对路径清单。报告把它与逐任务文件分开说明;旧 `ai_adoption_review.ai_diff.files` 只作兼容来源。
@@ -2,6 +2,24 @@
2
2
  description: opsx-apply 的详细模板:telemetry 命令、worktree 全套策略、子代理派发、收尾脚本。仅在需要参考详细模板时读取。
3
3
  ---
4
4
 
5
+ ## 自动执行并取证(默认入口)
6
+
7
+ 使用 test-run 运行真实测试并记录同一条严格事件。工具采集命令参数、实际 cwd、退出码、耗时、测试计数和原始输出哈希;不让模型抄写这些字段。测试命令用 JSON 参数数组,无 shell 展开。
8
+
9
+ ```bash
10
+ node "<spec-package>/skywalk-sdd/log.cjs" test-run --project="<spec-package>" --change=<变更名称> --session-id=<会话ID> --task-ids=<TASK-ID列表> --cwd="<代码仓>" --tdd-phase=regression --command-json='["node","--test","--test-reporter=tap","test/order-total.test.js"]'
11
+ ```
12
+
13
+ RED 使用 --tdd-phase=red --expected-failure;真正的断言失败才能形成有效 RED。找不到测试入口/模块、编译/环境错误、超时均不能冒充业务红灯。命令返回实际测试退出码,失败仍保留事件与回执;不要因为 RED 非零就重复执行同一个 run-id。
14
+
15
+ 默认支持 Node TAP/Node 测试汇总。其他运行器输出无法可靠解析时 counts_known=false,不猜计数,不自动完成任务;已有适配器可继续用下方兼容入口提交实际报告。调用方明确声明 task-ids,工具不会把测试通过自动等同于覆盖了任意业务任务。
16
+
17
+ 需要测试成功后同步任务时,增加 --completion-file=<完成信息JSON文件>。内容为数组,每项包含 task_id、实际修改的仓库相对 files、tdd_required;TDD 任务同时写 tdd_pair_id/tdd_role。无文件修改填写 no_file_change_reason。工具将完成事件自动关联本轮 test_event_id 并同步任务勾选;RED、失败或未知计数不会自动完成任务。
18
+
19
+ 回执路径在输出 receipt 字段;原始 stdout/stderr 和执行清单保留在 skywalk-sdd/state/test-runs。记录失败后用原 details.json 重试 record --strict,禁止重写退出码或补造测试数。输出哈希不一致会被拒绝。自动 Hook 仅作补充,不对同一次 test-run 再手写一条 test_result。
20
+
21
+ 以下手写 test_result 模板仅保留给旧集成和已有测试报告适配器,新流程优先用 test-run。
22
+
5
23
  # opsx-apply — 详细参考(reference)
6
24
 
7
25
  > 本文件承载 opsx-apply 的重细节模板:telemetry 命令、§1.5 worktree 全套策略、§5c 子代理派发、§5.1 AI 产出快照、§6.0 单元测试真实执行、§6.1 worktree 收尾脚本。
@@ -95,7 +113,7 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --strict --type=test_res
95
113
  - `expected_failure=true` 仅允许 `tdd_phase=red`
96
114
  - 有效 RED:`expected_failure=true` + `exit_code≠0` + `failure_type` 为 `assertion` 或 `contract`
97
115
 
98
- > **自动记录**:`sdd-post-tool.cjs` hook 会在 agent 运行 `mvn test`/`npm test` 等命令时自动记录 `test_result`,使用正确的 `test_results` key。但自动记录依赖 `activeStage.task_id`,若未设置则 `tdd_phase` 默认为 `regression`。需要精确标注 RED/GREEN 时建议手动记录。
116
+ > **自动记录**:`sdd-post-tool.cjs` hook 会在 agent 运行 `mvn test`/`npm test` 等命令时自动记录 `test_result`,使用正确的 `test_results` key。但自动记录依赖 `activeStage.task_id`,若未设置则 `tdd_phase` 默认为 `regression`。需要精确标注 RED/GREEN 时使用上方 test-run 自动取证。
99
117
 
100
118
  ### process_note(阶段内过程事件:决策/范围变化/故障与恢复)
101
119
 
@@ -45,7 +45,7 @@ allowed-tools:
45
45
 
46
46
  ## 启动流程
47
47
 
48
- ### 1. 确认变更名称并记录阶段开始
48
+ ### 1. 确认变更名称
49
49
 
50
50
  如果未提供变更名,运行:
51
51
 
@@ -53,13 +53,7 @@ allowed-tools:
53
53
  openspec list
54
54
  ```
55
55
 
56
- 让用户选择现有变更。确认后记录阶段开始:
57
-
58
- ```bash
59
- node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" start --command=archive --project=. --change=<变更名称> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID>
60
- ```
61
-
62
- 保存 `event_id`。
56
+ 按参数、会话或唯一活动变更定位;只有多个候选且无法判断时请用户选择。此时尚未开始 archive 阶段。
63
57
 
64
58
  ### 2. 记录归档原因
65
59
 
@@ -85,6 +79,14 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" doctor --project=. --change=<
85
79
 
86
80
  如果存在 `severe_issues`,暂停归档并说明必须先修复。`superseded_open_stages` 和 `rework_summary` 只作为返工/重复执行展示,不要求用户人工区分测试回滚或真实研发返工。
87
81
 
82
+ 诊断通过后才记录本轮 archive 开始,保存返回的 `event_id`:
83
+
84
+ ```bash
85
+ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" start --command=archive --project=. --change=<变更名称> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID>
86
+ ```
87
+
88
+ 不要在本轮阶段已开始但未结束时再次运行前置 doctor;历史未闭环阶段仍须按真实结果补齐,不得跳过诊断或删除事件。
89
+
88
90
  ### 4. 扫描 tasks 完成状态
89
91
 
90
92
  ```bash
@@ -44,7 +44,7 @@ node "<spec-package>/skywalk-sdd/log.cjs" start --command=check --project="<spec
44
44
  ## 2. 程序预检(在完整文档评审前)
45
45
 
46
46
  ```bash
47
- node "<spec-package>/skywalk-sdd/log.cjs" semantic-check --project="<spec-package>" --change=<变更名称> --profile=auto --include-tasks
47
+ node "<spec-package>/skywalk-sdd/log.cjs" semantic-check --project="<spec-package>" --change=<变更名称> --profile=auto --include-tasks > "<spec-package>/skywalk-sdd/state/<变更名称>-preflight.json"
48
48
  ```
49
49
 
50
50
  输出 `profile / valid / revision / counts / diagnostics`、`review_inputs`(文档路径与哈希、历史身份哈希)、`task_completion` 和 `timings_ms`。程序时间不等于模型评审总耗时。
@@ -93,15 +93,17 @@ ${CHECK_REVIEW_DELEGATION_GUIDANCE}
93
93
 
94
94
  ## 4. 一次记录与结束
95
95
 
96
- 评审完成后,主 Agent 读取 [reference.md](reference.md),将结构化结果按 [result-template.json](result-template.json) 写入本轮临时 details 文件,执行唯一的 strict check_result 命令,再执行 end。不要在 shell 中手工转义大段 JSON。
96
+ 将实际评审项和真实会话身份保存为 [review-template.json](review-template.json) 格式。每项只写唯一 ID、类别、结论和证据;工具自动计算统计、读取任务状态、判断 partial/failure、关联阶段结束。旧 [result-template.json](result-template.json) 仅保留给已有集成。
97
97
 
98
- 顶层 `total` 必须是大于 0 的整数且来自实测,不能照抄模板的 0。空 details 导致 Q3 不可计算,strict 会拒绝。consistency_score 是 legacy 兼容字段,不可评分填 null。未执行项不能计入通过;warning 记录明细和处置理由。
98
+ 类别:completeness / consistency / executability / tdd_compliance;状态:passed / error / warning / suggestion / na。所有项包含证据或不适用理由,na 不计入通过。不要手填 total、errors、fixed_before_apply 或任务计数,不把模板占位内容当作已评审。
99
+
100
+ 兼容旧集成时,顶层 `total` 必须是大于 0 的整数,且来自实际评审项;空 details 导致记录被拒绝、Q3 不可计算。`consistency_score` 仅为 legacy 字段,不能替代有证据的分类检查。新入口由工具计算这些统计,评审者无需重复填写。
99
101
 
100
102
  ```bash
101
- node "<spec-package>/skywalk-sdd/log.cjs" end --event-id=<event_id> --command=check --project="<spec-package>" --change=<变更名称> --capability=<可选capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<父会话ID> --result=success/partial/failure --summary="本轮实测摘要"
103
+ node "<spec-package>/skywalk-sdd/log.cjs" check-record --project="<spec-package>" --change=<变更名称> --run-id=<本轮Check稳定ID> --session-id=<父会话ID> --agent=<Agent类型> --preflight-file="<spec-package>/skywalk-sdd/state/<变更名称>-preflight.json" --review-file="<spec-package>/skywalk-sdd/state/<变更名称>-review.json" --end-event-id=<event_id>
102
104
  ```
103
105
 
104
- 结束命令仍执行一次全量对账,核验评审期间文件/历史身份的变化;这次复核有时间边界意义,保留它。默认 auto 模式对照返回 semantic_state.revision 与预检 revision;不一致时不能宣称刚审过的版本仍有效,重新预检并复核变动及受影响的依赖,另记新一轮结果。显式 strict 时 end 的 auto revision 不能直接与 strict revision 比较;最终额外运行一次 --profile=strict 的 semantic-check 并比较 strict revision,auto 结果不能替代 strict 门禁。
106
+ check-record 按预检的同一 profile 重新核验正文、文件集合和历史身份。输入变化返回 CHECK_REVIEW_STALE,不写本轮通过记录;重新预检并复核受影响内容。成功时仍通过原 strict 校验和阶段结束入口,不另手动执行 end。评审输入始终保留,记录失败可以修复后重试。未真实开展评审时不用此命令,按原 end 命令记录 failure 和原因。
105
107
 
106
108
  报告输出五维结论、多仓配置、KB 状态、证据与未执行项,说明覆盖范围。只请求检查则结束;已授权实现且无阻断项时进入 apply。修复文档后再检查相关依赖,禁止携带旧结论直接通过;不把 Capability 局部评审写成全 Change 通过。
107
109
 
@@ -1,5 +1,7 @@
1
1
  # Check 结果记录(主 Agent 在评审完成后读取)
2
2
 
3
+ 新流程优先使用 SKILL.md 中的 check-record 与 [review-template.json](review-template.json):工具计算统计、补任务状态、校验评审输入是否过期,并可一次结束阶段。以下完整字段和 record --strict 保留给已有脚本,不要求新流程手填。
4
+
3
5
  收到实际评审结果后,必须把结果转换为 `check_result` 事件,并在 `stage_end` 之前执行成功。禁止用 stage_end 代替已执行评审的证据。程序预检阻断且未开展评审时,只记录阶段 failure 与真实原因,不伪造 reviewer 或零项评审。
4
6
 
5
7
  字段口径:
@@ -21,7 +23,7 @@
21
23
  - `categories`: 必须包含 `completeness`、`consistency`、`executability`、`tdd_compliance`;四类 total 合计等于顶层 total,四类未通过数合计等于 errors。算法正确性计入 consistency,文字报告单列,避免重复计数。
22
24
  - `task_completion`: 复用本轮 `semantic-check --include-tasks` 的输出(使用 tasks-status 同一扫描器),只取模板中的计数字段;未进入 apply 时 `checked_for_archive_readiness=false`。
23
25
 
24
- 将评审结果写为 UTF-8 JSON:`skywalk-sdd/state/<变更名称>-check-<run-id>.json`,结构见 [result-template.json](result-template.json)。其中 `0` 是待替换位置,执行前换成本轮实测值,顶层 `total` 必须是大于 0 的整数。不要直接把安装目录中的模板传给命令(details 文件成功读取后默认清理)。
26
+ 将评审结果写为 UTF-8 JSON:`skywalk-sdd/state/<变更名称>-check-<run-id>.json`,结构见 [result-template.json](result-template.json)。其中 `0` 是待替换位置,执行前换成本轮实测值,顶层 `total` 必须是大于 0 的整数。不要直接把安装目录中的模板传给命令(details 文件仅在校验、记录和投影成功后清理,失败保留)。
25
27
 
26
28
  ```bash
27
29
  node "<spec-package>/skywalk-sdd/log.cjs" record --strict --type=check_result --command=check --project="<spec-package>" --change=<变更名称> --capability=<可选capability-name> --run-id=<本轮Check稳定ID> --agent=<Agent类型> --source=opsx-command --session-id=<父会话ID> --result=success/partial/failure --summary="检查结果摘要" --details-file=skywalk-sdd/state/<变更名称>-check-<run-id>.json
@@ -0,0 +1,22 @@
1
+ {
2
+ "reviewer": {
3
+ "reviewer": "<实际评审者>",
4
+ "review_execution_mode": "subagent",
5
+ "reviewer_agent_id": "<真实子代理ID>",
6
+ "parent_session_id": "<主会话ID>",
7
+ "review_session_id": "<评审会话ID>",
8
+ "author_session_id": "<作者会话ID>",
9
+ "reviewer_independence": "independent-review",
10
+ "fallback_reason_code": null,
11
+ "fallback_reason": null
12
+ },
13
+ "items": [
14
+ {
15
+ "id": "<唯一检查项ID>",
16
+ "category": "consistency",
17
+ "status": "passed",
18
+ "evidence": "<实际文件:行号或锚点及检查依据>"
19
+ }
20
+ ],
21
+ "warning_dispositions": []
22
+ }
@@ -15,10 +15,10 @@
15
15
 
16
16
  #### 4.1 完整性检查
17
17
 
18
- - [ ] proposal.md 存在且包含完整章节
18
+ - [ ] proposal.md 存在,实际业务意图、能力、范围与约束完整;不要求无关空章节
19
19
  - [ ] 每个 Capability 都有 spec.md
20
20
  - [ ] Full 模式每个 spec.md 都有对应 design.md,每个 design.md 都有对应 tasks.md;Simple 未生成这些文档不误报缺失
21
- - [ ] 所有文档符合模板结构
21
+ - [ ] 所有文档保留解析需要的 frontmatter、身份、锚点与关系,内容规模匹配实际变更
22
22
 
23
23
  #### 4.2 一致性检查
24
24
 
@@ -93,8 +93,8 @@
93
93
 
94
94
  ## 返回给主 Agent
95
95
 
96
- 一次返回五维结论和 [result-template.json](result-template.json) 中的 `check_results`(替换全部占位值),以及检查范围、逐项证据路径/锚点、未执行项及原因、阻断问题、warning 明细和修复建议。算法问题归到四类统计中的 `consistency`,五维文字报告仍单列算法;不得重复计数。
96
+ 一次返回 [review-template.json](review-template.json) 的 items、检查范围和必要问题说明;身份由主 Agent 根据真实工具结果补齐。每项包含唯一 id、category、status、evidence。算法归入 consistency,不重复计数;不手算总数,不复述所有通过项,不复制文档全文。
97
97
 
98
- 顶层 `total` 必须是大于 0 的整数;四类 categories.total 合计等于 total,四类未通过数合计等于 errors。N/A 项 total=0。不要为使计数好看填“全部通过”。任务状态直接复用程序输出,reviewer 身份使用真实工具返回 ID;无法确认作者身份不能标为 independent-review。
98
+ 至少有一个实际适用项。未执行项使用 na 并写原因,不能为计数填“全部通过”。任务状态由工具读取,不能伪造 reviewer 或独立性。主 Agent 无需为统计不匹配重新委派评审。
99
99
 
100
- 评审期间若发现文件变化,返回受影响路径,不能用旧版本结果覆盖新文档。只读检查不修文档、不改代码。
100
+ 评审期间若发现文件变化,返回受影响路径。只读检查不修文档、不改代码。