kld-sdd 2.6.12 → 2.6.13

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.
Files changed (81) hide show
  1. package/README.md +20 -0
  2. package/kld-sdd-guide.html +429 -159
  3. package/lib/init.js +40 -7
  4. package/package.json +5 -2
  5. package/skywalk-sdd/index.cjs +2668 -415
  6. package/skywalk-sdd/metrics-v3.cjs +1153 -0
  7. package/skywalk-sdd/ontology/archive-package.cjs +114 -3
  8. package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -80
  9. package/skywalk-sdd/ontology/working-artifacts.cjs +2 -1
  10. package/templates/hooks/claude/hooks/sdd-apply-test-gate.cjs +39 -9
  11. package/templates/hooks/claude/hooks/sdd-post-tool.cjs +87 -8
  12. package/templates/hooks/claude/hooks/sdd-prompt.cjs +3 -1
  13. package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +39 -9
  14. package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +87 -9
  15. package/templates/hooks/codebuddy/hooks/sdd-prompt.cjs +3 -1
  16. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +14 -13
  17. package/templates/skills/kld-sdd/opsx-apply/checklist.md +20 -20
  18. package/templates/skills/kld-sdd/opsx-apply/implementer-prompt.md +12 -12
  19. package/templates/skills/kld-sdd/opsx-apply/reference.md +32 -31
  20. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +44 -6
  21. package/templates/skills/kld-sdd/opsx-archive/checklist.md +4 -1
  22. package/templates/skills/kld-sdd/opsx-check/SKILL.md +19 -11
  23. package/templates/skills/kld-sdd/opsx-check/checklist.md +7 -6
  24. package/templates/skills/kld-sdd/opsx-design/SKILL.md +1 -1
  25. package/templates/skills/kld-sdd/opsx-design/checklist.md +1 -1
  26. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +9 -1
  27. package/templates/skills/kld-sdd/opsx-propose/reference.md +29 -3
  28. package/templates/skills/kld-sdd/opsx-rules/reference.md +3 -1
  29. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +1 -1
  30. package/templates/skills/kld-sdd/opsx-spec/checklist.md +1 -1
  31. package/templates/skills/kld-sdd/opsx-task/SKILL.md +10 -10
  32. package/templates/skills/kld-sdd/opsx-task/checklist.md +3 -3
  33. package/templates/skills/kld-sdd/opsx-task/reference.md +3 -3
  34. package/templates/skills/kld-sdd/opsx-test/SKILL.md +7 -5
  35. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/SKILL.md +4 -4
  36. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/reference.md +4 -4
  37. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/SKILL.md +13 -13
  38. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/checklist.md +5 -5
  39. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/reference.md +12 -12
  40. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/SKILL.md +3 -3
  41. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/checklist.md +2 -2
  42. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/SKILL.md +2 -2
  43. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/parameterized-testing.md +1 -1
  44. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/SKILL.md +5 -5
  45. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/checklist.md +5 -5
  46. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/SKILL.md +3 -3
  47. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/non-tdd-modules.md +1 -1
  48. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/refactor-checklist.md +1 -1
  49. package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +19 -0
  50. package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-skeleton-telemetry.md +0 -19
  51. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/cause-effect-clarity.md +0 -0
  52. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/clean-test-data.md +0 -0
  53. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/existing-test-awareness.md +0 -0
  54. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/given-when-then.md +0 -0
  55. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/good-test-qualities.md +0 -0
  56. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/mock-boundary.md +0 -0
  57. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/naming-conventions.md +0 -0
  58. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/no-logic-in-tests.md +0 -0
  59. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/one-test-one-scenario.md +0 -0
  60. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/prefer-public-apis.md +0 -0
  61. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/test-behaviors-not-methods.md +0 -0
  62. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/argument-matching.md +0 -0
  63. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/controller-test-rules.md +0 -0
  64. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/domain-service-rules.md +0 -0
  65. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/java-test-template.md +0 -0
  66. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/json-serialization.md +0 -0
  67. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/logging-rules.md +0 -0
  68. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/compilation-verification.md +0 -0
  69. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/execution-verification.md +0 -0
  70. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/python/py-test-template.md +0 -0
  71. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/typescript/ts-test-template.md +0 -0
  72. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/controller-strategy.md +0 -0
  73. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/dag-generation-rules.md +0 -0
  74. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/des-step-annotation.md +0 -0
  75. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/exception-path-coverage.md +0 -0
  76. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-scope-declaration.md +0 -0
  77. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-yagni-fence.md +0 -0
  78. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/multi-validation-split.md +0 -0
  79. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/task-type-definitions.md +0 -0
  80. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/tdd-strategy-selection.md +0 -0
  81. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/test-execution-gate.md +0 -0
@@ -3,8 +3,8 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const crypto = require('crypto');
6
7
  const { execFileSync } = require('child_process');
7
- const core = require('./hook-gate-core.cjs');
8
8
 
