kld-sdd 2.4.8 → 2.4.9
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/package.json
CHANGED
package/skywalk-sdd/index.js
CHANGED
|
@@ -171,7 +171,44 @@ function findActiveStage(projectRoot, criteria) {
|
|
|
171
171
|
})
|
|
172
172
|
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
173
173
|
|
|
174
|
-
return candidates[0]
|
|
174
|
+
if (candidates.length > 0) return candidates[0];
|
|
175
|
+
|
|
176
|
+
// 跨 session 匹配:当 session_id 变化(如 compaction 恢复)但 command+change 相同时,
|
|
177
|
+
// 在 events 目录中搜索最近的 stage_start 事件
|
|
178
|
+
if (criteria.command && criteria.change) {
|
|
179
|
+
const dataDir = getDataDir(projectRoot);
|
|
180
|
+
const eventsDir = path.join(dataDir, 'events');
|
|
181
|
+
if (fs.existsSync(eventsDir)) {
|
|
182
|
+
const changeDirs = fs.readdirSync(eventsDir).filter(d => {
|
|
183
|
+
return fs.statSync(path.join(eventsDir, d)).isDirectory();
|
|
184
|
+
});
|
|
185
|
+
const matchingStarts = [];
|
|
186
|
+
for (const changeDir of changeDirs) {
|
|
187
|
+
const dir = path.join(eventsDir, changeDir);
|
|
188
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort().reverse();
|
|
189
|
+
for (const file of files) {
|
|
190
|
+
const lines = fs.readFileSync(path.join(dir, file), 'utf-8').split('\n').filter(Boolean);
|
|
191
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
192
|
+
try {
|
|
193
|
+
const event = JSON.parse(lines[i]);
|
|
194
|
+
if (event.type === 'stage_start' &&
|
|
195
|
+
event.command === criteria.command &&
|
|
196
|
+
event.change === criteria.change &&
|
|
197
|
+
(!criteria.capability || event.capability === criteria.capability)) {
|
|
198
|
+
matchingStarts.push(event);
|
|
199
|
+
}
|
|
200
|
+
} catch {}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (matchingStarts.length > 0) {
|
|
205
|
+
matchingStarts.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
206
|
+
return matchingStarts[0];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
175
212
|
}
|
|
176
213
|
|
|
177
214
|
function clearActiveStage(projectRoot, event) {
|
|
@@ -1805,6 +1842,16 @@ function renderPdfMvpMarkdown(metrics) {
|
|
|
1805
1842
|
].join('\n');
|
|
1806
1843
|
}
|
|
1807
1844
|
|
|
1845
|
+
function formatMsToSeconds(ms) {
|
|
1846
|
+
if (ms == null) return 'null';
|
|
1847
|
+
return `${(ms / 1000).toFixed(2)} s`;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
function formatRatioToPercent(ratio) {
|
|
1851
|
+
if (ratio == null) return 'null';
|
|
1852
|
+
return `${(ratio * 100).toFixed(1)}%`;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1808
1855
|
function renderExecutiveReportMarkdown(report) {
|
|
1809
1856
|
const metrics = report.metrics;
|
|
1810
1857
|
const health = report.doctor;
|
|
@@ -1818,28 +1865,28 @@ function renderExecutiveReportMarkdown(report) {
|
|
|
1818
1865
|
`- 统计范围:${report.change ? `change/${report.change}` : 'project'}`,
|
|
1819
1866
|
'',
|
|
1820
1867
|
'## 执行摘要',
|
|
1821
|
-
`- Telemetry 健康分:${health.telemetry_health_score
|
|
1822
|
-
`- 阶段闭环率:${health.matched_stage_rate
|
|
1823
|
-
`- 严重问题数:${health.severe_issues?.length || 0}
|
|
1868
|
+
`- Telemetry 健康分:${formatRatioToPercent(health.telemetry_health_score)}(0-100%,反映遥测数据的完整性,扣分项包括未闭环阶段、孤儿事件等)`,
|
|
1869
|
+
`- 阶段闭环率:${formatRatioToPercent(health.matched_stage_rate)}(已配对 start/end 的阶段占有效阶段总数的比例)`,
|
|
1870
|
+
`- 严重问题数:${health.severe_issues?.length || 0}(存在未闭环阶段、孤儿事件或未知命令等阻断级问题的数量)`,
|
|
1824
1871
|
'',
|
|
1825
1872
|
'## 效率指标',
|
|
1826
|
-
`- E1 需求到归档总时长:${metrics.efficiency?.e1_lead_time_ms
|
|
1827
|
-
`- E2 编码时间占比:${metrics.efficiency?.e2_coding_time_ratio
|
|
1828
|
-
`- E3 规约时间占比:${metrics.efficiency?.e3_spec_time_ratio
|
|
1829
|
-
`- E4 AI 一次成码率:${metrics.efficiency?.e4_ai_code_first_pass_rate
|
|
1830
|
-
`- 有效阶段总耗时:${metrics.efficiency?.effective_stage_duration_ms
|
|
1831
|
-
`- 返工阶段总耗时:${metrics.efficiency?.rework_stage_duration_ms
|
|
1873
|
+
`- E1 需求到归档总时长:${formatMsToSeconds(metrics.efficiency?.e1_lead_time_ms)}`,
|
|
1874
|
+
`- E2 编码时间占比:${formatRatioToPercent(metrics.efficiency?.e2_coding_time_ratio)}`,
|
|
1875
|
+
`- E3 规约时间占比:${formatRatioToPercent(metrics.efficiency?.e3_spec_time_ratio)}`,
|
|
1876
|
+
`- E4 AI 一次成码率:${formatRatioToPercent(metrics.efficiency?.e4_ai_code_first_pass_rate)}`,
|
|
1877
|
+
`- 有效阶段总耗时:${formatMsToSeconds(metrics.efficiency?.effective_stage_duration_ms)}`,
|
|
1878
|
+
`- 返工阶段总耗时:${formatMsToSeconds(metrics.efficiency?.rework_stage_duration_ms)}`,
|
|
1832
1879
|
'',
|
|
1833
1880
|
'## 质量指标',
|
|
1834
1881
|
`- Q1 规约符合度:${metrics.quality?.q1_spec_conformance_score ?? 'null'}`,
|
|
1835
|
-
`- Q3 构建一次通过率:${metrics.quality?.q3_build_first_pass_rate
|
|
1836
|
-
`- Q4 规约驱动测试覆盖率:${metrics.quality?.q4_spec_driven_test_coverage
|
|
1882
|
+
`- Q3 构建一次通过率:${formatRatioToPercent(metrics.quality?.q3_build_first_pass_rate)}`,
|
|
1883
|
+
`- Q4 规约驱动测试覆盖率:${formatRatioToPercent(metrics.quality?.q4_spec_driven_test_coverage)}`,
|
|
1837
1884
|
`- Q5 跨文档一致性得分:${metrics.quality?.q5_cross_doc_consistency_score ?? 'null'}`,
|
|
1838
1885
|
'',
|
|
1839
1886
|
'## 过程指标',
|
|
1840
1887
|
`- P1 文档迭代次数:${metrics.process?.p1_spec_iteration_count ?? 'null'}`,
|
|
1841
|
-
`- P2 AI 代码保留率:${metrics.process?.p2_ai_code_adoption_rate
|
|
1842
|
-
`- P4 质量门前置率:${metrics.process?.p4_quality_gate_enforcement_rate
|
|
1888
|
+
`- P2 AI 代码保留率:${formatRatioToPercent(metrics.process?.p2_ai_code_adoption_rate)}`,
|
|
1889
|
+
`- P4 质量门前置率:${formatRatioToPercent(metrics.process?.p4_quality_gate_enforcement_rate)}`,
|
|
1843
1890
|
`- 返工次数:${metrics.process?.rework_summary?.total_rework_attempts ?? 'null'}`,
|
|
1844
1891
|
`- 被后续成功执行覆盖的未闭环阶段数:${metrics.process?.rework_summary?.superseded_open_stages ?? 'null'}`,
|
|
1845
1892
|
'',
|
|
@@ -1853,22 +1900,9 @@ function renderExecutiveReportMarkdown(report) {
|
|
|
1853
1900
|
`- 未勾选任务项:${taskCompletion?.incomplete ?? 'null'}`,
|
|
1854
1901
|
`- 任务项总数:${taskCompletion?.total ?? 'null'}`,
|
|
1855
1902
|
'',
|
|
1856
|
-
'## 人工反馈',
|
|
1857
|
-
`- 平均 NPS:${metrics.manual_insights?.avg_nps ?? 'null'}`,
|
|
1858
|
-
`- 平均认知负荷:${metrics.manual_insights?.avg_cognitive_load ?? 'null'}`,
|
|
1859
|
-
`- 平均规约疲劳指数:${metrics.manual_insights?.avg_spec_fatigue_index ?? 'null'}`,
|
|
1860
|
-
`- 相对传统方式节省时长比例:${metrics.manual_insights?.baseline_time_saved_ratio ?? 'null'}`,
|
|
1861
|
-
'',
|
|
1862
|
-
'## 数据质量',
|
|
1863
|
-
`- 未闭环阶段数:${health.open_stages ?? 'null'}`,
|
|
1864
|
-
`- 已被后续成功执行覆盖的未闭环阶段数:${health.superseded_open_stages ?? 'null'}`,
|
|
1865
|
-
`- 返工次数:${health.rework_attempts ?? 'null'}`,
|
|
1866
|
-
`- 孤儿结束事件数:${health.orphan_events ?? 'null'}`,
|
|
1867
|
-
`- 未知命令事件数:${health.unknown_command_events ?? 'null'}`,
|
|
1868
|
-
'',
|
|
1869
1903
|
'## 说明',
|
|
1870
1904
|
'- `null` 表示当前还没有采集到对应事件或该指标暂不适用。',
|
|
1871
|
-
'- Q1
|
|
1905
|
+
'- Q1 规约符合度与人工反馈类指标属于评审信号,默认不作为强阻断门禁。',
|
|
1872
1906
|
].join('\n');
|
|
1873
1907
|
}
|
|
1874
1908
|
|
|
@@ -2156,7 +2190,7 @@ function cmdEnd(args, options = {}) {
|
|
|
2156
2190
|
task_id: args['task-id'] || args.task_id || startEvent?.task_id,
|
|
2157
2191
|
agent_type: args.agent || args['agent-type'] || startEvent?.agent_type || 'unknown',
|
|
2158
2192
|
project_root: projectRoot,
|
|
2159
|
-
session_id: args['session-id'] || args.session_id
|
|
2193
|
+
session_id: startEvent?.session_id || args['session-id'] || args.session_id,
|
|
2160
2194
|
git_sha: args['git-sha'] || args.git_sha || startEvent?.git_sha,
|
|
2161
2195
|
timestamp,
|
|
2162
2196
|
duration_ms: durationMs,
|
|
@@ -2244,6 +2278,12 @@ function cmdRecord(args) {
|
|
|
2244
2278
|
}
|
|
2245
2279
|
|
|
2246
2280
|
const dataDir = getDataDir(projectRoot);
|
|
2281
|
+
const activeStage = findActiveStage(projectRoot, {
|
|
2282
|
+
session_id: args['session-id'] || args.session_id,
|
|
2283
|
+
change,
|
|
2284
|
+
command: args.command,
|
|
2285
|
+
agent_type: agentType,
|
|
2286
|
+
});
|
|
2247
2287
|
const eventId = args['event-id'] || args.event_id || generateEventId();
|
|
2248
2288
|
const timestamp = nowISO();
|
|
2249
2289
|
const event = cleanOptionalFields({
|
|
@@ -2258,7 +2298,7 @@ function cmdRecord(args) {
|
|
|
2258
2298
|
task_id: args['task-id'] || args.task_id,
|
|
2259
2299
|
agent_type: agentType,
|
|
2260
2300
|
project_root: projectRoot,
|
|
2261
|
-
session_id: args['session-id'] || args.session_id,
|
|
2301
|
+
session_id: activeStage?.session_id || args['session-id'] || args.session_id,
|
|
2262
2302
|
git_sha: args['git-sha'] || args.git_sha,
|
|
2263
2303
|
timestamp,
|
|
2264
2304
|
result: args.result,
|
|
@@ -20,9 +20,9 @@ function main() {
|
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const args = [logCli, 'doctor',
|
|
23
|
+
const args = [logCli, 'doctor', `--project=${projectRoot}`];
|
|
24
24
|
if (changeName) {
|
|
25
|
-
args.push(
|
|
25
|
+
args.push(`--change=${changeName}`);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const result = spawnSync(process.execPath, args, {
|
|
@@ -390,8 +390,10 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
|
|
|
390
390
|
每完成一个任务后,必须立即:
|
|
391
391
|
|
|
392
392
|
1. **修改 tasks.md 文件**:
|
|
393
|
-
-
|
|
394
|
-
|
|
393
|
+
- 将该任务对应的 `- [ ]` 替换为 `- [x]`(包括任务执行拓扑图中的复选框和任务详情中的状态行)
|
|
394
|
+
- 将 `- **状态**: [ ] 未完成` 替换为 `- **状态**: [x] 已完成`
|
|
395
|
+
- **两种格式必须同步更新**,不可遗漏
|
|
396
|
+
2. **验证修改成功**:读取文件确认两种格式的复选框均已勾选
|
|
395
397
|
3. **显示进度提示**:
|
|
396
398
|
> "✅ **[TASK-XXX-01] 已完成** [N/M]
|
|
397
399
|
> - 编译检查: ✅ 通过
|
|
@@ -462,7 +464,7 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
|
|
|
462
464
|
- **⛔ 渐进式加载**:只加载当前 Capability 的四文档链(overview + proposal + spec + design + tasks)
|
|
463
465
|
- **⛔ 隔离红线**:绝对禁止加载同级其他 Capability 的文档
|
|
464
466
|
- **⛔ 拓扑依赖拦截**:执行任务前必须检查依赖,前置未完成必须拦截
|
|
465
|
-
- **⛔ 必须实时更新任务状态**:每完成一个任务,**立即**修改 tasks.md
|
|
467
|
+
- **⛔ 必须实时更新任务状态**:每完成一个任务,**立即**修改 tasks.md 中的 `- [ ]` 为 `- [x]`,**同时修改** `**状态**: [ ] 未完成` 为 `**状态**: [x] 已完成`,两种格式不可遗漏,禁止批量更新
|
|
466
468
|
- **⛔ 算法一致性门禁**:对于 IMPL 任务,检查实现代码与 design.md 伪代码的一致性,核心逻辑不一致时拦截
|
|
467
469
|
- **⛔ 编译检查门禁**:每完成一个任务后,**必须运行编译检查**,编译失败禁止标记已完成
|
|
468
470
|
- **⛔ 测试执行门禁**:根据 `test-strategy` 决定测试门禁行为(tdd=强制, impl-first=警告, none=跳过)
|
|
@@ -291,7 +291,7 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
|
|
|
291
291
|
### [TASK-XXX-01] 任务名称
|
|
292
292
|
- **类型**: 数据层 / 接口层 / UI层 / 测试
|
|
293
293
|
- **依赖**: 无
|
|
294
|
-
- **状态**: [ ]
|
|
294
|
+
- **状态**: [ ] 未完成(勾选格式统一使用 GitHub 标准的 `- [ ]` / `- [x]`)
|
|
295
295
|
|
|
296
296
|
#### 任务描述
|
|
297
297
|
<!-- 一句话描述本任务要做什么 -->
|
|
@@ -180,7 +180,9 @@ e. **⛔ 测试执行门禁**
|
|
|
180
180
|
| `none` | **跳过**:不执行测试门禁 |
|
|
181
181
|
|
|
182
182
|
f. **⛔ 立即更新任务状态**
|
|
183
|
-
- 修改 tasks.md
|
|
183
|
+
- 修改 tasks.md:将 `- [ ]` 替换为 `- [x]`(包括拓扑图和任务详情中的复选框)
|
|
184
|
+
- 同时修改:`- **状态**: [ ] 未完成` → `- **状态**: [x] 已完成`
|
|
185
|
+
- **两种格式必须同步更新**,不可遗漏
|
|
184
186
|
- 验证修改成功
|
|
185
187
|
- 显示进度:`✅ [TASK-ID] 已完成 [N/M]`
|
|
186
188
|
- 记录任务级 Telemetry:
|
|
@@ -216,7 +218,7 @@ node skywalk-sdd/log.js record --type=ai_adoption_review --command=apply --proje
|
|
|
216
218
|
- **⛔ DAG 依赖拦截**:执行任务前必须检查依赖,前置未完成必须拦截
|
|
217
219
|
- **⛔ 编译检查门禁**:每完成一个任务后,**必须运行编译检查**,编译失败禁止标记已完成
|
|
218
220
|
- **⛔ 测试执行门禁**:根据 `test-strategy` 决定测试门禁行为(tdd=强制, impl-first=警告, none=跳过)
|
|
219
|
-
- **⛔ 必须实时更新任务状态**:每完成一个任务,**立即**修改 tasks.md
|
|
221
|
+
- **⛔ 必须实时更新任务状态**:每完成一个任务,**立即**修改 tasks.md 中的 `- [ ]` 为 `- [x]`,**同时修改** `**状态**: [ ] 未完成` 为 `**状态**: [x] 已完成`,两种格式不可遗漏
|
|
220
222
|
- **Git 只读策略**:禁止为了度量自动初始化 Git、创建分支或提交 commit;非 Git 项目使用 `vcs_mode=no-git` 继续执行
|
|
221
223
|
- **显示进度反馈**:每完成一个任务,显示「✅ [TASK-ID] 已完成 [N/M]」
|
|
222
224
|
- **保持任务聚焦**:每次只处理一个任务
|