kld-sdd 2.6.11 → 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 (87) hide show
  1. package/README.md +20 -0
  2. package/kld-sdd-guide.html +429 -159
  3. package/lib/hook-gate-core.js +41 -5
  4. package/lib/init.js +101 -95
  5. package/package.json +5 -2
  6. package/skywalk-sdd/index.cjs +2668 -415
  7. package/skywalk-sdd/metrics-v3.cjs +1153 -0
  8. package/skywalk-sdd/ontology/archive-package.cjs +114 -3
  9. package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -80
  10. package/skywalk-sdd/ontology/working-artifacts.cjs +2 -1
  11. package/templates/hooks/claude/hooks/sdd-apply-test-gate.cjs +39 -9
  12. package/templates/hooks/claude/hooks/sdd-post-tool.cjs +87 -8
  13. package/templates/hooks/claude/hooks/sdd-prompt.cjs +25 -5
  14. package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +39 -9
  15. package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +87 -9
  16. package/templates/hooks/codebuddy/hooks/sdd-prompt.cjs +24 -3
  17. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +17 -16
  18. package/templates/skills/kld-sdd/opsx-apply/checklist.md +22 -22
  19. package/templates/skills/kld-sdd/opsx-apply/implementer-prompt.md +12 -12
  20. package/templates/skills/kld-sdd/opsx-apply/reference.md +43 -42
  21. package/templates/skills/kld-sdd/opsx-apply/worktree-setup.md +3 -3
  22. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +58 -20
  23. package/templates/skills/kld-sdd/opsx-archive/checklist.md +6 -3
  24. package/templates/skills/kld-sdd/opsx-check/SKILL.md +30 -22
  25. package/templates/skills/kld-sdd/opsx-check/checklist.md +7 -6
  26. package/templates/skills/kld-sdd/opsx-design/SKILL.md +5 -5
  27. package/templates/skills/kld-sdd/opsx-design/checklist.md +1 -1
  28. package/templates/skills/kld-sdd/opsx-design/reference.md +2 -2
  29. package/templates/skills/kld-sdd/opsx-explore/SKILL.md +5 -5
  30. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +20 -12
  31. package/templates/skills/kld-sdd/opsx-propose/reference.md +31 -5
  32. package/templates/skills/kld-sdd/opsx-rules/SKILL.md +3 -3
  33. package/templates/skills/kld-sdd/opsx-rules/reference.md +3 -1
  34. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +6 -6
  35. package/templates/skills/kld-sdd/opsx-spec/checklist.md +1 -1
  36. package/templates/skills/kld-sdd/opsx-spec/reference.md +2 -2
  37. package/templates/skills/kld-sdd/opsx-task/SKILL.md +14 -14
  38. package/templates/skills/kld-sdd/opsx-task/checklist.md +3 -3
  39. package/templates/skills/kld-sdd/opsx-task/reference.md +5 -5
  40. package/templates/skills/kld-sdd/opsx-test/SKILL.md +11 -9
  41. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/SKILL.md +4 -4
  42. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/reference.md +4 -4
  43. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/SKILL.md +13 -13
  44. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/checklist.md +5 -5
  45. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/reference.md +12 -12
  46. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/SKILL.md +3 -3
  47. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/checklist.md +2 -2
  48. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/SKILL.md +2 -2
  49. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/parameterized-testing.md +1 -1
  50. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/SKILL.md +5 -5
  51. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/checklist.md +5 -5
  52. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/SKILL.md +3 -3
  53. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/non-tdd-modules.md +1 -1
  54. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/refactor-checklist.md +1 -1
  55. package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +19 -0
  56. package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-skeleton-telemetry.md +0 -19
  57. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/cause-effect-clarity.md +0 -0
  58. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/clean-test-data.md +0 -0
  59. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/existing-test-awareness.md +0 -0
  60. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/given-when-then.md +0 -0
  61. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/good-test-qualities.md +0 -0
  62. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/mock-boundary.md +0 -0
  63. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/naming-conventions.md +0 -0
  64. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/no-logic-in-tests.md +0 -0
  65. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/one-test-one-scenario.md +0 -0
  66. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/prefer-public-apis.md +0 -0
  67. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/test-behaviors-not-methods.md +0 -0
  68. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/argument-matching.md +0 -0
  69. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/controller-test-rules.md +0 -0
  70. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/domain-service-rules.md +0 -0
  71. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/java-test-template.md +0 -0
  72. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/json-serialization.md +0 -0
  73. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/logging-rules.md +0 -0
  74. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/compilation-verification.md +0 -0
  75. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/execution-verification.md +0 -0
  76. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/python/py-test-template.md +0 -0
  77. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/typescript/ts-test-template.md +0 -0
  78. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/controller-strategy.md +0 -0
  79. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/dag-generation-rules.md +0 -0
  80. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/des-step-annotation.md +0 -0
  81. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/exception-path-coverage.md +0 -0
  82. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-scope-declaration.md +0 -0
  83. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-yagni-fence.md +0 -0
  84. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/multi-validation-split.md +0 -0
  85. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/task-type-definitions.md +0 -0
  86. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/tdd-strategy-selection.md +0 -0
  87. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/test-execution-gate.md +0 -0