9
9
  function latestActiveStage(projectRoot) {
10
10
  const stateDir = path.join(projectRoot, 'skywalk-sdd', 'state');
@@ -26,19 +26,54 @@ function latestActiveStage(projectRoot) {
26
26
  function inferResult(input) {
27
27
  const response = input.tool_response || input.toolResponse || {};
28
28
  if (typeof response.exit_code === 'number') {
29
- return { result: response.exit_code === 0 ? 'success' : 'failure', exit_code_known: true };
29
+ return { result: response.exit_code === 0 ? 'success' : 'failure', exit_code_known: true, exit_code: response.exit_code };
30
30
  }
31
31
  if (typeof response.exitCode === 'number') {
32
- return { result: response.exitCode === 0 ? 'success' : 'failure', exit_code_known: true };
32
+ return { result: response.exitCode === 0 ? 'success' : 'failure', exit_code_known: true, exit_code: response.exitCode };
33
33
  }
34
34
  if (typeof response.success === 'boolean') {
35
- return { result: response.success ? 'success' : 'failure', exit_code_known: true };
35
+ return { result: response.success ? 'success' : 'failure', exit_code_known: true, exit_code: response.success ? 0 : 1 };
36
36
  }
37
37
  const out = String(response.stdout || response.stderr || '');
38
38
  if (/BUILD FAILED|error TS\d+|ERR_/i.test(out)) {
39
- return { result: 'failure', exit_code_known: false };
39
+ return { result: 'failure', exit_code_known: false, exit_code: null };
40
40
  }
41
- return { result: 'partial', exit_code_known: false };
41
+ return { result: 'partial', exit_code_known: false, exit_code: null };
42
+ }
43
+
44
+ function parseTestCounts(output) {
45
+ const text = String(output || '');
46
+ const tapPass = text.match(/#\s*pass(?:ed)?\s+(\d+)/i);
47
+ const tapFail = text.match(/#\s*fail(?:ed)?\s+(\d+)/i);
48
+ const tapSkip = text.match(/#\s*skip(?:ped)?\s+(\d+)/i);
49
+ if (tapPass || tapFail) {
50
+ return {
51
+ counts_known: true,
52
+ passed: Number(tapPass?.[1] || 0),
53
+ failed: Number(tapFail?.[1] || 0),
54
+ skipped: Number(tapSkip?.[1] || 0),
55
+ };
56
+ }
57
+ const summary = text.match(/Tests?\s+.*?(\d+)\s+passed(?:.*?(\d+)\s+failed)?(?:.*?(\d+)\s+skipped)?/i);
58
+ if (summary) {
59
+ return {
60
+ counts_known: true,
61
+ passed: Number(summary[1] || 0),
62
+ failed: Number(summary[2] || 0),
63
+ skipped: Number(summary[3] || 0),
64
+ };
65
+ }
66
+ return { counts_known: false };
67
+ }
68
+
69
+ function stableRunId(input, activeStage, command) {
70
+ const explicit = input.tool_use_id || input.toolUseId || input.tool_call_id || input.toolCallId;
71
+ if (explicit) return String(explicit);
72
+ const response = input.tool_response || input.toolResponse || {};
73
+ return `hook-${crypto.createHash('sha256')
74
+ .update(JSON.stringify([activeStage.event_id || '', activeStage.session_id || '', command, response]))
75
+ .digest('hex')
76
+ .slice(0, 20)}`;
42
77
  }
43
78
 
44
79
  function inferRecord(command) {
@@ -69,6 +104,34 @@ function recordTelemetry(projectRoot, activeStage, record, result, exitCodeKnown
69
104
  const toolInput = (input && (input.tool_input || input.toolInput)) || {};
70
105
  const toolResponse = (input && (input.tool_response || input.toolResponse)) || {};
71
106
  const details = { [record.detailsKey]: { ...record.details } };
107
+ let strictTestEvent = false;
108
+ let runId = null;
109
+ if (record.type === 'test_result') {
110
+ const output = String(toolResponse.stdout || '') + '\n' + String(toolResponse.stderr || '');
111
+ const counts = parseTestCounts(output);
112
+ const exitCode = typeof toolResponse.exit_code === 'number'
113
+ ? toolResponse.exit_code
114
+ : (typeof toolResponse.exitCode === 'number'
115
+ ? toolResponse.exitCode
116
+ : (typeof toolResponse.success === 'boolean' ? (toolResponse.success ? 0 : 1) : null));
117
+ const tddPhase = toolInput.tdd_phase || input.tdd_phase || 'regression';
118
+ details.test_results = {
119
+ command: toolInput.command || record.details.command,
120
+ tdd_phase: tddPhase,
121
+ exit_code: exitCode,
122
+ counts_known: counts.counts_known,
123
+ ...(counts.counts_known ? {
124
+ passed: counts.passed,
125
+ failed: counts.failed,
126
+ skipped: counts.skipped,
127
+ } : {}),
128
+ duration_ms: Number.isFinite(toolResponse.duration_ms) ? toolResponse.duration_ms : 0,
129
+ failure_type: result === 'failure' ? (exitCode == null ? 'infrastructure' : 'assertion') : 'none',
130
+ expected_failure: tddPhase === 'red' && result === 'failure' && exitCode != null,
131
+ };
132
+ strictTestEvent = exitCode != null && Boolean(activeStage.task_id);
133
+ runId = stableRunId(input || {}, activeStage, details.test_results.command);
134
+ }
72
135
  if (record.type === 'build_result') {
73
136
  details.build_results.success = result === 'success';
74
137
  details.build_results.error_count = result === 'success' ? 0 : null;
@@ -82,7 +145,7 @@ function recordTelemetry(projectRoot, activeStage, record, result, exitCodeKnown
82
145
  logCli,
83
146
  'record',
84
147
  `--type=${record.type}`,
85
- `--command=${toolInput.command || activeStage.command || activeStage.stage || 'unknown'}`,
148
+ `--command=${activeStage.command || activeStage.stage || 'unknown'}`,
86
149
  `--project=${projectRoot}`,
87
150
  `--change=${activeStage.change || 'general'}`,
88
151
  `--agent=${activeStage.agent_type || 'codebuddy'}`,
@@ -91,14 +154,22 @@ function recordTelemetry(projectRoot, activeStage, record, result, exitCodeKnown
91
154
  `--summary=CodeBuddy hook captured ${record.type}`,
92
155
  `--details-json=${JSON.stringify(details)}`,
93
156
  ];
157
+ if (strictTestEvent) {
158
+ args.push(
159
+ '--strict',
160
+ `--run-id=${runId}`,
161
+ `--session-id=${activeStage.session_id || `stage-${activeStage.event_id}`}`,
162
+ );
163
+ }
94
164
  if (activeStage.capability) args.push(`--capability=${activeStage.capability}`);
95
165
  if (activeStage.task_id) args.push(`--task-id=${activeStage.task_id}`);
96
- if (activeStage.session_id) args.push(`--session-id=${activeStage.session_id}`);
166
+ if (!strictTestEvent && activeStage.session_id) args.push(`--session-id=${activeStage.session_id}`);
97
167
  const run = runner || ((a) => execFileSync('node', a, { cwd: projectRoot, stdio: 'ignore' }));
98
168
  run(args);
99
169
  }
100
170
 
101
171
  if (require.main === module) {
172
+ const core = require('./hook-gate-core.cjs');
102
173
  const parsed = core.parseHookInput(core.readStdin(), { strict: false });
103
174
  const input = core.normalizeHookInput(parsed.input || {});
104
175
  const toolInput = input.tool_input || {};
@@ -120,4 +191,11 @@ if (require.main === module) {
120
191
  }
121
192
  }
122
193
 
123
- module.exports = { inferResult, inferRecord, recordTelemetry, latestActiveStage };
194
+ module.exports = {
195
+ inferResult,
196
+ inferRecord,
197
+ parseTestCounts,
198
+ stableRunId,
199
+ recordTelemetry,
200
+ latestActiveStage,
201
+ };
@@ -54,8 +54,10 @@ const activeStages = readActiveStages(projectRoot)
54
54
 
55
55
  const lines = [
56
56
  'SDD Telemetry reminder:',
57
+ '- Resolve the exact change key first; every standard OPSX start/end command must include --change=<change-key>.',
57
58
  '- Run node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" start before the OPSX stage work begins.',
58
- '- Run node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" end before stopping the stage.',
59
+ '- Run node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" end with the same change, stage, session and event identity before stopping.',
60
+ '- change=general is reserved for explicitly allowed explore events and must later use scope_link for historical attribution.',
59
61
  '- Hooks are only an enhancement; OPSX skill instructions remain authoritative.',
60
62
  ];
61
63
 
@@ -38,7 +38,8 @@ allowed-tools:
38
38
  > - 较大的 `--details-json` 负载可先写入文件,再通过 `--details-file="$(cat .sdd-spec-root)/skywalk-sdd/state/<变更名称>-<type>.json"` 传递(例如 `task_update` 对应 `<变更名称>-task-update.json`)。
39
39
  > - `task_update` 记录成功后会自动调用 `check-task` 更新 `tasks.md` 中的 checkbox;录入成功后,agent 可以通过 `check-task` 命令验证该 checkbox 已更新。
40
40
  > - **⚠️ 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」。
41
- > - **⚠️ P1-3 process_note 必记**:阶段内发生 修复/澄清/决策/API 故障/换模型 时,**必须**记 `process_note` 事件(`kind` 见 `./reference.md`「必记节点清单」)。全程 0 条 process_note 视为过程记录缺失,report 过程记录区显示空。
41
+ > - **严格证据链**:一次真实测试执行只写一条严格 `test_result`;任务完成事件只通过 `test_event_id` 引用成功的 green/refactor/regression 测试,并明确 `tdd_required=true/false`,不复制测试计数。测试不适用时必须写明原因。完整 schema 见 `./reference.md`。
42
+ > - **process_note 必记**:用户决策、范围变化、API/模型/测试异常及恢复动作必须写严格 `process_note`;范围、模式、测试策略选择统一用 `kind=user_decision` 并标注 `decision_type`。
42
43
 
43
44
  > **🔒 Git 策略(只读增强,不改变开发流)**
44
45
  > - Git 只作为可选度量数据源,不是 apply 前置条件。
@@ -205,29 +206,29 @@ g. **继续下一个层级** — 重新检查 DAG,找出依赖已满足的下
205
206
  3. **GREEN**:读取 RED 失败原因 → 执行 GREEN Scope 声明 → 逐条确认 YAGNI 围栏 → 写最少代码让测试通过 → 禁止捆绑未测试代码
206
207
  4. **GREEN Scope 门禁**:Verify GREEN 之后,检查生产代码无越界逻辑分支(属于后续 RED 的行为 → 删除)
207
208
  5. **REFACTOR**:在测试全绿状态下重构 → 运行全部测试确认仍绿
208
- 6. **可选:TDD 审查子代理**:GREEN 任务完成后,可派发独立审查子代理执行 `opsx-tdd-review/SKILL.md` §7(子代理审查提示模板),检测"测试通过但没测到关键点"。implementer 自审查 ≠ 独立审查。
209
+ 6. **可选:TDD 审查子代理**:GREEN 任务完成后,可派发独立审查子代理执行 `tdd-review/SKILL.md` §7(子代理审查提示模板),检测"测试通过但没测到关键点"。implementer 自审查 ≠ 独立审查。
209
210
  7. 再进入下一个任务
210
211
 
211
- > 完整执行步骤见 opsx-tdd-core/reference.md §1-§3
212
- > 合理化预防表见 opsx-tdd-core/SKILL.md §7
213
- > REFACTOR 检查点见 opsx-tdd-rules/rules/refactor-checklist.md
214
- > 异常路径覆盖门禁见 opsx-tdd-rules/rules/exception-path-coverage.md
212
+ > 完整执行步骤见 tdd-core/reference.md §1-§3
213
+ > 合理化预防表见 tdd-core/SKILL.md §7
214
+ > REFACTOR 检查点见 tdd-rules/rules/refactor-checklist.md
215
+ > 异常路径覆盖门禁见 tdd-rules/rules/exception-path-coverage.md
215
216
  > TDD 节奏校验(执行后校验)见 `./checklist.md` §5e.1
216
217
 
217
218
  **【S2.1 RED 测试质量标准】**(test-strategy=tdd 时强制):
218
219
 
219
220
  ⛔ RED 测试质量门禁见 `./checklist.md`「§5e.2 RED 测试质量门禁」,核心包括:
220
- - Mock 边界(引用 opsx-tdd-quality/SKILL.md §2)
221
- - 测试命名与结构(引用 opsx-tdd-quality/SKILL.md §3-§4)
222
- - 断言深度(引用 opsx-tdd-anti-patterns/SKILL.md §4 反模式 14)
223
- - RED 阶段反模式检查(引用 opsx-tdd-anti-patterns/SKILL.md §3)
221
+ - Mock 边界(引用 tdd-quality/SKILL.md §2)
222
+ - 测试命名与结构(引用 tdd-quality/SKILL.md §3-§4)
223
+ - 断言深度(引用 tdd-anti-patterns/SKILL.md §4 反模式 14)
224
+ - RED 阶段反模式检查(引用 tdd-anti-patterns/SKILL.md §3)
224
225
 
225
226
  ⛔ BEFORE 编写 RED 测试,必须读取:
226
- - opsx-tdd-anti-patterns/SKILL.md §3(RED 阶段 3 种反模式 + 门禁函数)
227
+ - tdd-anti-patterns/SKILL.md §3(RED 阶段 3 种反模式 + 门禁函数)
227
228
  读取后在报告中确认:"已读取 RED 阶段反模式检查表"。
228
229
 
229
- > 完整 Mock 边界矩阵见 opsx-tdd-quality/SKILL.md §2
230
- > 完整 15 种反模式见 opsx-tdd-anti-patterns/SKILL.md §3-§4
230
+ > 完整 Mock 边界矩阵见 tdd-quality/SKILL.md §2
231
+ > 完整 15 种反模式见 tdd-anti-patterns/SKILL.md §3-§4
231
232
 
232
233
  ### 5f. 【S3 apply 结束前 checkbox 全量自检】
233
234
 
@@ -34,7 +34,7 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
34
34
 
35
35
  ### §5e 测试执行门禁(按 `proposal.md` 的 `test-strategy`)
36
36
 
37
- > 测试执行门禁策略见 opsx-tdd-rules/rules/test-execution-gate.md
37
+ > 测试执行门禁策略见 tdd-rules/rules/test-execution-gate.md
38
38
 
39
39
  - [ ] `tdd` → ⛔ 强制执行(RED 确认失败、GREEN 确认通过、REFACTOR 全部测试仍绿)
40
40
  - [ ] `impl-first` → ⚠️ 警告模式
@@ -42,10 +42,10 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
42
42
 
43
43
  ### §5e.1 TDD 执行合规自检(仅 test-strategy=tdd 时)
44
44
 
45
- ⛔ 每完成一个 RED/GREEN/REFACTOR 任务后,必须执行 `opsx-tdd-core/checklist.md` §A(11 项)逐项勾选。
45
+ ⛔ 每完成一个 RED/GREEN/REFACTOR 任务后,必须执行 `tdd-core/checklist.md` §A(11 项)逐项勾选。
46
46
 
47
- > 不在此内联复制,以 opsx-tdd-core/checklist.md §A 为唯一真相源。
48
- > 额外补充:REFACTOR 任务还需执行 `opsx-tdd-rules/rules/refactor-checklist.md`(7 项重构检查点)。
47
+ > 不在此内联复制,以 tdd-core/checklist.md §A 为唯一真相源。
48
+ > 额外补充:REFACTOR 任务还需执行 `tdd-rules/rules/refactor-checklist.md`(7 项重构检查点)。
49
49
 
50
50
  ⛔ **TDD 节奏校验**(RED→GREEN 严格串行的执行后校验):
51
51
  - [ ] 连续的 RED-N `task_update` 与 GREEN-N `task_update` 之间有可验证的执行间隔(建议 >60 秒),若时间戳差距过小视为批量执行信号
@@ -54,50 +54,50 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
54
54
 
55
55
  ### §5e.2 RED 测试质量门禁(仅 test-strategy=tdd 时,RED 任务完成后强制检查)
56
56
 
57
- ⛔ 核心原则(引用 opsx-tdd-quality/SKILL.md §2):Mock 边界,不 Mock 行为
57
+ ⛔ 核心原则(引用 tdd-quality/SKILL.md §2):Mock 边界,不 Mock 行为
58
58
  - [ ] 未 mock 被测行为本身
59
59
  - [ ] Mock 仅用于系统边界依赖(Mapper/HTTP)
60
60
  - [ ] RED 测试 Given 是真实输入
61
61
 
62
- ⛔ 测试命名与结构(引用 opsx-tdd-quality/SKILL.md §3-§4):
62
+ ⛔ 测试命名与结构(引用 tdd-quality/SKILL.md §3-§4):
63
63
  - [ ] 测试方法名符合 `{method}_{state}_{outcome}` 格式
64
64
  - [ ] 测试包含 `// Given` / `// When` / `// Then` 注释结构
65
65
  - [ ] 一测一场景(测试方法名不含 "and")
66
66
 
67
- ⛔ 断言深度(引用 opsx-tdd-anti-patterns/SKILL.md §4 反模式 14):
67
+ ⛔ 断言深度(引用 tdd-anti-patterns/SKILL.md §4 反模式 14):
68
68
  - [ ] 每个测试至少有一个具体值断言(assertEquals),而非仅 assertNotNull
69
69
  - [ ] 异常测试使用 `assertThrows(BusinessException.class, ...)` 并验证错误码(而非 RuntimeException.class)
70
70
 
71
71
  ⛔ BEFORE 标记 RED 任务完成,必须读取:
72
- - opsx-tdd-anti-patterns/SKILL.md §3(RED 阶段反模式检查)
72
+ - tdd-anti-patterns/SKILL.md §3(RED 阶段反模式检查)
73
73
  读取后确认:"已检查 RED 阶段反模式"。
74
74
 
75
- > 完整质量门禁见 opsx-tdd-quality/SKILL.md §2-§6
76
- > 完整 15 种反模式见 opsx-tdd-anti-patterns/SKILL.md §3-§4
75
+ > 完整质量门禁见 tdd-quality/SKILL.md §2-§6
76
+ > 完整 15 种反模式见 tdd-anti-patterns/SKILL.md §3-§4
77
77
 
78
78
  ### 任务状态实时更新
79
79
 
80
- - [ ] tasks.md:`- [ ]` → `- [x]`(拓扑图和任务详情中的复选框)
81
- - [ ] 同时:`- **状态**: [ ] 未完成` → `- **状态**: [x] 已完成`
82
- - [ ] 两种格式同步更新,不可遗漏
80
+ - [ ] 每个主任务只保留一条规范状态行:`- **状态**: [ ] 未完成` → `- **状态**: [x] 已完成`
81
+ - [ ] 自动同步仅更新任务块内的 `- **状态**:` 行,不改标题复选框、场景列表或验收清单
82
+ - [ ] “验收证据”与主任务进度分开统计;必须由真实测试、人工验收或评审证据确认,不自动勾选
83
83
  - [ ] 显示进度:`✅ [TASK-ID] 已完成 [N/M]`
84
- - [ ] 记录任务级 Telemetry(`task_update`,`--task-id=<TASK-ID>` 必填,否则 E4 指标无法计算);TDD 测试骨架任务须在 `--details-json` `"task_kind":"test-skeleton"`(P3,避免红灯误判拉低 E4)
85
- - [ ] ⛔ **task_update 后必须验证 checkbox 已更新**:执行 `node "$(cat .sdd-spec-root)/skywalk-sdd/index.cjs" check-task --project=. --change=<变更名称> --task-id=<TASK-ID>` 确认 tasks.md 中对应行已从 `- [ ]` 变为 `- [x]`;若未更新,手动修改 tasks.md 并报告
84
+ - [ ] 记录严格任务事件:`task_update` 必须含 `--task-id`、`--run-id`,完成时引用成功的 green/refactor/regression `test_event_id` 并明确 `tdd_required=true/false`;测试不适用时写明 `verification_not_applicable_reason`
85
+ - [ ] ⛔ **task_update 后必须验证状态已更新**:执行 `node "$(cat .sdd-spec-root)/skywalk-sdd/index.cjs" check-task --project=. --change=<变更名称> --task-id=<TASK-ID>`,确认对应规范状态行已变为 `[x]`
86
86
 
87
87
  ### §5f.1 apply 结束前 checkbox 全量同步校验
88
88
 
89
89
  > ⛔ 在 `stage_end` telemetry 记录前必须执行此校验,防止任务状态滞后到 archive 阶段。
90
90
 
91
- - [ ] 对比 telemetry `task_update` 记录的已完成任务数与 tasks.md 中 `[x]` 数量,不一致则补齐
92
- - [ ] tasks.md 中所有 `- [ ]` / `- [x]` 与 `**状态**: [ ]` / `[x]` 两种格式已同步
93
- - [ ] 手动验证清单(如有)已勾选
91
+ - [ ] 对比 telemetry `task_update` 已完成任务数与 `primary_tasks.completed`,不一致则修正任务状态或事件
92
+ - [ ] `acceptance_evidence` 单独报告已确认/待确认数量,不将其混入任务完成率
93
+ - [ ] 手动验证清单(如有)仅在真实完成后勾选
94
94
  - [ ] 文档更新项(如有)已完成或显式标注推迟
95
95
 
96
96
  ---
97
97
 
98
98
  ## §6.0 单元测试真实执行自检(`test-strategy` 非 `none`)
99
99
 
100
- > 单元测试真实执行自检见 opsx-tdd-core/reference.md §9
100
+ > 单元测试真实执行自检见 tdd-core/reference.md §9
101
101
 
102
102
  - [ ] 真实运行单元测试命令并留 telemetry 证据
103
103
  - [ ] `tdd`:无测试证据不得结束 apply / 不得 finish worktree
@@ -126,7 +126,7 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
126
126
  - [ ] ⛔ **DAG 依赖拦截**:执行任务前必须检查依赖,前置未完成必须拦截
127
127
  - [ ] ⛔ **编译检查门禁**:每完成一个任务后必须运行编译检查,编译失败禁止标记已完成
128
128
  - [ ] ⛔ **测试执行门禁**:根据 `test-strategy` 决定(tdd=强制, impl-first=强制补跑, none=跳过);须真实执行并留 telemetry,`sdd-apply-test-gate` 校验非占位数据
129
- - [ ] ⛔ **RED 测试质量门禁**:见 §5e.2(引用 opsx-tdd-quality + opsx-tdd-anti-patterns,不在此内联复制)
129
+ - [ ] ⛔ **RED 测试质量门禁**:见 §5e.2(引用 tdd-quality + tdd-anti-patterns,不在此内联复制)
130
130
  - [ ] ⛔ **必须实时更新任务状态**:每完成一个任务立即改 tasks.md,两种格式(`- [ ]`→`- [x]` 与 `**状态**: [ ]`→`[x]`)同步
131
131
  - [ ] ⛔ **apply 结束前 checkbox 全量同步校验**:见 §5f.1,`stage_end` 前对比 telemetry `task_update` 记录数与 tasks.md `[x]` 数量
132
132
  - [ ] ⛔ **task_update 后必须验证 checkbox 已更新**:执行 `check-task` 确认 tasks.md 对应行已变更;未更新则手动修改
@@ -14,26 +14,26 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
14
14
  2. 编写测试代码:Given-When-Then 结构 + 真实断言
15
15
  3. 运行测试,确认失败(失败原因必须是功能未实现)
16
16
  4. 如果测试通过:说明测试无效或功能已存在,重新编写
17
- 5. ⛔ 检查异常路径覆盖:每个 orElseThrow/边界检查须有对应测试方法(见 `opsx-tdd-rules/rules/exception-path-coverage.md`)
17
+ 5. ⛔ 检查异常路径覆盖:每个 orElseThrow/边界检查须有对应测试方法(见 `tdd-rules/rules/exception-path-coverage.md`)
18
18
 
19
19
  ⛔ BEFORE 编写测试代码,必须读取对应语言的规则文件:
20
- - Java:opsx-tdd-quality/rules/java/java-test-template.md + argument-matching.md + domain-service-rules.md
21
- - TypeScript:opsx-tdd-quality/rules/typescript/ts-test-template.md
22
- - Python:opsx-tdd-quality/rules/python/py-test-template.md
23
- - 通用:opsx-tdd-quality/rules/general/naming-conventions.md + given-when-then.md + no-logic-in-tests.md
20
+ - Java:tdd-quality/rules/java/java-test-template.md + argument-matching.md + domain-service-rules.md
21
+ - TypeScript:tdd-quality/rules/typescript/ts-test-template.md
22
+ - Python:tdd-quality/rules/python/py-test-template.md
23
+ - 通用:tdd-quality/rules/general/naming-conventions.md + given-when-then.md + no-logic-in-tests.md
24
24
  读取后在报告中列出已读取的规则文件路径。
25
25
 
26
26
  ### 执行 实现-GREEN 任务
27
27
  1. 读取对应 RED 任务的失败原因
28
- 2. 【Scope 声明】按 `opsx-tdd-rules/rules/green-scope-declaration.md` 执行 Scope 声明步骤
28
+ 2. 【Scope 声明】按 `tdd-rules/rules/green-scope-declaration.md` 执行 Scope 声明步骤
29
29
  3. 编写最少代码——仅实现 Scope 声明中标记为"属于当前 RED"的步骤
30
30
  4. 【Scope 自检】检查生产代码中是否有未被任何当前 RED 断言覆盖的逻辑路径?有则删除
31
31
  5. 不提前实现没有测试要求的功能(YAGNI)
32
32
  6. 禁止捆绑未测试的代码(Controller/Filter/Config)
33
33
 
34
- > 完整执行步骤见 opsx-tdd-core/reference.md §1-§3
35
- > 完整质量标准见 opsx-tdd-quality/SKILL.md §2
36
- > Scope 声明规则见 opsx-tdd-rules/rules/green-scope-declaration.md
34
+ > 完整执行步骤见 tdd-core/reference.md §1-§3
35
+ > 完整质量标准见 tdd-quality/SKILL.md §2
36
+ > Scope 声明规则见 tdd-rules/rules/green-scope-declaration.md
37
37
 
38
38
  ### 执行 重构-REFACTOR 任务
39
39
  1. 在所有测试通过的状态下开始
@@ -41,9 +41,9 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
41
41
  3. 运行**全部测试**:`mvn test`(或项目对应命令)
42
42
  4. 确认所有测试仍通过
43
43
  5. 如果任何测试失败:回退重构,重新尝试
44
- 6. ⛔ 执行 `opsx-tdd-rules/rules/refactor-checklist.md`(7 项重构检查点)
44
+ 6. ⛔ 执行 `tdd-rules/rules/refactor-checklist.md`(7 项重构检查点)
45
45
 
46
- > 完整 REFACTOR 检查点见 opsx-tdd-rules/rules/refactor-checklist.md
46
+ > 完整 REFACTOR 检查点见 tdd-rules/rules/refactor-checklist.md
47
47
 
48
48
  ## 派发格式
49
49
 
@@ -94,7 +94,7 @@ Agent (general-purpose):
94
94
  3. 遵循 overview.md 的全局规范
95
95
  4. 保持变更最小化,不超出任务范围
96
96
  5. **当任务类型为 测试-RED 时**:编写带真实断言的测试,运行并确认失败,记录失败原因
97
- 6. **当任务类型为 实现-GREEN 时**:读取对应 RED 的失败原因,按 `opsx-tdd-rules/rules/green-scope-declaration.md` 执行 Scope 声明,仅实现标记为"属于"的步骤,写最少代码让测试通过,不提前实现未要求的功能
97
+ 6. **当任务类型为 实现-GREEN 时**:读取对应 RED 的失败原因,按 `tdd-rules/rules/green-scope-declaration.md` 执行 Scope 声明,仅实现标记为"属于"的步骤,写最少代码让测试通过,不提前实现未要求的功能
98
98
  7. **当任务类型为 重构-REFACTOR 时**:在测试全绿状态下优化代码,运行全部测试确认仍绿
99
99
  8. 自我审查(见下方)
100
100
  9. 报告结果
@@ -17,14 +17,20 @@ description: opsx-apply 的详细模板:telemetry 命令、worktree 全套策
17
17
  ### task_update(每完成一个任务记录)
18
18
 
19
19
  ```bash
20
- node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"files_changed\":[],\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0},\"test_results\":{\"command\":\"<实际测试命令>\",\"passed\":0,\"failed\":0,\"skipped\":0,\"coverage\":null,\"duration_ms\":0}}"
20
+ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --strict --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --run-id=<本次任务更新稳定ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"task_update\":{\"test_event_id\":\"<同一change内成功test_result的event_id>\",\"tdd_required\":<true|false>},\"files_changed\":[]}"
21
21
  ```
22
22
 
23
- **⚠️ 注意**:`--task-id=<TASK-ID>` 必须替换为实际任务 ID,否则 E4 指标无法计算。
23
+ **⚠️ 注意**:`--task-id=<TASK-ID>` 必须替换为实际任务 ID。完成任务必须引用同一 change 内、`tdd_phase` 为 green/refactor/regression、退出码为 0、`failure_type=none` 的 `test_result.event_id`,并明确填写 `tdd_required=true/false`;禁止把测试计数复制进 `task_update`。
24
+
25
+ 只有文档、纯配置说明等确实不适用测试的任务,才能显式声明:
26
+
27
+ ```json
28
+ {"task_update":{"verification_not_applicable":true,"verification_not_applicable_reason":"<为什么本任务无需测试的具体原因>"}}
29
+ ```
24
30
 
25
31
  **🧪 TDD 测试骨架任务(`test-strategy: tdd`)**:当任务是"测试骨架"时,`task_update` 必须在 `--details-json` 中带 `"task_kind":"test-skeleton"`。
26
32
 
27
- > 完整 test-skeleton telemetry 模板见 opsx-tdd-rules/rules/test-skeleton-telemetry.md
33
+ > 完整 test-skeleton telemetry 模板见 tdd-rules/rules/test-skeleton-telemetry.md
28
34
 
29
35
  **📄 通过文件传递大 payload**:若 `details-json` 内容过长,可先写入 `"$(cat .sdd-spec-root)/skywalk-sdd/state/<变更名称>-task-update.json`,再使用 `--details-file` 指定该文件:
30
36
 
@@ -38,47 +44,42 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=task_update --com
38
44
  node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" check-task --project=. --change=<变更名称> --task-id=<TASK-ID>
39
45
  ```
40
46
 
41
- **【Q3 details schema 字段说明】**:`details-json` 中的 `test_results` `build_results` 字段非强制,但提供后能让 E4 一次成码率更准确:
42
- - `test_results`: `{ command, passed, failed, skipped, coverage, duration_ms }`
43
- - `build_results`: `{ command, success, duration_ms, error_count }`
44
- - 若 agent 只记 `--status=completed` 不带 build/test(tools 兼容),index.cjs 会 fallback 用 status=completed 作为 success=true(Q3 修复)
45
-
46
- **【L6 test_count 口径】**:`test_results.passed + failed + skipped` 含全部用例(含 cases 数组外的单独用例)。不要只计 cases 数组内数量而漏计独立用例。
47
+ **【严格证据口径】**:测试执行事实只存在于 `test_result`;`task_update` 只表达任务状态与证据引用。旧事件仍可读取,但新事件必须使用 `--strict`。
47
48
 
48
- ### process_note(阶段内过程事件:修复/澄清/决策/故障)
49
+ ### process_note(阶段内过程事件:决策/范围变化/故障与恢复)
49
50
 
50
- 记录阶段内过程信息,供 execution-log 叙事与报告"过程记录"板块统计(U2 叙事信号源 / U3 修复可追溯 / M1 时长虚高旁证)。`details.kind` 必填,枚举:
51
+ 记录阶段内过程信息,供 execution-log 叙事与报告“过程记录”板块统计。新事件使用 `--strict`,`details.kind` 必填,枚举:
51
52
 
52
53
  | kind | 场景 | 示例 |
53
54
  |------|------|------|
54
- | `fix` | check/apply/test 后修复动作 | README/CHANGELOG、勾 checkbox、修配置 |
55
- | `clarification` | 用户澄清/多轮交流(带 `round`) | 用户要求补充 CHANGELOG(R1/R2...) |
56
- | `decision` | 关键设计/实现决策 | 选方案 A 而非 B |
57
- | `api-error` | API 故障(M1 stage 时长虚高旁证) | Connection closed mid response |
58
- | `model-switch` | 换模型 | kimi-k2.7-code → glm-5.2 |
59
- | `manual-action` | 用户手动操作 | 用户手动改配置 |
55
+ | `user_decision` | 用户确认关键选择 | 范围、执行模式、测试策略、方案 A/B |
56
+ | `scope_change` | 已确认范围发生变化 | 新增或移除交付项 |
57
+ | `api_error` | API 或外部服务故障 | 连接中断、限流 |
58
+ | `model_error` | 模型执行故障 | 输出中断、工具调用异常 |
59
+ | `recovery` | 对故障或失败的恢复动作 | 修复配置后重试 |
60
+ | `test_exception` | 测试执行异常 | 环境缺依赖、超时 |
60
61
 
61
62
  ```bash
62
- node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=process_note --command=<stage> --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --summary="<人读摘要>" --details-json="{\"kind\":\"<fix|clarification|decision|api-error|model-switch|manual-action>\",\"target\":\"<可选,关联对象>\",\"round\":<可选,clarification 轮次整数>}"
63
+ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --strict --type=process_note --command=<stage> --project=. --change=<变更名称> --capability=<capability-name> --run-id=<本次过程事件稳定ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --summary="<人读摘要>" --details-json="{\"kind\":\"<user_decision|scope_change|api_error|model_error|recovery|test_exception>\",\"decision_type\":\"<user_decision时可填scope|mode|test_strategy|other>\",\"target\":\"<可选关联对象>\"}"
63
64
  ```
64
65
 
65
66
  **使用时机**:
66
- - **check 后用户选 B 补修复** 每个修复动作记一条 `kind=fix`(U3,归档前修补不再黑箱)
67
- - **用户多轮澄清**每轮记一条 `kind=clarification` 带 `round`(1/2/3...,任意轮次可记)
68
- - **API 错误/换模型** `kind=api-error`/`model-switch`(M1 stage 时长虚高旁证,报告"过程记录"by_kind 体现)
69
- - **关键决策**`kind=decision`
67
+ - **用户确认范围/模式/测试策略** → `kind=user_decision`,并用 `decision_type` 说明是哪类选择
68
+ - **范围后来改变** → `kind=scope_change`
69
+ - **API/模型/测试环境故障**分别记录 `api_error` / `model_error` / `test_exception`
70
+ - **修复后恢复执行** → `kind=recovery`
70
71
 
71
72
  **必记节点清单**(遗漏即视为过程记录缺失,`sdd-apply-test-gate` 会记 `telemetry_warning(process_note_missing)`):
72
73
 
73
74
  | 节点 | kind | target |
74
75
  |------|------|--------|
75
- | 门禁拦截(apply 被门禁阻断) | `decision` | stage |
76
- | frontmatter/文档补齐 | `fix` | 文件:行 |
77
- | 编辑失败重试 | `fix` | 文件 |
78
- | 归档前 checkbox 勾选 | `fix` | tasks.md:行 |
79
- | 用户交互决策点(选 A/B/C 等) | `decision` | - |
80
- | API 故障 | `api-error` | - |
81
- | 换模型 | `model-switch` | - |
76
+ | 门禁拦截后的处理选择 | `user_decision` | stage |
77
+ | frontmatter/文档补齐后恢复 | `recovery` | 文件:行 |
78
+ | 编辑失败并重试 | `recovery` | 文件 |
79
+ | 用户交互决策点(范围/模式/测试策略) | `user_decision` | - |
80
+ | API 故障 | `api_error` | - |
81
+ | 模型故障 | `model_error` | - |
82
+ | 测试环境异常 | `test_exception` | - |
82
83
 
83
84
  ### ai_adoption_review(AI 产出快照)
84
85
 
@@ -349,7 +350,7 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=ai_adoption_revie
349
350
 
350
351
  ## §6.0 单元测试真实执行(`test-strategy` 非 `none`)
351
352
 
352
- > 单元测试真实执行见 opsx-tdd-core/reference.md §9
353
+ > 单元测试真实执行见 tdd-core/reference.md §9
353
354
 
354
355
  当 `test-strategy` 为 `tdd` 或 `impl-first` 时,必须真实运行单元测试并留 telemetry 证据。`sdd-apply-test-gate.cjs` 会在 `log.cjs end`、`apply-worktree-finish`、会话 Stop 时自动校验。
355
356
 
@@ -357,7 +358,7 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=ai_adoption_revie
357
358
 
358
359
  ## §6.0a 测试反模式检查(TDD 模式下)
359
360
 
360
- > 完整 9 种反模式检测见 opsx-tdd-anti-patterns/SKILL.md §3-§4 + reference.md
361
+ > 完整 9 种反模式检测见 tdd-anti-patterns/SKILL.md §3-§4 + reference.md
361
362
 
362
363
  ⛔ RED 阶段就必须检查,不要等到 GREEN 之后才发现测试是假的。
363
364
 
@@ -35,7 +35,7 @@ allowed-tools:
35
35
  | 维度 | 内容 |
36
36
  |---|---|
37
37
  | 核心问题 | 变更生命周期结束与度量收口 |
38
- | 关键输出 | `openspec/changes/archive/<日期>-<change>/`、`openspec/specs/`、`openspec/changes/archive/<日期>-<change>/reports/<change>-report.md`、`openspec/changes/archive/<日期>-<change>/reports/<change>-report.html`、`openspec/changes/archive/<日期>-<change>/logs/execution-log.md` |
38
+ | 关键输出 | `openspec/changes/archive/<日期>-<change>/`、`openspec/specs/`、`openspec/changes/archive/<日期>-<change>/reports/<change>-report.md`、`openspec/changes/archive/<日期>-<change>/reports/<change>-report.html`、`openspec/changes/archive/<日期>-<change>/reports/<change>-report.json`、`openspec/changes/archive/<日期>-<change>/logs/execution-log.md` |
39
39
  | 触发时机 | 任务完成、变更取消、变更搁置、或用户要求归档 |
40
40
 
41
41
  ---
@@ -96,9 +96,45 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" tasks-status --project=. --chan
96
96
 
97
97
  即使用户选择“变更已完成实施”,未勾选项也不阻断归档。它可能代表任务真实未完成,也可能代表代码已完成但文档未同步;不要猜测,也不要静默忽略。最终必须让 `archive-docs` 将其写入 `archive_result.task_completion` 和报告。
98
98
 
99
- > **⚠️ P2-2 归档原因反映真实完成度**:`has_incomplete=true` 时,`archive-docs` 会自动将归档原因改写为"部分完成(N 项验收未勾选)",**不得手动传 `--reason=变更已完成实施` 覆盖**。归档 reason 必须与 task_completion 状态一致,避免"已完成实施"与未勾选项矛盾。
99
+ 归档结果必须分别输出:
100
100
 
101
- > **📊 归档前 process_note(U3)**:归档前对 tasks.md checkbox 的勾选、文档补齐等修补动作,**必须**记 `process_note` 事件(`kind=fix`,`target=tasks.md:行号`),让"check 时待确认 N 项 → archive 结案 N 项"的对照可追溯。若归档前存在拦截/失败但无 `process_note`,`sdd-apply-test-gate` 会记 `telemetry_warning(process_note_missing)`。模板见 `opsx-apply/reference.md`「process_note」。
101
+ - `task_completion.primary_tasks`:真正的交付任务,决定“完成/部分完成”结论。
102
+ - `task_completion.acceptance_evidence`:测试、评审、手工验收等证据的确认情况,只提示证据缺口,不冒充任务进度。
103
+
104
+ 仅当 `primary_tasks.has_incomplete=true` 时,归档原因才改写为“部分完成(N 个主任务未完成)”。主任务已完成但验收证据待确认时,允许归档,但最终报告必须在“需要处理”中明确提示。
105
+
106
+ > **📊 归档前 process_note(U3)**:归档前对 tasks.md checkbox 的勾选、文档补齐等修补动作,**必须**记 `process_note` 事件(`kind=recovery`,`target=tasks.md:行号`),让"check 时待确认 N 项 → archive 结案 N 项"的对照可追溯。若归档前存在拦截/失败但无 `process_note`,`sdd-apply-test-gate` 会记 `telemetry_warning(process_note_missing)`。模板见 `opsx-apply/reference.md`「process_note」。
107
+
108
+ ### 4.5 可选记录成熟度证据(仅有可审计证据时)
109
+
110
+ 如需评估 L3/L4,必须在 `archive-docs` 生成最终报告前记录标准 `maturity_evidence`。先将以下 JSON 写入 `skywalk-sdd/state/<变更名称>-maturity-evidence.json`:
111
+
112
+ ```json
113
+ {
114
+ "maturity_evidence": {
115
+ "change_type": "config|document|report|composite|other",
116
+ "e1_actual_ms": 500,
117
+ "e1_target_ms": 600,
118
+ "e1_type_target_met": true,
119
+ "code_fully_spec_generated": true,
120
+ "generated_files": ["src/generated.js"],
121
+ "manual_code_lines": 0,
122
+ "evidence_summary": "规约生成范围与独立复核结论",
123
+ "reviewer": "独立评审者标识",
124
+ "reviewer_independence": "independent-review",
125
+ "review_session_id": "review-session-id",
126
+ "author_session_id": "author-session-id"
127
+ }
128
+ }
129
+ ```
130
+
131
+ 然后执行:
132
+
133
+ ```bash
134
+ node skywalk-sdd/log.cjs record --type=maturity_evidence --command=archive --project=. --change=<变更名称> --agent=<Agent类型> --source=manual --session-id=<会话ID> --result=success --summary="成熟度证据" --details-file=skywalk-sdd/state/<变更名称>-maturity-evidence.json
135
+ ```
136
+
137
+ `e1_type_target_met` 必须等于 `e1_actual_ms <= e1_target_ms` 的计算结果。仅当 `code_fully_spec_generated=true` 时,`generated_files`、`manual_code_lines=0`、证据摘要和独立评审字段才可授予 L4;`review_session_id` 必须与 `author_session_id` 不同。CLI 会硬校验这些字段,只有两个布尔断言的事件不会被报告采信。
102
138
 
103
139
  ### 5. 一步执行真实归档
104
140
 
@@ -117,7 +153,8 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" archive-docs --project=. --chan
117
153
  - Full Spec 的 `specs/<capability>/spec.md` 同步到 `openspec/specs/<capability>/spec.md`。
118
154
  - archive 阶段写入 `stage_end`。
119
155
  - 未勾选 tasks 被写入 `archive_result.task_completion`。
120
- - 最终中文报告生成到 `openspec/changes/archive/<日期>-<name>/reports/<name>-report.md` 及同名 `<name>-report.html`(默认同时生成 .md 与 .html 双产物,默认归档后 archive 目录,可用 --report-output 自定义)。
156
+ - 最终中文报告生成到 `openspec/changes/archive/<日期>-<name>/reports/<name>-report.md`、`<name>-report.html``<name>-report.json`(三份同源报告,默认归档后 archive 目录,可用 --report-output 自定义)。
157
+ - `archive_result` 含 `report_path`、`report_html_path`、`report_json_path`;任一 companion 写入失败须记录 telemetry 警告,主 Markdown 失败仍按现有失败策略处理。
121
158
  - 执行日志 `openspec/changes/archive/<日期>-<name>/logs/execution-log.md` 随归档整目录迁移(人读审计层)。
122
159
 
123
160
  ### 5.4 隐式注销活动变更
@@ -152,7 +189,7 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" end --event-id=<event_id> --com
152
189
 
153
190
  > 变更 `<name>` 已真实归档。
154
191
  > - 归档目录:`openspec/changes/archive/<日期>-<name>/`
155
- > - 最终报告(默认同时生成 .md + .html):`openspec/changes/archive/<日期>-<name>/reports/<name>-report.md`、`openspec/changes/archive/<日期>-<name>/reports/<name>-report.html`
192
+ > - 最终报告(Markdown + HTML + JSON 三产物):`openspec/changes/archive/<日期>-<name>/reports/<name>-report.md`、`<name>-report.html`、`<name>-report.json`
156
193
  > - 执行日志:`openspec/changes/archive/<日期>-<name>/logs/execution-log.md`
157
194
  > - 知识库归档包:`openspec/changes/archive/<日期>-<name>.zip`
158
195
  > - 本体消费入口:`openspec/changes/archive/<日期>-<name>/canonical-facts.json`
@@ -199,7 +236,8 @@ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" record --type=baseline_record -
199
236
  - 归档操作执行前必须让用户确认归档原因。
200
237
  - 不要调用 OpenSpec 自带归档命令;统一由 `archive-docs` 负责真实归档、阶段结束和报告生成。
201
238
  - “完成实施”归档允许 tasks 未全部勾选;未勾选项必须进入 archive details 和最终报告。
202
- - 最终报告由 `archive-docs` 自动生成(md+html 双产物,默认落归档后 archive 目录的 reports/ 子目录,可用 --report-output 自定义路径)。
239
+ - 最终报告由 `archive-docs` 自动生成(Markdown + HTML + JSON 三产物,默认落归档后 archive 目录的 reports/ 子目录,可用 --report-output 自定义路径)。
240
+ - 双周/月度周期跟踪为**归档后或项目级独立入口**,不阻塞单变更归档;示例:`node skywalk-sdd/log.cjs report --project=. --period=biweekly --date-from=YYYY-MM-DD --date-to=YYYY-MM-DD`
203
241
  - 已归档变更不要重复移动;展示已有 archive 目录和 report 路径。
204
242
 
205
243
  ---
@@ -14,6 +14,8 @@ description: "opsx-archive 前后日志/总结自检清单 — 仅在 archive
14
14
  - [ ] `openspec/changes/<变更名称>/logs/execution-log.md` 各阶段条目齐全:propose/spec/design/task/check/apply/test/archive 的 `stage_start`/`stage_end` 闭环
15
15
  - [ ] execution-log 状态标记规范:成功 `✅OK`、部分 `🟡WARN`、失败 `❌FAIL`;未闭环阶段已修复或标记 partial
16
16
  - [ ] 无残留 Apply worktree(`git worktree list` 仅主工作区,或 telemetry 存在 `worktree_finish`+success 事件)
17
+ - [ ] 如需评估 L3/L4,已在归档报告生成前记录标准 `maturity_evidence`;不得只提交 `e1_type_target_met` / `code_fully_spec_generated` 两个布尔断言
18
+ - [ ] `maturity_evidence` 的 E1 实测/目标可复算;声称完整规约生成时,`generated_files` 非空、`manual_code_lines=0`,且 `review_session_id` 与 `author_session_id` 不同
17
19
 
18
20
  ## B. 归档后自检
19
21
 
@@ -24,7 +26,8 @@ description: "opsx-archive 前后日志/总结自检清单 — 仅在 archive
24
26
  - [ ] `openspec/changes/archive/<日期>-<变更名称>.zip` 存在并可由知识库 `ArchivePackageReader` 读取
25
27
  - [ ] canonical facts 中每个实体/关系均能通过 `source.file`、`source.anchor_id`、`source.content_hash` 定向展开到包内原文
26
28
  - [ ] 最终报告 `openspec/changes/archive/<日期>-<变更名称>/reports/<变更名称>-report.md` 存在且含「归档结果」段
27
- - [ ] `reports/<变更名称>-report.md` 与 `reports/<变更名称>-report.html` 都已生成(md+html 双产物)
29
+ - [ ] `reports/<变更名称>-report.md`、`<变更名称>-report.html` 与 `<变更名称>-report.json` 均已生成(Markdown + HTML + JSON 三产物同源)
30
+ - [ ] `archive_result` 或报告「归档结果」段含 `report_path`、`report_html_path`、`report_json_path`
28
31
  - [ ] html 报告含 `SDD 效果度量报告` 标题与各度量章节(执行摘要/效率/质量/过程/归档结果/说明)
29
32
  - [ ] 执行日志 `openspec/changes/archive/<日期>-<变更名称>/logs/execution-log.md` 随 change 整目录迁移到 archive(含 archive 阶段 stage_start+stage_end+✅OK)
30
33
  - [ ] 正式 specs 同步到 `openspec/specs/<capability>/spec.md`