kld-sdd 2.6.13 → 2.6.15

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 (30) hide show
  1. package/README.md +16 -12
  2. package/kld-sdd-guide.html +4 -5
  3. package/lib/init.js +14 -11
  4. package/package.json +2 -2
  5. package/skywalk-sdd/index.cjs +936 -109
  6. package/skywalk-sdd/metrics-v3.cjs +103 -15
  7. package/skywalk-sdd/ontology/archive-package.cjs +6 -0
  8. package/skywalk-sdd/ontology/identity-index.cjs +9 -2
  9. package/skywalk-sdd/ontology/ontology-paths.cjs +73 -0
  10. package/skywalk-sdd/ontology/runtime.cjs +32 -22
  11. package/skywalk-sdd/ontology/structural-identity.cjs +11 -2
  12. package/skywalk-sdd/ontology/traceability-validator.cjs +16 -8
  13. package/skywalk-sdd/ontology/working-artifacts.cjs +2 -1
  14. package/skywalk-sdd/reporting/change-report-markdown.cjs +294 -0
  15. package/skywalk-sdd/reporting/change-report-model.cjs +452 -0
  16. package/skywalk-sdd/reporting/change-report-renderer.cjs +349 -0
  17. package/skywalk-sdd/reporting/change-report-view-model.cjs +340 -0
  18. package/skywalk-sdd/runtime-metadata.cjs +21 -0
  19. package/templates/skills/kld-sdd/opsx-apply/reference.md +4 -2
  20. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +1 -1
  21. package/templates/skills/kld-sdd/opsx-archive/checklist.md +1 -1
  22. package/templates/skills/kld-sdd/opsx-check/SKILL.md +2 -2
  23. package/templates/skills/kld-sdd/opsx-check/checklist.md +1 -1
  24. package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +14 -0
  25. package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +31 -0
  26. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +6 -6
  27. package/templates/skills/kld-sdd/opsx-propose/reference.md +6 -7
  28. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +1 -1
  29. package/templates/skills/kld-sdd/tdd-core/reference.md +3 -1
  30. package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +1 -1
