kld-sdd 2.6.14 → 2.6.16
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 +7 -5
- package/lib/init.js +118 -73
- package/lib/skills-bundle.js +3 -0
- package/package.json +3 -2
- package/skywalk-sdd/index.cjs +936 -109
- package/skywalk-sdd/metrics-v3.cjs +103 -15
- package/skywalk-sdd/openspec-shim.cjs +48 -0
- package/skywalk-sdd/reporting/change-report-markdown.cjs +294 -0
- package/skywalk-sdd/reporting/change-report-model.cjs +170 -27
- package/skywalk-sdd/reporting/change-report-renderer.cjs +78 -154
- package/skywalk-sdd/reporting/change-report-view-model.cjs +340 -0
- package/templates/git-hooks/commit-msg +46 -0
- package/templates/git-hooks/pre-commit +57 -0
- package/templates/git-hooks/pre-commit-consistency-check.cjs +193 -0
- package/templates/git-hooks/pre-push +57 -0
- package/templates/git-hooks/pre-push-consistency-check.cjs +197 -0
- package/templates/hooks/codebuddy/hooks/hook-gate-core.cjs +369 -0
- package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +41 -0
- package/templates/hooks/codebuddy/hooks/sdd-mid-checkpoint.cjs +108 -0
- package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +36 -1
- package/templates/hooks/codebuddy/hooks/sdd-tdd-rhythm-gate.cjs +248 -0
- package/templates/hooks/codebuddy/settings.json +8 -0
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +13 -0
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +15 -0
- package/templates/skills/kld-sdd/opsx-apply/reference.md +45 -2
- package/templates/skills/kld-sdd/opsx-archive/SKILL.md +1 -1
- package/templates/skills/kld-sdd/opsx-consistency-check/SKILL.md +592 -0
- package/templates/skills/kld-sdd/opsx-propose/SKILL.md +3 -3
- package/templates/skills/kld-sdd/opsx-propose/reference.md +6 -7
- package/templates/skills/kld-sdd/tdd-core/reference.md +3 -1
- package/templates/skills/kld-sdd/tdd-rules/SKILL.md +1 -0
- package/templates/skills/kld-sdd/tdd-rules/rules/tdd-rhythm-enforcement.md +101 -0
- package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { buildChangeReportModel } = require('./change-report-model.cjs');
|
|
4
|
+
const { buildChangeReportViewModel } = require('./change-report-view-model.cjs');
|
|
4
5
|
|
|
5
6
|
function escapeHtml(value) {
|
|
6
7
|
return String(value ?? '').replace(/[&<>"']/g, (char) => ({
|
|
@@ -12,28 +13,6 @@ function escapeHtml(value) {
|
|
|
12
13
|
}[char]));
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
function percent(value, digits = 2) {
|
|
16
|
-
return Number.isFinite(value) ? `${(value * 100).toFixed(digits)}%` : '暂无数据';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function duration(value) {
|
|
20
|
-
if (!Number.isFinite(value)) return '暂无数据';
|
|
21
|
-
const totalSeconds = Math.round(value / 1000);
|
|
22
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
23
|
-
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
24
|
-
const seconds = totalSeconds % 60;
|
|
25
|
-
return [hours ? `${hours}小时` : '', minutes ? `${minutes}分` : '', `${seconds}秒`].filter(Boolean).join('');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function fraction(value, total) {
|
|
29
|
-
return value == null || total == null ? '暂无数据' : `${value} / ${total}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function durationMetricHtml(value) {
|
|
33
|
-
if (!Number.isFinite(value)) return '<span class="muted-null">null</span>';
|
|
34
|
-
return `${Math.round(value / 60000)}<span class="unit">分钟</span>`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
16
|
function statusTone(status) {
|
|
38
17
|
if (['trusted', 'complete', 'passed', 'verified', 'implemented'].includes(status)) return 'good';
|
|
39
18
|
if (['attention', 'incomplete', 'provisional', 'needs_input', 'open'].includes(status)) return 'warn';
|
|
@@ -52,38 +31,33 @@ function metricCard({ name, code, value, state, explanation, source }) {
|
|
|
52
31
|
}
|
|
53
32
|
|
|
54
33
|
function renderChangeReportHtml(report) {
|
|
55
|
-
report
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
34
|
+
if (!report.change_summary || !report.artifacts?.files) {
|
|
35
|
+
report = buildChangeReportModel(report);
|
|
36
|
+
}
|
|
37
|
+
const view = buildChangeReportViewModel(report);
|
|
38
|
+
const overviewSection = view.sections.overview;
|
|
39
|
+
const metricsSection = view.sections['metrics-stages'];
|
|
40
|
+
const issueSection = view.sections['issues-evidence'];
|
|
41
|
+
const artifactSection = view.sections['artifacts-trace'];
|
|
61
42
|
const summary = report.change_summary || {};
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const evidenceCategories = Array.isArray(evidence.categories) ? evidence.categories : [];
|
|
76
|
-
const stageNodes = report.stage_timeline?._available && Array.isArray(report.stage_timeline.nodes)
|
|
77
|
-
? report.stage_timeline.nodes
|
|
78
|
-
: [];
|
|
79
|
-
const activeLinks = report.attribution_audit?.active_links || [];
|
|
80
|
-
const candidateIds = report.attribution_audit?.unassigned_candidate_event_ids || [];
|
|
81
|
-
const knownWarnings = report.known_risks?.warnings || [];
|
|
43
|
+
const evidence = {
|
|
44
|
+
...(summary.evidence || {}),
|
|
45
|
+
categories: issueSection.categories || [],
|
|
46
|
+
issues: issueSection.issues || [],
|
|
47
|
+
};
|
|
48
|
+
const changed = artifactSection.changed_files || {};
|
|
49
|
+
const artifacts = artifactSection.artifacts || [];
|
|
50
|
+
const conclusionTone = statusTone(view.conclusion?.status);
|
|
51
|
+
const evidenceCategories = evidence.categories;
|
|
52
|
+
const stageNodes = metricsSection.stages || [];
|
|
53
|
+
const activeLinks = artifactSection.attribution?.active_links || [];
|
|
54
|
+
const candidateIds = artifactSection.attribution?.unassigned_candidate_event_ids || [];
|
|
55
|
+
const knownWarnings = view.known_risks?.warnings || [];
|
|
82
56
|
const warningDetails = knownWarnings.flatMap((warning) => (
|
|
83
57
|
Array.isArray(warning.items) ? warning.items : []
|
|
84
58
|
));
|
|
85
|
-
const telemetryWarnings =
|
|
86
|
-
const artifactRows =
|
|
59
|
+
const telemetryWarnings = view.telemetry_warnings?.by_type || {};
|
|
60
|
+
const artifactRows = artifacts.map((item) => `
|
|
87
61
|
<tr>
|
|
88
62
|
<td>${escapeHtml({
|
|
89
63
|
markdown_report: 'Markdown 报告',
|
|
@@ -92,12 +66,27 @@ function renderChangeReportHtml(report) {
|
|
|
92
66
|
execution_log: '执行日志',
|
|
93
67
|
manifest: '归档清单',
|
|
94
68
|
archive_zip: '归档包',
|
|
95
|
-
}[name] || name)}</td>
|
|
69
|
+
}[item.name] || item.name)}</td>
|
|
96
70
|
<td><span class="state ${item?.exists ? 'good' : 'neutral'}">${item?.exists ? '已生成' : '未生成'}</span></td>
|
|
97
71
|
<td class="path">${escapeHtml(item?.path || '—')}</td>
|
|
98
|
-
<td>${item?.
|
|
72
|
+
<td>${item?.size_display
|
|
73
|
+
? escapeHtml(item.size_display)
|
|
74
|
+
: (item?.size_bytes == null ? '—' : escapeHtml(item.size_bytes))}</td>
|
|
99
75
|
</tr>`).join('');
|
|
100
|
-
const evidenceRows = evidenceCategories.map((group) =>
|
|
76
|
+
const evidenceRows = evidenceCategories.map((group) => {
|
|
77
|
+
const issueDetails = (group.issues || []).map((issue) => [
|
|
78
|
+
`问题:${issue.issue_id}`,
|
|
79
|
+
`对象:${issue.subject || '本次变更'}`,
|
|
80
|
+
`状态:${issue.status}`,
|
|
81
|
+
`判定:${issue.verdict}`,
|
|
82
|
+
`根因:${issue.root_cause}`,
|
|
83
|
+
`预期:${issue.expected}`,
|
|
84
|
+
`实际:${issue.actual}`,
|
|
85
|
+
`影响:${issue.impact}`,
|
|
86
|
+
`修复建议:${issue.remediation}`,
|
|
87
|
+
`证据事件:${(issue.evidence_event_ids || []).join('、') || '暂无可定位事件'}`,
|
|
88
|
+
].join('\n')).join('\n\n');
|
|
89
|
+
return `
|
|
101
90
|
<article class="issue-row">
|
|
102
91
|
<div><span class="issue-count">${escapeHtml(group.occurrence_count)}</span></div>
|
|
103
92
|
<div>
|
|
@@ -106,78 +95,33 @@ function renderChangeReportHtml(report) {
|
|
|
106
95
|
</div>
|
|
107
96
|
<button class="text-button drawer-trigger" type="button"
|
|
108
97
|
data-drawer-title="${escapeHtml(group.label)}"
|
|
109
|
-
data-drawer-body="${escapeHtml(`规则编码:${group.code}\n关联任务:${(group.affected_tasks || []).join('、') || '无'}\n事件:${(group.event_ids || []).join('、') || '无'}\n处置:${JSON.stringify(group.dispositions || {})}`)}">查看详情</button>
|
|
110
|
-
</article
|
|
98
|
+
data-drawer-body="${escapeHtml(`${issueDetails || `规则编码:${group.code}\n状态:open\n根因:当前证据未满足规则\n预期:规则证据完整\n实际:证据不足\n修复建议:补充真实证据`}\n\n规则编码:${group.code}\n关联任务:${(group.affected_tasks || []).join('、') || '无'}\n事件:${(group.event_ids || []).join('、') || '无'}\n处置:${JSON.stringify(group.dispositions || {})}`)}">查看详情</button>
|
|
99
|
+
</article>`;
|
|
100
|
+
}).join('');
|
|
111
101
|
const stageRows = stageNodes.map((node) => {
|
|
112
|
-
const stage = node.
|
|
113
|
-
const round = node.
|
|
114
|
-
const start = node.start_ts ? new Date(node.start_ts).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) : '—';
|
|
115
|
-
const end = node.end_ts ? new Date(node.end_ts).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) : '—';
|
|
102
|
+
const stage = node.display_stage;
|
|
103
|
+
const round = node.display_round;
|
|
116
104
|
return `
|
|
117
105
|
<tr class="tl-node">
|
|
118
106
|
<td>${escapeHtml(stage)}${round > 1 ? '²' : ''}${round > 1 ? '<span class="tl-repeat">R</span>' : ''}</td>
|
|
119
|
-
<td>${escapeHtml(
|
|
120
|
-
<td><span class="tl-time">${escapeHtml(
|
|
121
|
-
<td><span class="state ${statusTone(node.
|
|
107
|
+
<td>${escapeHtml(round)}</td>
|
|
108
|
+
<td><span class="tl-time">${escapeHtml(node.display_start)}→${escapeHtml(node.display_end)}</span><br>${escapeHtml(node.display_duration)}</td>
|
|
109
|
+
<td><span class="state ${statusTone(node.display_result === 'success' ? 'passed' : node.display_result)}">${escapeHtml(node.display_result)}</span></td>
|
|
122
110
|
</tr>`;
|
|
123
111
|
}).join('');
|
|
124
112
|
|
|
125
|
-
const metricCards = [
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
state: (
|
|
131
|
-
minSet.evidence_status?.e1 === 'trusted'
|
|
132
|
-
|| minSet.trusted_metrics?.includes?.('e1')
|
|
133
|
-
|| minSet.trusted?.includes?.('e1')
|
|
134
|
-
) ? '可信' : '需补证',
|
|
135
|
-
explanation: '七个标准阶段总耗时(含返工)÷ 验收场景数。',
|
|
136
|
-
source: `公式:标准阶段总耗时 ÷ 场景数。场景数:${efficiency.scenario_count ?? '未知'};当前值:${e1 ?? '不可计算'} ms。`,
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
name: '规约检查得分',
|
|
140
|
-
code: 'Q3',
|
|
141
|
-
value: quality.q3_spec_quality_score == null ? '暂无数据' : `${quality.q3_spec_quality_score} / 100`,
|
|
142
|
-
state: quality.q3_score_status === 'verified' ? '已验证' : '临时',
|
|
143
|
-
explanation: '最新规约检查结果;独立复核后才标记为已验证。',
|
|
144
|
-
source: `评审独立性:${quality.q3_reviewer_independence || '未知'};证据状态:${quality.q3_score_status || '未知'}。`,
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
name: '编码前已完成检查',
|
|
148
|
-
code: 'P4',
|
|
149
|
-
value: percent(processMetrics.p4_quality_gate_enforcement_rate),
|
|
150
|
-
state: Number.isFinite(processMetrics.p4_quality_gate_enforcement_rate) ? '可计算' : '需补证',
|
|
151
|
-
explanation: '编码前是否先检查:首次进入编码前,是否存在已配对且成功或部分通过的检查。',
|
|
152
|
-
source: '只接受能与 check 开始事件配对、且发生在首次 apply 之前的成功或部分通过结果。',
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
name: '阶段重复次数',
|
|
156
|
-
code: 'P-R',
|
|
157
|
-
value: processMetrics.rework_summary?.total_rework_attempts == null
|
|
158
|
-
? '暂无数据'
|
|
159
|
-
: `${processMetrics.rework_summary.total_rework_attempts} 次`,
|
|
160
|
-
state: processMetrics.rework_summary ? '可计算' : '需补证',
|
|
161
|
-
explanation: '七个标准阶段中,首次执行之后的重复尝试次数。',
|
|
162
|
-
source: '辅助 test / explore 阶段不计入标准阶段返工总数。',
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: '执行记录完整度',
|
|
166
|
-
code: 'P-H',
|
|
167
|
-
value: telemetry.p_h_telemetry_health_score == null
|
|
168
|
-
? '暂无数据'
|
|
169
|
-
: `${telemetry.p_h_telemetry_health_score} / 100`,
|
|
170
|
-
state: telemetry.p_h_telemetry_health_score == null ? '需补证' : '可计算',
|
|
171
|
-
explanation: '执行记录是否完整:标准阶段的身份、开始结束配对和数据质量综合评分。',
|
|
172
|
-
source: '沿用既有权重,但只以 propose、spec、design、task、check、apply、archive 七阶段为计算范围。',
|
|
173
|
-
},
|
|
174
|
-
].map(metricCard).join('');
|
|
113
|
+
const metricCards = (metricsSection.metric_cards || []).map(metricCard).join('');
|
|
114
|
+
const summaryCards = (overviewSection.cards || []).map((card) => `
|
|
115
|
+
<article class="summary-card"><span>${escapeHtml(card.label)}</span><strong>${escapeHtml(card.value)}</strong></article>`).join('');
|
|
116
|
+
const metricRows = (metricsSection.metric_rows || []).map((row) => `
|
|
117
|
+
<tr><td>${escapeHtml(row.label)}${row.code ? ` <span class="metric-code">${escapeHtml(row.code)}</span>` : ''}</td><td>${escapeHtml(row.value)}</td></tr>`).join('');
|
|
175
118
|
|
|
176
119
|
return `<!doctype html>
|
|
177
120
|
<html lang="zh-CN" data-theme="light">
|
|
178
121
|
<head>
|
|
179
122
|
<meta charset="utf-8">
|
|
180
123
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
124
|
+
<meta name="sdd-semantic-digest" content="${view.semantic_digest}">
|
|
181
125
|
<title>SDD 效果度量报告 · ${escapeHtml(report.change)}</title>
|
|
182
126
|
<style>
|
|
183
127
|
:root{--ink:#16213a;--muted:#68758c;--line:#dfe5ef;--paper:#fff;--canvas:#eef2f7;--blue:#175cd3;--blue-soft:#edf4ff;--good:#067647;--good-soft:#ecfdf3;--warn:#b54708;--warn-soft:#fffaeb;--bad:#b42318;--bad-soft:#fef3f2;--shadow:0 18px 54px rgba(31,43,66,.1)}
|
|
@@ -248,18 +192,13 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
248
192
|
<section class="panel" role="tabpanel" id="panel-overview" aria-labelledby="tab-overview">
|
|
249
193
|
<div class="hero">
|
|
250
194
|
<article class="card conclusion">
|
|
251
|
-
<span class="state ${conclusionTone}">${escapeHtml(
|
|
195
|
+
<span class="state ${conclusionTone}">${escapeHtml(view.conclusion?.status === 'trusted' ? '结论可信' : '需要关注')}</span>
|
|
252
196
|
<h2>一句话结论</h2>
|
|
253
|
-
<p>${escapeHtml(
|
|
197
|
+
<p>${escapeHtml(view.conclusion?.text || '当前证据不足,暂不能形成可信结论。')}</p>
|
|
254
198
|
</article>
|
|
255
|
-
<article class="card scope"><span>七个标准阶段总耗时</span><strong>${escapeHtml(
|
|
256
|
-
</div>
|
|
257
|
-
<div class="summary-grid">
|
|
258
|
-
<article class="summary-card"><span>最终测试</span><strong>${escapeHtml(fraction(tests.passed, tests.total))}</strong><p>${tests.status === 'passed' ? '最终权威运行全部通过' : '暂无可验证的最终运行'}</p></article>
|
|
259
|
-
<article class="summary-card"><span>文档任务完成</span><strong>${escapeHtml(fraction(tasks.document?.completed, tasks.document?.total))}</strong><p>来自 tasks 文档状态</p></article>
|
|
260
|
-
<article class="summary-card"><span>事件可追溯</span><strong>${escapeHtml(fraction(tasks.traceability?.verified, tasks.traceability?.total))}</strong><p>来自严格 task/test 事件对账</p></article>
|
|
261
|
-
<article class="summary-card"><span>证据问题</span><strong>${escapeHtml(evidence.category_count ?? 0)} 类</strong><p>${escapeHtml(evidence.occurrence_count ?? 0)} 次规则命中,不等于独立缺陷数</p></article>
|
|
199
|
+
<article class="card scope"><span>七个标准阶段总耗时</span><strong>${escapeHtml(overviewSection.scope_duration)}</strong><span>辅助阶段不进入效率分母</span></article>
|
|
262
200
|
</div>
|
|
201
|
+
<div class="summary-grid">${summaryCards}</div>
|
|
263
202
|
<div class="section">
|
|
264
203
|
<div class="section-head"><h2>核心指标</h2><p>中文业务名称为主,编码仅作定位标签</p></div>
|
|
265
204
|
<div class="metric-grid">${metricCards}</div>
|
|
@@ -276,12 +215,7 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
276
215
|
<div class="two-col">
|
|
277
216
|
<article class="wide-card">
|
|
278
217
|
<h3>效率拆分</h3>
|
|
279
|
-
<table><tbody>
|
|
280
|
-
<tr><td>七阶段总耗时</td><td><strong>${escapeHtml(duration(totalDuration))}</strong></td></tr>
|
|
281
|
-
<tr><td>每个场景平均用时 <span class="metric-code">E1</span></td><td>${escapeHtml(Number.isFinite(e1) ? `${duration(e1)} / 场景` : '暂无数据')}</td></tr>
|
|
282
|
-
<tr><td>编码时间占比 <span class="metric-code">E2</span></td><td>${escapeHtml(percent(e2))}</td></tr>
|
|
283
|
-
<tr><td>规划与规约时间占比 <span class="metric-code">E3</span></td><td>${escapeHtml(percent(e3))}</td></tr>
|
|
284
|
-
</tbody></table>
|
|
218
|
+
<table><tbody>${metricRows}</tbody></table>
|
|
285
219
|
</article>
|
|
286
220
|
<article class="wide-card">
|
|
287
221
|
<h3>时间线与阶段执行明细</h3>
|
|
@@ -289,35 +223,25 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
289
223
|
</article>
|
|
290
224
|
</div>
|
|
291
225
|
<p class="callout">标准阶段固定为:提案、规格、设计、任务拆解、检查、实现、归档。测试和探索属于辅助阶段,不计入效率分母。</p>
|
|
292
|
-
<details class="legacy-appendix">
|
|
293
|
-
<summary>历史兼容数据(默认折叠,仅供旧版消费者对照)</summary>
|
|
294
|
-
<div class="two-col wide-card section">
|
|
295
|
-
<div>
|
|
296
|
-
<h3>Legacy 效率指标</h3>
|
|
297
|
-
<p>一次成码率:${escapeHtml(percent(efficiency.e4_ai_code_first_pass_rate))}</p>
|
|
298
|
-
<h3>Legacy 质量指标</h3>
|
|
299
|
-
<p>规约符合度:${escapeHtml(quality.q1_spec_conformance_score ?? '暂无数据')}</p>
|
|
300
|
-
<h3>Legacy 过程指标</h3>
|
|
301
|
-
<p>阶段重复执行:${escapeHtml(processMetrics.rework_summary?.total_rework_attempts ?? '暂无数据')}</p>
|
|
302
|
-
<p>阶段间等待:${durationMetricHtml(efficiency.idle_time_ms)}</p>
|
|
303
|
-
<p>Q1 规约符合度:${escapeHtml(quality.q1_spec_conformance_score ?? '暂无数据')}${quality.q1_human_status === 'unverified' ? '(自评·待人工确认)' : ''}</p>
|
|
304
|
-
<p>E4 一次成码率:${escapeHtml(percent(efficiency.e4_ai_code_first_pass_rate))}(基于 task_update 信号判定,非代码 diff)${telemetryWarnings.task_update_reuse > 0 ? ';受 task_update 复用影响,已扣分' : ''}</p>
|
|
305
|
-
<p>过程记录事件:${escapeHtml(report.process_notes?.total ?? 0)} 条</p>
|
|
306
|
-
${changed._available ? '' : '<p><span class="muted-null">null</span>:旧版文件统计不可用</p>'}
|
|
307
|
-
</div>
|
|
308
|
-
<div>
|
|
309
|
-
<h3>重复执行原因</h3>
|
|
310
|
-
<p>${escapeHtml((processMetrics.rework_summary?.reasons?.details || []).map((item) => item.reason === 'pre-archive-recheck' ? '归档前强制复检' : item.reason).join('、') || '无')}</p>
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
</details>
|
|
314
226
|
</section>
|
|
315
227
|
|
|
316
228
|
<section class="panel" role="tabpanel" id="panel-evidence" aria-labelledby="tab-evidence" hidden>
|
|
317
229
|
<div class="section-head"><div><h2>问题与证据</h2><p>按问题类别聚合;展开后再查看任务和事件,不把规则命中误称为独立缺陷。</p></div></div>
|
|
318
230
|
<div class="issue-summary"><strong>${escapeHtml(evidence.category_count ?? 0)} 类问题</strong><p>${escapeHtml(evidence.occurrence_count ?? 0)} 次规则命中</p></div>
|
|
319
231
|
<article class="wide-card">${evidenceRows || '<p class="micro">当前没有证据问题。</p>'}</article>
|
|
320
|
-
|
|
232
|
+
<article class="wide-card section">
|
|
233
|
+
<h3>已知风险</h3>
|
|
234
|
+
<p>严重问题:${escapeHtml(view.known_risks?._available ? (view.known_risks.severe_issues?.length ?? 0) : '无数据')}</p>
|
|
235
|
+
${warningDetails.length > 0
|
|
236
|
+
? warningDetails.map((item) => `<p>${escapeHtml(item.description || '待确认项')}${item.target ? `(${escapeHtml(item.target)})` : ''}</p>`).join('')
|
|
237
|
+
: '<p class="micro">无需修复</p>'}
|
|
238
|
+
</article>
|
|
239
|
+
${Array.isArray(view.process_notes?.notes) && view.process_notes.notes.length > 0 ? `
|
|
240
|
+
<article class="wide-card section">
|
|
241
|
+
<h3>过程记录事件</h3>
|
|
242
|
+
<p>过程事件总数:${escapeHtml(view.process_notes.total ?? 0)}</p>
|
|
243
|
+
${view.process_notes.notes.slice(0, 40).map((note) => `<p>[${escapeHtml(note.stage || 'unknown')}] ${escapeHtml(note.kind || 'note')}${note.round ? ` R${escapeHtml(note.round)}` : ''}:${escapeHtml(note.summary || '')}</p>`).join('')}
|
|
244
|
+
</article>` : ''}
|
|
321
245
|
<p class="callout">${escapeHtml(evidence.note || '同一任务可能命中多条规则,命中次数不等于独立缺陷数。')}</p>
|
|
322
246
|
</section>
|
|
323
247
|
|
|
@@ -337,9 +261,9 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
337
261
|
<article class="wide-card">
|
|
338
262
|
<h3>归因审计</h3>
|
|
339
263
|
<table><tbody>
|
|
340
|
-
<tr><td>显式关联历史事件</td><td>${escapeHtml(
|
|
341
|
-
<tr><td>有效关联</td><td>${escapeHtml(
|
|
342
|
-
<tr><td>未归属候选</td><td>${escapeHtml(
|
|
264
|
+
<tr><td>显式关联历史事件</td><td>${escapeHtml(artifactSection.attribution_summary.linked_source_event_count)}</td></tr>
|
|
265
|
+
<tr><td>有效关联</td><td>${escapeHtml(artifactSection.attribution_summary.active_link_count)}</td></tr>
|
|
266
|
+
<tr><td>未归属候选</td><td>${escapeHtml(artifactSection.attribution_summary.unassigned_candidate_count)}</td></tr>
|
|
343
267
|
</tbody></table>
|
|
344
268
|
<button class="text-button drawer-trigger" type="button" data-drawer-title="归因审计详情" data-drawer-body="${escapeHtml(`有效关联:${JSON.stringify(activeLinks)}\n未归属候选:${candidateIds.join('、') || '无'}`)}">查看归因事件</button>
|
|
345
269
|
</article>
|
|
@@ -348,7 +272,7 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
348
272
|
<h3>产物清单 · 实际产物 · 归档结果</h3>
|
|
349
273
|
<table><thead><tr><th>产物</th><th>状态</th><th>路径</th><th>大小</th></tr></thead><tbody>${artifactRows}</tbody></table>
|
|
350
274
|
</article>
|
|
351
|
-
<article class="wide-card section"><h3
|
|
275
|
+
<article class="wide-card section"><h3>产物备注</h3><p class="micro">归档目录:${escapeHtml(report.archive_result?.archive_path || report.artifacts?.archive_path || '暂不可用')}</p><p class="micro">已知风险明细仅在“问题与证据”页展示,本页不重复列表。</p></article>
|
|
352
276
|
</section>
|
|
353
277
|
</main>
|
|
354
278
|
|