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.
- package/README.md +20 -0
- package/kld-sdd-guide.html +429 -159
- package/lib/init.js +40 -7
- package/package.json +5 -2
- package/skywalk-sdd/index.cjs +2668 -415
- package/skywalk-sdd/metrics-v3.cjs +1153 -0
- package/skywalk-sdd/ontology/archive-package.cjs +114 -3
- package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -80
- package/skywalk-sdd/ontology/working-artifacts.cjs +2 -1
- package/templates/hooks/claude/hooks/sdd-apply-test-gate.cjs +39 -9
- package/templates/hooks/claude/hooks/sdd-post-tool.cjs +87 -8
- package/templates/hooks/claude/hooks/sdd-prompt.cjs +3 -1
- package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +39 -9
- package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +87 -9
- package/templates/hooks/codebuddy/hooks/sdd-prompt.cjs +3 -1
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +14 -13
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +20 -20
- package/templates/skills/kld-sdd/opsx-apply/implementer-prompt.md +12 -12
- package/templates/skills/kld-sdd/opsx-apply/reference.md +32 -31
- package/templates/skills/kld-sdd/opsx-archive/SKILL.md +44 -6
- package/templates/skills/kld-sdd/opsx-archive/checklist.md +4 -1
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +19 -11
- package/templates/skills/kld-sdd/opsx-check/checklist.md +7 -6
- package/templates/skills/kld-sdd/opsx-design/SKILL.md +1 -1
- package/templates/skills/kld-sdd/opsx-design/checklist.md +1 -1
- package/templates/skills/kld-sdd/opsx-propose/SKILL.md +9 -1
- package/templates/skills/kld-sdd/opsx-propose/reference.md +29 -3
- package/templates/skills/kld-sdd/opsx-rules/reference.md +3 -1
- package/templates/skills/kld-sdd/opsx-spec/SKILL.md +1 -1
- package/templates/skills/kld-sdd/opsx-spec/checklist.md +1 -1
- package/templates/skills/kld-sdd/opsx-task/SKILL.md +10 -10
- package/templates/skills/kld-sdd/opsx-task/checklist.md +3 -3
- package/templates/skills/kld-sdd/opsx-task/reference.md +3 -3
- package/templates/skills/kld-sdd/opsx-test/SKILL.md +7 -5
- package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/SKILL.md +4 -4
- package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/reference.md +4 -4
- package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/SKILL.md +13 -13
- package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/checklist.md +5 -5
- package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/reference.md +12 -12
- package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/SKILL.md +3 -3
- package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/checklist.md +2 -2
- package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/SKILL.md +2 -2
- package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/parameterized-testing.md +1 -1
- package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/SKILL.md +5 -5
- package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/checklist.md +5 -5
- package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/SKILL.md +3 -3
- package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/non-tdd-modules.md +1 -1
- package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/refactor-checklist.md +1 -1
- package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +19 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-skeleton-telemetry.md +0 -19
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/cause-effect-clarity.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/clean-test-data.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/existing-test-awareness.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/given-when-then.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/good-test-qualities.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/mock-boundary.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/naming-conventions.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/no-logic-in-tests.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/one-test-one-scenario.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/prefer-public-apis.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/test-behaviors-not-methods.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/argument-matching.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/controller-test-rules.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/domain-service-rules.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/java-test-template.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/json-serialization.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/logging-rules.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/compilation-verification.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/execution-verification.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/python/py-test-template.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/typescript/ts-test-template.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/controller-strategy.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/dag-generation-rules.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/des-step-annotation.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/exception-path-coverage.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-scope-declaration.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-yagni-fence.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/multi-validation-split.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/task-type-definitions.md +0 -0
- /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/tdd-strategy-selection.md +0 -0
- /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=${
|
|
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 = {
|
|
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
|
|
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
|
-
> -
|
|
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 任务完成后,可派发独立审查子代理执行 `
|
|
209
|
+
6. **可选:TDD 审查子代理**:GREEN 任务完成后,可派发独立审查子代理执行 `tdd-review/SKILL.md` §7(子代理审查提示模板),检测"测试通过但没测到关键点"。implementer 自审查 ≠ 独立审查。
|
|
209
210
|
7. 再进入下一个任务
|
|
210
211
|
|
|
211
|
-
> 完整执行步骤见
|
|
212
|
-
> 合理化预防表见
|
|
213
|
-
> REFACTOR 检查点见
|
|
214
|
-
> 异常路径覆盖门禁见
|
|
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 边界(引用
|
|
221
|
-
- 测试命名与结构(引用
|
|
222
|
-
- 断言深度(引用
|
|
223
|
-
- RED 阶段反模式检查(引用
|
|
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
|
-
-
|
|
227
|
+
- tdd-anti-patterns/SKILL.md §3(RED 阶段 3 种反模式 + 门禁函数)
|
|
227
228
|
读取后在报告中确认:"已读取 RED 阶段反模式检查表"。
|
|
228
229
|
|
|
229
|
-
> 完整 Mock 边界矩阵见
|
|
230
|
-
> 完整 15 种反模式见
|
|
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
|
-
> 测试执行门禁策略见
|
|
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 任务后,必须执行 `
|
|
45
|
+
⛔ 每完成一个 RED/GREEN/REFACTOR 任务后,必须执行 `tdd-core/checklist.md` §A(11 项)逐项勾选。
|
|
46
46
|
|
|
47
|
-
> 不在此内联复制,以
|
|
48
|
-
> 额外补充:REFACTOR 任务还需执行 `
|
|
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
|
-
⛔ 核心原则(引用
|
|
57
|
+
⛔ 核心原则(引用 tdd-quality/SKILL.md §2):Mock 边界,不 Mock 行为
|
|
58
58
|
- [ ] 未 mock 被测行为本身
|
|
59
59
|
- [ ] Mock 仅用于系统边界依赖(Mapper/HTTP)
|
|
60
60
|
- [ ] RED 测试 Given 是真实输入
|
|
61
61
|
|
|
62
|
-
⛔ 测试命名与结构(引用
|
|
62
|
+
⛔ 测试命名与结构(引用 tdd-quality/SKILL.md §3-§4):
|
|
63
63
|
- [ ] 测试方法名符合 `{method}_{state}_{outcome}` 格式
|
|
64
64
|
- [ ] 测试包含 `// Given` / `// When` / `// Then` 注释结构
|
|
65
65
|
- [ ] 一测一场景(测试方法名不含 "and")
|
|
66
66
|
|
|
67
|
-
⛔ 断言深度(引用
|
|
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
|
-
-
|
|
72
|
+
- tdd-anti-patterns/SKILL.md §3(RED 阶段反模式检查)
|
|
73
73
|
读取后确认:"已检查 RED 阶段反模式"。
|
|
74
74
|
|
|
75
|
-
> 完整质量门禁见
|
|
76
|
-
> 完整 15 种反模式见
|
|
75
|
+
> 完整质量门禁见 tdd-quality/SKILL.md §2-§6
|
|
76
|
+
> 完整 15 种反模式见 tdd-anti-patterns/SKILL.md §3-§4
|
|
77
77
|
|
|
78
78
|
### 任务状态实时更新
|
|
79
79
|
|
|
80
|
-
- [ ]
|
|
81
|
-
- [ ]
|
|
82
|
-
- [ ]
|
|
80
|
+
- [ ] 每个主任务只保留一条规范状态行:`- **状态**: [ ] 未完成` → `- **状态**: [x] 已完成`
|
|
81
|
+
- [ ] 自动同步仅更新任务块内的 `- **状态**:` 行,不改标题复选框、场景列表或验收清单
|
|
82
|
+
- [ ] “验收证据”与主任务进度分开统计;必须由真实测试、人工验收或评审证据确认,不自动勾选
|
|
83
83
|
- [ ] 显示进度:`✅ [TASK-ID] 已完成 [N/M]`
|
|
84
|
-
- [ ]
|
|
85
|
-
- [ ] ⛔ **task_update
|
|
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`
|
|
92
|
-
- [ ]
|
|
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
|
-
> 单元测试真实执行自检见
|
|
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(引用
|
|
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/边界检查须有对应测试方法(见 `
|
|
17
|
+
5. ⛔ 检查异常路径覆盖:每个 orElseThrow/边界检查须有对应测试方法(见 `tdd-rules/rules/exception-path-coverage.md`)
|
|
18
18
|
|
|
19
19
|
⛔ BEFORE 编写测试代码,必须读取对应语言的规则文件:
|
|
20
|
-
- Java:
|
|
21
|
-
- TypeScript:
|
|
22
|
-
- Python:
|
|
23
|
-
- 通用:
|
|
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 声明】按 `
|
|
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
|
-
> 完整执行步骤见
|
|
35
|
-
> 完整质量标准见
|
|
36
|
-
> Scope 声明规则见
|
|
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. ⛔ 执行 `
|
|
44
|
+
6. ⛔ 执行 `tdd-rules/rules/refactor-checklist.md`(7 项重构检查点)
|
|
45
45
|
|
|
46
|
-
> 完整 REFACTOR 检查点见
|
|
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 的失败原因,按 `
|
|
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="{\"
|
|
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
|
|
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 模板见
|
|
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
|
-
|
|
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
|
|
51
|
+
记录阶段内过程信息,供 execution-log 叙事与报告“过程记录”板块统计。新事件使用 `--strict`,`details.kind` 必填,枚举:
|
|
51
52
|
|
|
52
53
|
| kind | 场景 | 示例 |
|
|
53
54
|
|------|------|------|
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
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\":\"<
|
|
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
|
-
-
|
|
67
|
-
-
|
|
68
|
-
- **API
|
|
69
|
-
-
|
|
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
|
-
|
|
|
76
|
-
| frontmatter
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
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
|
-
> 单元测试真实执行见
|
|
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 种反模式检测见
|
|
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
|
-
|
|
99
|
+
归档结果必须分别输出:
|
|
100
100
|
|
|
101
|
-
|
|
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
|
|
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
|
-
> -
|
|
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` 自动生成(
|
|
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` 与
|
|
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`
|