@@ -74,6 +74,26 @@ function isRealTestDetails(testResults) {
74
74
  return false;
75
75
  }
76
76
 
77
+ function isPassingTestDetails(testResults) {
78
+ if (!isRealTestDetails(testResults)) return false;
79
+ const failed = Number(testResults.failed);
80
+ const exitCode = Number(testResults.exit_code);
81
+ return (!Number.isFinite(failed) || failed === 0)
82
+ && (!Number.isFinite(exitCode) || exitCode === 0);
83
+ }
84
+
85
+ function isStrictTestEvidence(event, changeName) {
86
+ const testResults = event?.details?.test_results;
87
+ if (event?.type !== 'test_result' || event.change !== changeName) return false;
88
+ if (!event.run_id || !event.source || testResults?.evidence_tier !== 'strict') return false;
89
+ if (!['green', 'refactor', 'regression'].includes(testResults.tdd_phase)) return false;
90
+ if (!Number.isInteger(testResults.exit_code) || testResults.counts_known !== true) return false;
91
+ return event.result === 'success'
92
+ && testResults.exit_code === 0
93
+ && Number(testResults.failed) === 0
94
+ && isPassingTestDetails(testResults);
95
+ }
96
+
77
97
  function loadChangeEvents(projectRoot, changeName) {
78
98
  const safe = safeChangeName(changeName);
79
99
  const dir = path.join(projectRoot, 'skywalk-sdd', 'events', safe);
@@ -100,33 +120,36 @@ function loadChangeEvents(projectRoot, changeName) {
100
120
  function hasRealTestExecution(projectRoot, changeName, capability, sinceTimestamp) {
101
121
  const since = sinceTimestamp ? new Date(sinceTimestamp).getTime() : 0;
102
122
  const events = loadChangeEvents(projectRoot, changeName);
123
+ let legacyEvidence = null;
103
124
 
104
125
  for (const event of events) {
105
126
  const ts = new Date(event.timestamp || 0).getTime();
106
127
  if (since && ts < since) continue;
107
128
 
108
- if (event.type === 'stage_end' && event.command === 'test' &&
109
- (event.result === 'success' || event.result === 'failure')) {
110
- return { kind: 'opsx-test', event };
129
+ if (event.type === 'stage_end' && event.command === 'test' && event.result === 'success') {
130
+ legacyEvidence ||= { kind: 'legacy-opsx-test', event };
111
131
  }
112
132
 
113
133
  if (event.type === 'test_result') {
114
134
  const tr = event.details && event.details.test_results;
115
- if (isRealTestDetails(tr)) {
116
- return { kind: 'test_result', event };
135
+ if (isStrictTestEvidence(event, changeName)) {
136
+ return { kind: 'strict-test-result', event };
137
+ }
138
+ if (event.result === 'success' && isPassingTestDetails(tr)) {
139
+ legacyEvidence ||= { kind: 'legacy-test-result', event };
117
140
  }
118
141
  }
119
142
 
120
143
  if (event.type === 'task_update' && (event.command === 'apply' || !event.command)) {
121
144
  const tr = event.details && event.details.test_results;
122
- if (isRealTestDetails(tr)) {
145
+ if (event.result !== 'failure' && isPassingTestDetails(tr)) {
123
146
  if (!capability || !event.capability || event.capability === capability) {
124
- return { kind: 'task_update', event };
147
+ legacyEvidence ||= { kind: 'legacy-task-update', event };
125
148
  }
126
149
  }
127
150
  }
128
151
  }
129
- return null;
152
+ return legacyEvidence;
130
153
  }
131
154
 
132
155
  function isApplyCompletionBash(command) {
@@ -210,9 +233,10 @@ function shouldGate(projectRoot, applyEvent) {
210
233
  applyEvent.capability,
211
234
  applyEvent.timestamp
212
235
  );
213
- if (evidence) {
236
+ if (evidence?.kind === 'strict-test-result') {
214
237
  return null;
215
238
  }
239
+ if (evidence) return { strategy, changeName: applyEvent.change, capability: applyEvent.capability, legacyEvidence: evidence };
216
240
  return { strategy, changeName: applyEvent.change, capability: applyEvent.capability };
217
241
  }
218
242
 
@@ -374,6 +398,11 @@ if (require.main === module) {
374
398
  if (!gate) {
375
399
  process.exit(0);
376
400
  }
401
+ if (gate.legacyEvidence) {
402
+ recordWarning(projectRoot, { ...activeApply, test_strategy: gate.strategy }, 'legacy_test_evidence',
403
+ '仅检测到旧版测试证据;本次兼容放行,请补录包含 run-id、exit code、TDD phase 和严格身份字段的 test_result');
404
+ process.exit(0);
405
+ }
377
406
 
378
407
  const reason = buildReason(gate.strategy, gate.changeName, gate.capability);
379
408
  recordWarning(projectRoot, { ...activeApply, test_strategy: gate.strategy }, 'apply_test_missing', 'Apply test gate blocked: no unit test evidence');
@@ -391,5 +420,6 @@ module.exports = {
391
420
  detectMissingTddRed,
392
421
  hasExistingWarning,
393
422
  isRealTestDetails,
423
+ isStrictTestEvidence,
394
424
  requiresUnitTests,
395
425
  };
@@ -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
+ };
@@ -13,8 +13,27 @@ if (!/\/opsx[:|-]/.test(prompt)) {
13
13
  process.exit(0);
14
14
  }
15
15
 
16
+ function resolveSkywalkDir(dir) {
17
+ const direct = path.join(dir, 'skywalk-sdd');
18
+ if (fs.existsSync(path.join(direct, 'log.cjs')) || fs.existsSync(path.join(direct, 'log.js'))) {
19
+ return direct;
20
+ }
21
+ const hintFile = path.join(dir, '.sdd-spec-root');
22
+ if (fs.existsSync(hintFile)) {
23
+ const rel = String(fs.readFileSync(hintFile, 'utf8') || '').trim().split(/\r?\n/)[0].trim();
24
+ if (rel) {
25
+ const nested = path.join(dir, rel, 'skywalk-sdd');
26
+ if (fs.existsSync(path.join(nested, 'log.cjs')) || fs.existsSync(path.join(nested, 'log.js'))) {
27
+ return nested;
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+
16
34
  function readActiveStages(projectRoot) {
17
- const stateDir = path.join(projectRoot, 'skywalk-sdd', 'state');
35
+ const skywalk = resolveSkywalkDir(projectRoot);
36
+ const stateDir = skywalk ? path.join(skywalk, 'state') : path.join(projectRoot, 'skywalk-sdd', 'state');
18
37
  if (!fs.existsSync(stateDir)) return [];
19
38
  return fs.readdirSync(stateDir)
20
39
  .filter((file) => file.endsWith('.json'))
@@ -35,8 +54,10 @@ const activeStages = readActiveStages(projectRoot)
35
54
 
36
55
  const lines = [
37
56
  'SDD Telemetry reminder:',
38
- '- Run skywalk-sdd/log.cjs start before the OPSX stage work begins.',
39
- '- Run skywalk-sdd/log.cjs end before stopping the stage.',
57
+ '- Resolve the exact change key first; every standard OPSX start/end command must include --change=<change-key>.',
58
+ '- Run node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" start before the OPSX stage work begins.',
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.',
40
61
  '- Hooks are only an enhancement; OPSX skill instructions remain authoritative.',
41
62
  ];
42
63
 
@@ -27,18 +27,19 @@ allowed-tools:
27
27
  > - ⛔ **隔离红线**:绝对禁止加载同级其他 Capability 的文档
28
28
 
29
29
  > **🖥️ 跨平台执行规则**
30
- > - **写代码**:cwd 保持在 Git 根(含 `src/`);**文档 / telemetry**:`--project` 指向 SDD 包裹包(`node skywalk-sdd/ontology/cli.cjs spec-root` 可查看)。
31
- > - `openspec` 命令:优先 `node skywalk-sdd/openspec-shim.cjs list`(自动 cd 到包裹包)。
30
+ > - **写代码**:cwd 保持在 Git 根(含 `src/`);**文档 / telemetry**:`--project` 指向 SDD 包裹包(`node "$(cat .sdd-spec-root)/skywalk-sdd/ontology/cli.cjs" spec-root` 可查看)。
31
+ > - `openspec` 命令:优先 `node "$(cat .sdd-spec-root)/skywalk-sdd/openspec-shim.cjs" list`(自动 cd 到包裹包)。
32
32
  > - Telemetry 命令默认使用 `--project=.`,兼容 Windows、macOS、Linux。
33
33
  > - ${SHELL_GUIDANCE}
34
34
  > - 不要省略 `--source=opsx-command` 与 `--session-id=<会话ID>`。
35
35
  > **📊 Telemetry(必做,不得跳过)**
36
36
  > - 阶段开始 / 阶段结束 / `task_update` / `ai_adoption_review` / `worktree_finish` 的**完整命令模板**见 `./reference.md`「📊 Telemetry 命令模板」。
37
37
  > - 不得跳过任何 telemetry 记录;`task_update` 的 `--task-id=<TASK-ID>` 必须替换为实际任务 ID,否则 E4 指标无法计算;TDD 测试骨架任务须在 `--details-json` 带 `"task_kind":"test-skeleton"`(P3,详见 `./reference.md`)。
38
- > - 较大的 `--details-json` 负载可先写入文件,再通过 `--details-file=skywalk-sdd/state/<变更名称>-<type>.json` 传递(例如 `task_update` 对应 `<变更名称>-task-update.json`)。
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
 
@@ -14,7 +14,7 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
14
14
  ### §1.2 Check 门禁(apply 前必做)
15
15
 
16
16
  - [ ] 读取 telemetry 事件数据,检查当前 change 是否已有完成的 check 阶段
17
- - `node skywalk-sdd/log.cjs doctor --project=. --change=<change-name>` 或搜 `skywalk-sdd/data/events/<change-name>/` 的 `stage_end` + `command=check` + `result=success`
17
+ - `node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" doctor --project=. --change=<change-name>` 或搜 `skywalk-sdd/data/events/<change-name>/` 的 `stage_end` + `command=check` + `result=success`
18
18
  - [ ] check 已完成 ✅ → 继续 apply 流程
19
19
  - [ ] check 未完成 ❌ → **强制拒绝** apply,引导用户先执行 `/opsx-check <change-name>`(不得跳过)
20
20
  - [ ] telemetry 数据缺失(新项目无历史) → 提示首次执行先运行 `/opsx-check`,询问是否现在执行
@@ -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 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
@@ -112,7 +112,7 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
112
112
  - [ ] 主仓库无与 merge 冲突的未跟踪文件(见 §1.5 Step 0.5)
113
113
  - [ ] `test-strategy` 为 `tdd`/`impl-first` 时,§6.0 测试证据已存在
114
114
  - [ ] 在**主仓库根目录**执行收尾脚本(禁止在 `.worktrees/...` 内执行)
115
- - [ ] 执行:`node skywalk-sdd/apply-worktree-finish.cjs --change=<变更名称> --capability=<capability-name>`
115
+ - [ ] 执行:`node "$(cat .sdd-spec-root)/skywalk-sdd/apply-worktree-finish.cjs" --change=<变更名称> --capability=<capability-name>`
116
116
  - [ ] 默认 merge 目标 = §1.5 `--record-base` 写入的 `integration_base`(**不是**默认 master)
117
117
  - [ ] 若 §1.5 判定未建 worktree(串行、强依赖、沙箱回退)→ 跳过本节
118
118
 
@@ -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. 报告结果