@@ -0,0 +1,294 @@
1
+ 'use strict';
2
+
3
+ const { buildChangeReportViewModel } = require('./change-report-view-model.cjs');
4
+
5
+ function formatValue(value) {
6
+ if (value == null || value === '') return '暂无数据';
7
+ return String(value);
8
+ }
9
+
10
+ function formatRatioToPercent(ratio) {
11
+ if (ratio == null || !Number.isFinite(Number(ratio))) return '暂无数据';
12
+ return `${(Number(ratio) * 100).toFixed(1)}%`;
13
+ }
14
+
15
+ function renderSectionOverview(section, report) {
16
+ const minSet = report.minimum_metric_set || {};
17
+ const lines = [
18
+ '## 一句话结论',
19
+ '',
20
+ `> ${section.summary}`,
21
+ '',
22
+ `## ${section.title}`,
23
+ '',
24
+ '### 摘要卡片',
25
+ ];
26
+ for (const card of section.cards || []) {
27
+ lines.push(`- ${card.label}:${formatValue(card.value)}`);
28
+ }
29
+ if (report.scheme_compliance?.counts) {
30
+ lines.push(
31
+ `- 方案覆盖:${report.scheme_compliance.counts.implemented ?? 0}/${report.scheme_compliance.items?.length ?? 0} 项已完整实现`,
32
+ );
33
+ }
34
+ lines.push(
35
+ '',
36
+ '### 关键指标可信覆盖',
37
+ `- 数值覆盖:${formatRatioToPercent(minSet.numeric_coverage_rate)}(${minSet.numeric_available ?? 0}/${minSet.numeric_total ?? 5})`,
38
+ `- 可信覆盖:${formatRatioToPercent(minSet.trusted_coverage_rate)}(${minSet.trusted_available ?? 0}/${minSet.numeric_total ?? 5};临时 ${minSet.provisional ?? 0} 项)`,
39
+ '',
40
+ '## 核心指标',
41
+ `- 每个场景平均用时(E1):见「指标与阶段」`,
42
+ `- 规约检查得分(Q3):见「指标与阶段」`,
43
+ `- 编码前是否先检查(P4):见「指标与阶段」`,
44
+ );
45
+ return lines;
46
+ }
47
+
48
+ function renderSectionMetrics(section, report) {
49
+ const tdd = section.tdd_pairs || {};
50
+ const reasons = report.metrics?.process?.rework_summary?.reasons;
51
+ const lines = [
52
+ `## ${section.title}`,
53
+ '',
54
+ section.summary,
55
+ '',
56
+ '### 关键指标',
57
+ ];
58
+ for (const metric of section.metric_cards || []) {
59
+ lines.push(`- ${metric.name}(${metric.code}):${metric.value} · ${metric.state}`);
60
+ }
61
+ if (reasons?.by_category) {
62
+ const parts = Object.entries(reasons.by_category)
63
+ .map(([k, v]) => `${k}=${v}`)
64
+ .join(', ');
65
+ lines.push(`- 重复执行原因分布:${parts || '无'}`);
66
+ }
67
+ lines.push(
68
+ '',
69
+ '### TDD 对',
70
+ `- 完整度:${tdd.complete == null || tdd.required == null ? '暂无数据' : `${tdd.complete}/${tdd.required}`}`,
71
+ );
72
+ const incomplete = (tdd.pairs || []).filter(pair => pair.status && pair.status !== 'complete');
73
+ if (incomplete.length > 0) {
74
+ lines.push('- 缺口:');
75
+ for (const pair of incomplete) {
76
+ lines.push(` - ${pair.pair_id}:${pair.status}`);
77
+ }
78
+ }
79
+ const stages = section.stages || [];
80
+ lines.push('', '### 阶段时间线');
81
+ if (stages.length === 0) {
82
+ lines.push('- 无阶段节点');
83
+ } else {
84
+ for (const node of stages.slice(0, 40)) {
85
+ lines.push(
86
+ `- ${node.display_stage} R${node.display_round}:${node.display_result}`
87
+ + `(${node.display_duration})`,
88
+ );
89
+ }
90
+ }
91
+ return lines;
92
+ }
93
+
94
+ function renderSectionIssues(section, report) {
95
+ const lines = [
96
+ `## ${section.title}`,
97
+ '',
98
+ section.summary,
99
+ '',
100
+ ];
101
+ const audit = section.task_evidence_audit || {};
102
+ if (audit.direct_test_backlink || audit.strict_test_coverage) {
103
+ lines.push('### 任务证据四口径');
104
+ lines.push(
105
+ `- 文档完成:${formatValue(audit.document_completion?.completed)}/${formatValue(audit.document_completion?.total)}`,
106
+ );
107
+ lines.push(
108
+ `- 直接回链:${formatValue(audit.direct_test_backlink?.verified)}/${formatValue(audit.direct_test_backlink?.total)}`,
109
+ );
110
+ lines.push(
111
+ `- 严格覆盖:${formatValue(audit.strict_test_coverage?.verified)}/${formatValue(audit.strict_test_coverage?.total)}`,
112
+ );
113
+ lines.push('');
114
+ }
115
+ lines.push('### 问题类别');
116
+ const categories = section.categories || [];
117
+ if (categories.length === 0) {
118
+ lines.push('- 当前没有归并后的问题类别。');
119
+ } else {
120
+ for (const group of categories) {
121
+ lines.push(
122
+ `- ${group.label || group.code}:命中 ${group.occurrence_count || 0} 次,关联任务 ${(group.affected_tasks || []).length} 个`,
123
+ );
124
+ }
125
+ }
126
+ const findings = section.apply_findings || [];
127
+ const issues = section.issues || [];
128
+ if (issues.length > 0) {
129
+ lines.push('', '### 问题明细');
130
+ for (const issue of issues) {
131
+ lines.push(
132
+ `- [${issue.code}] ${issue.subject || '本次变更'} · 状态:${issue.status} · 判定:${issue.verdict}`,
133
+ ` - 根因:${issue.root_cause}`,
134
+ ` - 预期:${issue.expected}`,
135
+ ` - 实际:${issue.actual}`,
136
+ ` - 影响:${issue.impact}`,
137
+ ` - 修复建议:${issue.remediation}`,
138
+ ` - 证据事件:${(issue.evidence_event_ids || []).join(', ') || '暂无可定位事件'}`,
139
+ );
140
+ }
141
+ } else if (findings.length > 0) {
142
+ lines.push('', '### 审计发现');
143
+ for (const finding of findings) {
144
+ lines.push(`- [${finding.code}] ${finding.task_id || '变更'}:${finding.message || ''}`);
145
+ }
146
+ }
147
+ const dispositions = section.warning_dispositions
148
+ || section.report_warning_dispositions
149
+ || [];
150
+ if (dispositions.length > 0) {
151
+ lines.push('', '### 告警处置');
152
+ for (const item of dispositions) {
153
+ lines.push(`- [告警处置] ${item.warning || item.code || 'UNKNOWN'}:${item.disposition || 'open'}`);
154
+ }
155
+ }
156
+
157
+ const known = report.known_risks || {};
158
+ lines.push('', '### 已知风险');
159
+ lines.push(`- 数据可用:${known._available ? '是' : '否'}`);
160
+ lines.push(
161
+ `- 严重问题数:${known._available ? (known.severe_issues?.length ?? 0) : '无数据'}`,
162
+ );
163
+ if (Array.isArray(known.warnings) && known.warnings.length > 0) {
164
+ for (const warning of known.warnings) {
165
+ const items = Array.isArray(warning.items) ? warning.items : null;
166
+ const count = items ? items.length : (warning.warnings || 0);
167
+ const statusLabel = warning.resolved === true ? '✅已修复' : '⏳待确认';
168
+ lines.push(`- 警告:${warning.command || 'check'} 阶段 ${count} 个${statusLabel}`);
169
+ if (items && items.length > 0) {
170
+ for (const item of items) {
171
+ lines.push(
172
+ ` - ${item.description || '待确认项'}${item.target ? `(${item.target})` : ''}`,
173
+ );
174
+ }
175
+ }
176
+ }
177
+ } else {
178
+ lines.push('- 无警告(无需修复)');
179
+ }
180
+
181
+ const notes = report.process_notes || {};
182
+ lines.push('', '## 过程记录');
183
+ lines.push(`- 数据可用:${notes._available ? '是' : '否'}`);
184
+ lines.push(`- 过程事件总数:${notes.total ?? 'null'}`);
185
+ if (notes.by_kind) {
186
+ lines.push(`- 按 kind 分布:${JSON.stringify(notes.by_kind)}`);
187
+ }
188
+ if (Array.isArray(notes.notes) && notes.notes.length > 0) {
189
+ for (const note of notes.notes) {
190
+ lines.push(
191
+ `- [${note.stage || 'unknown'}] ${note.kind || 'note'}${note.round ? ` R${note.round}` : ''}:${note.summary || ''}${note.target ? ` → ${note.target}` : ''}`,
192
+ );
193
+ }
194
+ } else {
195
+ lines.push('- 无过程事件');
196
+ }
197
+ return lines;
198
+ }
199
+
200
+ function renderSectionArtifacts(section, report) {
201
+ const changed = section.changed_files || {};
202
+ const rawChanged = report.changed_files || {};
203
+ const archive = report.archive_result || null;
204
+ const taskCompletion = archive?.task_completion || null;
205
+ const lines = [
206
+ `## ${section.title}`,
207
+ '',
208
+ section.summary,
209
+ '',
210
+ '### 变更文件',
211
+ `- 数据可用:${rawChanged._available ? '是' : '否'}`,
212
+ `- 最终变更文件数:${formatValue(changed.files_changed)}`,
213
+ `- 新增行数:${formatValue(changed.added_lines)}`,
214
+ `- 对账状态:${formatValue(changed.reconciliation_status)}`,
215
+ ];
216
+ if ((changed.only_in_task_events || []).length > 0) {
217
+ lines.push(`- 仅任务事件:${changed.only_in_task_events.join(', ')}`);
218
+ }
219
+ if ((changed.only_in_final_output || []).length > 0) {
220
+ lines.push(`- 仅最终输出:${changed.only_in_final_output.join(', ')}`);
221
+ }
222
+ lines.push('', '### 产物清单');
223
+ const artifacts = section.artifacts || [];
224
+ if (artifacts.length === 0) {
225
+ lines.push('- 暂无产物条目');
226
+ } else {
227
+ for (const item of artifacts) {
228
+ const size = item.size_display
229
+ || '—';
230
+ lines.push(
231
+ `- ${item.name}:${item.exists ? '已生成' : '未生成'} · ${item.path || '—'} · ${size}`,
232
+ );
233
+ }
234
+ }
235
+ const copied = report.artifacts?.copied_specs;
236
+ lines.push(
237
+ `- 迁移文件数:${Array.isArray(copied) ? copied.length : 'null'}`,
238
+ );
239
+ lines.push(
240
+ '',
241
+ '### 归因审计',
242
+ `- 显式关联历史事件:${section.attribution_summary?.linked_source_event_count ?? 0}`,
243
+ `- 有效关联:${section.attribution_summary?.active_link_count ?? 0}`,
244
+ `- 未归属候选:${section.attribution_summary?.unassigned_candidate_count ?? 0}`,
245
+ `- 被拒绝的跨变更碰撞:${section.attribution_summary?.rejected_collision_count ?? 0}`,
246
+ '',
247
+ '### 归档结果',
248
+ `- 归档原因:${archive?.reason || 'null'}`,
249
+ `- 归档方式:${archive?.method || 'null'}`,
250
+ `- 归档目录:${archive?.archive_path || report.artifacts?.archive_path || 'null'}`,
251
+ `- 归档清单:${archive?.manifest_path || report.artifacts?.manifest_path || 'null'}`,
252
+ `- 最终报告:${archive?.report_path || 'null'}`,
253
+ `- 主任务完成:${taskCompletion?.primary_tasks
254
+ ? `${taskCompletion.primary_tasks.completed}/${taskCompletion.primary_tasks.total}`
255
+ : 'null'}`,
256
+ `- 全部 checkbox 完成:${taskCompletion?.completed ?? 'null'}/${taskCompletion?.total ?? 'null'}(含验收子项;分母≠主任务数)`,
257
+ `- 未勾选 checkbox:${taskCompletion?.incomplete ?? 'null'}`,
258
+ `- 未勾选任务项:${taskCompletion?.incomplete ?? 'null'}`,
259
+ );
260
+ return lines;
261
+ }
262
+
263
+ function renderChangeReportMarkdown(report) {
264
+ const view = buildChangeReportViewModel(report);
265
+ const projectLabel = report.project_root
266
+ ? String(report.project_root).replace(/[\\/]+$/, '').split(/[\\/]/).pop()
267
+ : null;
268
+ const lines = [
269
+ `# SDD 效果度量报告${view.change ? ` - ${view.change}` : ''}`,
270
+ '',
271
+ `- 生成时间:${report.generated_at || '未知'}`,
272
+ ...(projectLabel ? [`- 项目路径:${projectLabel}`] : []),
273
+ `- 统计范围:change/${view.change || 'unknown'}`,
274
+ `- 报告 Schema:${view.schema_version || 'unknown'}(指标契约 ${view.metrics_contract || 'unknown'})`,
275
+ `<!-- sdd-semantic-digest:${view.semantic_digest} -->`,
276
+ '',
277
+ ];
278
+
279
+ for (const sectionId of view.section_order) {
280
+ const section = view.sections[sectionId];
281
+ if (!section) continue;
282
+ if (sectionId === 'overview') lines.push(...renderSectionOverview(section, report));
283
+ else if (sectionId === 'metrics-stages') lines.push(...renderSectionMetrics(section, report));
284
+ else if (sectionId === 'issues-evidence') lines.push(...renderSectionIssues(section, report));
285
+ else if (sectionId === 'artifacts-trace') lines.push(...renderSectionArtifacts(section, report));
286
+ lines.push('');
287
+ }
288
+
289
+ return lines.join('\n');
290
+ }
291
+
292
+ module.exports = {
293
+ renderChangeReportMarkdown,
294
+ };