dsh-workplan 0.0.0-stage → 1.4.3
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/CHANGELOG.md +282 -0
- package/LICENSE +21 -0
- package/README.en.md +147 -0
- package/README.md +161 -2
- package/client.js +1893 -0
- package/cordis.patch.yml +4 -0
- package/icon.svg +7 -0
- package/lib/host.js +301 -0
- package/lib/plan.js +1495 -0
- package/lib/runtime.js +1755 -0
- package/lib/watchdog.js +184 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +74 -4
- package/src/client.ts +2196 -0
- package/src/globals.d.ts +14 -0
- package/src/host.ts +333 -0
- package/src/plan.ts +1591 -0
- package/src/runtime.ts +1749 -0
- package/src/watchdog.ts +207 -0
- package/tsconfig.json +36 -0
- package/vendor/chart.umd.js +14 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
## 1.4.3 — 2026-10-28
|
|
2
|
+
|
|
3
|
+
**「同一个数字只能有一个来源」的根治版**。1.4.2 及更早版本里,同一个计划的数字存在两套真相源:
|
|
4
|
+
实时状态列表 `record.statuses` 与缓存在记录上的 `record.stats`。缓存只在**每批子任务全部跑完之后**
|
|
5
|
+
刷新一次,而看板头部读缓存、阶段徽标与卡片明细读列表,于是同一张卡片上会同时出现
|
|
6
|
+
「0 正在做」和「正在做」;预算用尽被重置成 pending 的子任务还带着上一轮的 `progress: 100`,
|
|
7
|
+
于是「0/3 做好」的阶段旁边挂着「正在做」、计划进度条显示 100% 而完成数是 0。
|
|
8
|
+
|
|
9
|
+
本版把计数改成**纯派生**:`derivePlanSnapshot(statuses)` 一次遍历同时产出 `statuses / stats / departments`,
|
|
10
|
+
再也没有第二份可被写坏的数字。
|
|
11
|
+
|
|
12
|
+
### 缺陷(4 条,全部有可复现证据)
|
|
13
|
+
|
|
14
|
+
用户报告(会话 `session-91ec3273-…`,19:02 截图):顶部汇总「0 正在做」,而计划卡里第 3 步挂着「正在做」。
|
|
15
|
+
以同一份会话数据重放,确认差异为**真实缺陷**而非观感问题:
|
|
16
|
+
|
|
17
|
+
- **D1 计数缓存冻结**(根因):`writeSubtaskStatus()` 只写 `record.statuses`,不写 `record.stats`;
|
|
18
|
+
`record.stats` 只在 `while(true)` 批次循环里、**整批子任务跑完之后**才重算。批次运行期间缓存一直是旧值,
|
|
19
|
+
`serialisePlan()` 又优先读缓存(`record.stats ?? computeStats(statuses)`,而 `stats` 恒已初始化,
|
|
20
|
+
回退分支永不触发)。实测:真实状态 `done 2 / 正在做 1 / 待做 0`,缓存仍报 `done 1 / 正在做 0 / 待做 2`,
|
|
21
|
+
进度条 33% 而真实进度 70%。
|
|
22
|
+
- **D2 同一报文自相矛盾**:`serialisePlan()` 里 `stats` 取自缓存、`departments` 却由 `statuses` 现场聚合,
|
|
23
|
+
一个 GET 响应内 `stats.inProgress = 0` 与 `design.inProgress = 1` 并存。
|
|
24
|
+
- **D3 重置绕过状态机**:预算用尽时用裸对象展开重置为 pending(`progress: item.progress ?? 0`),
|
|
25
|
+
绕过 `applyStatus()` 的伴随字段清理,留下 `pending + progress: 100 + childId + startedAt`。
|
|
26
|
+
连带后果:旧公式把每条 `progress` 直接平均(不计状态),12 项里 11 项完成也算出 **100%**。
|
|
27
|
+
- **D4 同一失败两种语义**:异常收敛分支把 blocked 写成 `progress: item.progress ?? 0`,
|
|
28
|
+
而其余 4 处 blocked 写入点都写 `progress: 100`。
|
|
29
|
+
|
|
30
|
+
### 修复
|
|
31
|
+
|
|
32
|
+
- **单一派生源**:`src/plan.ts` 新增 `StatusEntry / Stats / DepartmentSummary / PlanSnapshot` 与
|
|
33
|
+
`normaliseStatuses`、`deriveAggregates`、`derivePlanSnapshot`、`aggregateInvariantViolations`。
|
|
34
|
+
规则写死在一个地方:未知状态降级为 pending、pending 强制 `progress=0` 且清空 `childId/startedAt/finishedAt`、
|
|
35
|
+
done 强制 100、进度加权 done=100、blocked/in_progress=自身进度的 clamp、pending=0。
|
|
36
|
+
- **删除 `record.stats`**:`src/runtime.ts` 移除全部写入与读取点(16 处),`serialisePlan` /
|
|
37
|
+
`buildRoundResult` / `dispatchTool` / `updateStatus` / `cancelPlan` 全部改为「先派生一次,再用同一个快照填字段」。
|
|
38
|
+
`grep 'record.stats' src/runtime.ts` 现在只剩一条注释。
|
|
39
|
+
- **状态机收口**:预算用尽重置改为逐项 `applyStatus(..., { status: 'pending', error: '本轮预算用尽,等待续跑' })`;
|
|
40
|
+
`applyStatus` 的 pending 分支同时清空 `progress/startedAt/finishedAt/childId`(保留显式传入的 error)。
|
|
41
|
+
- **blocked 统一为 100**:异常收敛分支与其余写入点对齐,同一失败不再有两种进度。
|
|
42
|
+
- **构建卫生**:`build` 先 `rm -rf dist`、`test` 先 `rm -rf dist-tests`,并新增 `clean` 脚本。
|
|
43
|
+
旧编译物残留曾让 `npm test` 报 `ERR_MODULE_NOT_FOUND ... dist-tests/src/state.js`(本次已实测复现并修掉)。
|
|
44
|
+
- **移出失控产物**:子 Agent 遗留、未被任何生产代码引用的 `src/state.ts` 与 `tests/state-machine.test.ts`
|
|
45
|
+
移到 `docs/design-proposals/` 作参考(它与 `applyStatus` 重复实现同一语义,接入即再造双真相源);
|
|
46
|
+
`docs/design-derived-counts.reference.ts` 在设计落地后删除,`scripts/prove-derived-counts.ts`
|
|
47
|
+
改为直接导入 `src/plan.ts`,杜绝「设计与实现各算一套」。
|
|
48
|
+
|
|
49
|
+
### 验证
|
|
50
|
+
|
|
51
|
+
- `npm run check`:exit 0。
|
|
52
|
+
- `npm test`:**90 / 90 通过**(79 项原有 + 11 项新增 `tests/derived-counts.test.ts`)。
|
|
53
|
+
新增用例含**变异测试**:手工把 `stats.done +1`、`total +1`、`pending -1`、`progress=101`、部门 `done/total`
|
|
54
|
+
改坏,不变量检查器必须报错——证明断言有效,而不是复述当前行为。
|
|
55
|
+
- `node --experimental-strip-types scripts/repro-inconsistent-counts.ts --expect-fixed`:**全部不变量 PASS**
|
|
56
|
+
(场景 A 顶部 2 完成 / 1 正在做、卡片 70% / 正在做 1 / 待做 0、甜甜圈 67;场景 B 暂停项 `progress=0`、
|
|
57
|
+
`childId=null`、`stats.progress=0` 与甜甜圈一致;场景 C resume 后 blocked=0)。
|
|
58
|
+
同一脚本的默认模式(断言旧缺陷存在)现在 12 条全部失败——符合预期,缺陷已被消除。
|
|
59
|
+
- `node --experimental-strip-types scripts/prove-derived-counts.ts`:13 条断言全通过,含 500 组随机状态序列的
|
|
60
|
+
不变量与幂等检查。
|
|
61
|
+
|
|
62
|
+
### 兼容性
|
|
63
|
+
|
|
64
|
+
- 对外载荷字段不变:`statuses`、`stats`(`total/done/blocked/inProgress/pending/progress`)、
|
|
65
|
+
`departments` 同名同义;工具 JSON Schema 未改。
|
|
66
|
+
- `stats` 数值语义修正后可能与 1.4.2 的历史数字不同(这正是缺陷修复本身),旧结果消息仍可回放读取。
|
|
67
|
+
- `src/plan.ts` 的 `computeStats()` 保留为 `derivePlanSnapshot()` 的**别名**(标注 `@deprecated`),
|
|
68
|
+
外部调用者不需要改动即可获得正确结果。
|
|
69
|
+
|
|
70
|
+
### 回滚
|
|
71
|
+
|
|
72
|
+
**1.4.3 是本项目首次公开发布**,更早的 1.0.0–1.4.2 只在本地开发,没有发布到 npm,也没有对应的 git 标签,
|
|
73
|
+
因此公开仓库里没有 1.4.2 的可回滚版本。如果你是从本地开发副本升级上来的,回滚到升级前那份副本即可:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm ci && npm test # 回到升级前的行为,先确认它仍然可构建、测试通过
|
|
77
|
+
dsh plugin --profile web add /path/to/the/older/copy
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
已安装的运行实例需要重启 DSH(或重新加载 `web` profile)才会使用本版代码。
|
|
81
|
+
|
|
82
|
+
## 1.4.2 — 2026-10-27
|
|
83
|
+
|
|
84
|
+
运行态验收(用户重启后的真实页面复核,7 个子任务)驱动的一轮修正,全部为**可复现证据**下的收口。
|
|
85
|
+
|
|
86
|
+
- **图表文字在浅色主题下几乎不可见(1.14:1)**:取色用了 `getComputedStyle(document.documentElement)`,而 `--dsw-alias-*` 主题变量由主题包定义在 `<body>` 上,`<html>` 读不到自定义属性时返回空串,于是恒回退 `#e2e8f0` —— 白底上仅 **1.14:1**(WCAG 要求 ≥4.5:1)。改读 `document.body` 后实测 **17.43:1(浅)/ 15.01:1(深)**。这是本轮唯一的功能级缺陷。
|
|
87
|
+
- **两处「完成率」口径打架**:图表按**计划数**算(1/7 → 14%),组头摘要按**工作项**算(1/2 → 50%),同屏两个百分比互相打脸。修正:图表标题写明「两类计划数量占比」「进行中计划完成度(按工作项)」,口径写进标题,不再靠读者猜。
|
|
88
|
+
- **「进行中」一词自相矛盾**:组头摘要原写「进行中 N」,而 N 实际是「未完成件数」(含还没开始的),与同一按钮 aria-label 里的「正在做 0 件、还没开始 3 件」冲突。改为 **「未完成 N / 已完成 M(完成率 x%)」**,与 aria 三态明细口径一致。
|
|
89
|
+
- **主题切换后图表文字不变色**:`ink` 只在建图那一刻读取一次,切到深色主题后图表文字仍是浅色主题的颜色。改用 `MutationObserver` 监听 `body` 的 `data-ds-dark-theme` 属性重建图表(零依赖),切换主题即刷新文字色。
|
|
90
|
+
- **自动展开(设计稿 §7.3 补齐)**:①最后一个进行中任务完成时,已完成组自动展开一次;②用户正展开的卡片被移入已完成组时,该组自动展开。依赖均为布尔值,4 秒轮询不会反复弹开。
|
|
91
|
+
|
|
92
|
+
- **小字对比度未达标(2.77 / 2.80:1)**:18px 统计数字与 12px 状态胶囊直接用了品牌色,白底上只有 2.80:1(橙)/ 2.77:1(蓝),低于 WCAG 4.5:1。改为主题自适应混合色 `color-mix(in srgb, 品牌色 70%, 主文本色)`:浅色实测 **#b35616 4.93:1 / #0e79a9 4.85:1**,深色 **#f99c5b 7.82:1 / #55beee 7.86:1**,色相保留。
|
|
93
|
+
- **环形图弧线深色主题失效**:`--dsh-wp-done-ink` 硬编码 `#0284c7`,对深色底约 2.4:1;改挂同一个自适应色。
|
|
94
|
+
- **焦点环 2.66:1**:组头按钮 `:focus-visible` 用 `--dsh-wp-done`;改为主文本色(浅 18.12 / 深 16.30)。
|
|
95
|
+
- **首屏看不到任务卡(已按用户选择 B 修完)**:`GroupCharts` 两张卡各约 246px、总览块(完成率环 + 每计划进度条)也排在两组之前,首屏被三块内容占满。现顺序为:5 格数字统计条 → 一行口径说明 → 「进行中」组 → 「已完成」组 → 总览块 → 两张图表。打开面板第一屏即是进行中任务卡,所有图表下移到两组之后。
|
|
96
|
+
- **旧浏览器兜底**:`color-mix` 在 Safari <16.2 / Chrome <111 不可用,原写法会让 `--dsh-wp-done-text` 成为无效值。改为先声明主文本色兜底、再用 `@supports (color: color-mix(...))` 覆盖,旧浏览器只丢色相、不丢可读性。
|
|
97
|
+
- **宿主元数据版本滞后**:`src/host.ts` 与 `lib/host.js` 仍写 `1.4.1`,与 `package.json` 的 1.4.2 不一致;已同步为 1.4.2。
|
|
98
|
+
- **条形图静默吞掉第 7 个及以后的计划**:`groups.live.slice(0, 6)` 截断后没有任何提示,读者会以为图里就是全部。改为在图表下方注明「另有 N 个进行中计划未画入图表」(中英双语,零新增依赖)。
|
|
99
|
+
**验证**:`npm test` **79/79 通过**;`npm run build` 无 TS 报错。
|
|
100
|
+
|
|
101
|
+
## 1.4.1 — 2026-10-27
|
|
102
|
+
|
|
103
|
+
「一目了然 + 精美绝伦」的收口版本。1.4.0 存在的问题全部由本轮的**实测证据**驱动修正。
|
|
104
|
+
|
|
105
|
+
- **致命缺陷修复:图表永远不出现**。1.4.0 用 `chartState === 'ready'` 门控 `<canvas>`,而 `chartState` 只有建图成功后才置 `ready` —— 无 canvas 就不建图、不建图就不 ready,形成死循环。只读渲染探针实测:`canvasNodes: 0 / fallbackBars: 1 / liveBarTitle: 0`。改为 **canvas 与 fallback 并存**(ready 前 fallback 可见、ready 后 CSS 互斥隐藏),修复后同一探针:`canvasNodes: 2 / liveBarTitle: 1`。
|
|
106
|
+
- **对比度达标(WCAG AA)**:组头计数徽章原为 12px 白字压在橙/蓝硬编码渐变上,实测 **2.26–2.80:1(进行中)/ 2.14–4.10:1(已完成)**,全部不合格;组头副标题 tertiary 实测 3.56:1 也不合格。改为「主文本色 + 半透明品牌底 + 1px 内描边」,品牌色只做底与描边。组头标题实测 **18.15:1(浅)/ 16.28:1(深)**,本就合格,保持主文本色。
|
|
107
|
+
- **组头完成度摘要**:新增 `groupSummary` / `groupSummaryNoRate` 与 `groupAria` 文案,组头以纯文本给出「进行中 N / 已完成 M(完成率 x%)」,分母为 0 时输出「完成率暂无数据」而非 NaN;读屏可完整念出计划数、件数、完成率与三态明细。折叠态同样可读。
|
|
108
|
+
- **折叠保留 DOM**:折叠走 `hidden`(布尔 true,不是空字符串)+ `--folded` CSS 类隐藏,卡片节点不卸载;`aria-expanded` / `aria-controls` 指向真实存在的节点。
|
|
109
|
+
- **HTML 内容模型修正**:折叠态组头是 `<button>`,原 `<h2>` 嵌在按钮内不合法;改用 `<span role="heading" aria-level="2">`,文档大纲语义不变。
|
|
110
|
+
- **视觉精修**(只加细节,不改结构):组左栏 3px 竖向渐变强调条(位置+形状编码,非颜色唯一)、卡片内阴影与顶部高光、组头悬停反馈、图表标题色块、数字统一 `tabular-nums`;装饰性动效全部在 `prefers-reduced-motion` 下关闭。
|
|
111
|
+
|
|
112
|
+
**验证**:`npm test` **73/73 通过**(T5/T6 新增 6 例);`npm run build` 无 TS 报错;渲染探针见上。
|
|
113
|
+
|
|
114
|
+
**未验证**:真实浏览器目视、窄侧栏换行、深色模式观感 —— 需重启 `dsh web` 后人工确认。
|
|
115
|
+
|
|
116
|
+
## 1.4.0 — 2026-10-27
|
|
117
|
+
|
|
118
|
+
侧栏「任务进度」一眼分清「进行中 / 已完成」。(用户确认口径:严格两组;允许引入图表库。)
|
|
119
|
+
|
|
120
|
+
- **分组**:面板改成两组 —— 「进行中」(橙色,左侧橙色竖条 + 呼吸圆点)与「已完成」(蓝色)。判定只认 `phase === 'done'` 进「已完成」;`blocked / degraded / paused` 置顶并显示「需处理 N」红色徽章;`planning / executing / integrating` 次之;`pending` 最后。已取消计划继续不显示(与 1.3.7 一致)。
|
|
121
|
+
- **图表**:引入 `chart.js@4.6.1`,以 `vendor/chart.umd.js` 随包分发(MIT,未走打包器——本插件客户端是手写的 `window.__ModuleLoader__` 模块工厂,无 bundler)。新增「两类任务占比」环形图(渐变)与「进行中计划完成度」横向条形图,均带数值与无障碍标签。
|
|
122
|
+
- **折叠**(对齐设计稿 `docs/status-grouping-ia.md` §6.4 #1/#6):存在进行中任务时,「已完成」组默认折叠(组头可点、`aria-expanded` 同步、chevron 形状编码),避免历史任务抢走视觉权重;无进行中任务时默认展开。折叠态只在本组件内存里,不写偏好,刷新即回默认。
|
|
123
|
+
- **降级**:图表脚本未加载或宿主未重启时,首帧即渲染纯 CSS 双色占比条+图例,不出现空画布;`prefers-reduced-motion` 下关闭动效与脉冲。
|
|
124
|
+
- **宿主**:新增只读路由 `/dsh-workplan/vendor/chart.umd.js`(`text/javascript`,1 小时缓存);其余路由、工具、数据结构一字未动。
|
|
125
|
+
- **兼容修复**:`GroupCharts` 原用 `useRef`,而宿主注入的 React 兼容层未提供该 hook,`npm test` 实测抛 `TypeError: useRef is not a function`(3 个渲染用例失败)。改为「状态即 ref」(`useState` + 回调 ref,节点变化才更新),不再依赖 `useRef`;改后 `npm test` **67/67 通过**。
|
|
126
|
+
- **未验证**:本版本只完成编译与产物校验(`client.js` 含分组/图表/降级标记,`lib/host.js` 版本为 `1.4.0`)。**真实界面渲染需重启 `dsh web` 并刷新页面后才能看到**,尚未做视觉走查与浏览器截图验证。
|
|
127
|
+
|
|
128
|
+
## 1.3.7 — 2026-10-26
|
|
129
|
+
|
|
130
|
+
真机重启后当场复现,1.3.6 只修了一半。
|
|
131
|
+
|
|
132
|
+
- **复现**:重启后派发旧计划 `wp-8bc5c900`,返回 `Error: planId 与当前计划不匹配`。
|
|
133
|
+
- **根因**:1.3.6 只在「内存里没有记录」时重建。但重启后下一个回合的规划器会立刻生成新计划,内存马上就有一条别的计划记录,于是 `if (!record?.plan)` 为假,重建根本没跑,直接落到 planId 比对失败。重启窗口里内存为空的状态只存在几秒,实际用户永远撞不到那一半。
|
|
134
|
+
- **修复**:`dispatchTool` 现在在「内存无记录」**或**「调用方点名了另一份计划」时都重建(重建期间有派发在跑则不动),按 planId 从会话存档精确取回。点名一份存档里存在的计划,任何时候都应可派发。
|
|
135
|
+
- **验证**:新增用例「内存里是另一份计划时,按 planId 派发旧计划照样重建并继续跑」;`node --test` **67/67 通过**。用真实会话存档(2677 条事件)重放:不传 planId → 最新计划 `wp-0fd49b2d`、`pending`;传 `wp-8bc5c900` → `paused`、9 完成 / 2 未完成(T10、T11);传不存在的 planId → `null`。
|
|
136
|
+
|
|
137
|
+
## 1.3.6 — 2026-10-26
|
|
138
|
+
|
|
139
|
+
真机重启后暴露的新缺陷:**重启前暂停的旧计划会变得不可派发**。
|
|
140
|
+
|
|
141
|
+
- 现象(代码推定 + 单测复现):`reconstructFromSession` 只回放“最新那条计划消息”。重启后下一个回合自动生成了新计划,此时再派发重启前那份暂停计划,重建得到的是新计划,`dispatchTool` 随即抛 `PLAN_ID_MISMATCH`。磁盘上还有未完成的工作,却没有任何入口能继续它——又是一种“卡死”。
|
|
142
|
+
- 修复:`reconstructFromSession(session, targetPlanId?)` 现在为每条计划保留一份快照(状态、`resultPhase`、`resumeCount`、取消标记),派发时按 `planId` 精确取回;不传 `planId` 时行为不变(仍取最新计划,仪表盘语义不变)。
|
|
143
|
+
- 测试:新增「重启后仍能派发重启前暂停的旧计划」用例(两条计划在会话里、记忆为空、派发旧计划并跑完待续子任务)。`node --test` **66/66 通过**。
|
|
144
|
+
|
|
145
|
+
## 1.3.5 — 2026-10-26
|
|
146
|
+
|
|
147
|
+
真实进程(PID 7752,1.3.4)11 子任务派发:**完成 9、受阻 0、待续 2**,`phase: "paused"`、`resumeAvailable: true`、`errors` 只有一条预算说明——暂停语义与载荷字段在 1.3.4 真实进程再次成立。同一轮子任务查出两个真问题:
|
|
148
|
+
|
|
149
|
+
- **调用点变异存活(T4 实测)**:把 `buildRoundResult(record, …)` 改成 `buildRoundResult({ ...record, phase: undefined }, …)`,65/65 仍然全绿——所有用例都直接调函数本体,没有一条断言「session 里真正追加的那条结果消息」。现补端到端断言:重启重建用例派发后从会话取出 `workplan-result` 消息,断言 `phase === 'integrating'` 且键存在。同一变异现在会让用例变红(实测 64 pass / 1 fail)。
|
|
150
|
+
- **修复点 1 尚未在真实进程被触发(T7 实测)**:本轮派发对象是重启**之后**新建的计划(20:54:05 建、进程 20:48:23 启动),内存本来就热,`if (!record?.plan) reconstructFromSession(...)` 分支根本没进入。时间线证明「没报 NO_PLAN」≠「重建生效」。该修复目前仍只有代码 + 单测证据,需「先建计划、后重启、再派发」才能验证;验证动作安排在下一轮。
|
|
151
|
+
- **修复点 2 已取得运行时证据(T8)**:用真实事件流 + 进程实际加载的 `lib/runtime.js` 重放 20:54:18 派发边界 → 新计划 `phase: pending`、`resumeCount: 0`;把同一事件流喂给「删掉重置三行」的对照产物 → 立刻退回 `phase: paused`、`resumeAvailable: true`。对照组精确复现旧症状,说明这三行是承重的。
|
|
152
|
+
- **文档事实核对(T2)**:`plugins/.backups/dsh-workplan-1.3.4-20261026-2042.tar.gz` 文件名写的 20:42,文件系统记录的创建/修改时间是 20:32:15(构建窗口 20:32:07),命名与实际落盘时间不一致;内容本身与当前磁盘树逐字节相同(`lib/host.js` sha256 `3eea645b…`)。此处仅记录事实,不改写历史快照。
|
|
153
|
+
|
|
154
|
+
测试:65 例全部通过,其中「重启重建」一例新增端到端载荷断言。
|
|
155
|
+
|
|
156
|
+
## 1.3.4 — 2026-10-26
|
|
157
|
+
|
|
158
|
+
真实进程(PID 96748,1.3.3)的 11 子任务派发结果证明 1.3.3 的三处修正全部生效:`phase: "paused"` 进了载荷、`resumeAvailable: true`、`errors` 只剩一条预算说明、**受阻 0 / 完成 9 / 待续 2**。同一轮的子任务又查出三个新缺陷,本版修掉:
|
|
159
|
+
|
|
160
|
+
- **暂停后重启 `dsh web`,计划再也派发不了**:`dispatchTool` 只读内存记录,重启后内存为空就直接报 `NO_PLAN`「当前会话没有可派发的计划」——而卡片还在界面上。用户照我们的话重启一次,就把可续跑的计划变成了永久不可续跑。现在派发前会 `reconstructFromSession` 从会话存档重建记录并登记回内存。
|
|
161
|
+
- **上一份计划的暂停会污染下一份计划**:`reconstructFromSession` 的 `resultPhase` 在新计划消息到达时没有清零,于是同一会话里新建的计划在首次派发前就显示「预算用完」。现在新计划到达时重置 `resultPhase`/`resultSeen`/`resumeCount`。
|
|
162
|
+
- **结果写入处的接线没有测试**:1.3.3 的变异测试(B2:把 `phase: record.phase` 改成 `undefined`)存活,62 个测试全绿——没有任何用例覆盖调用点。现在结果消息构造抽成导出的 `buildRoundResult(record, sessionId, limits)`,并新增用例;同一变异现在会让测试变红(已实测)。
|
|
163
|
+
|
|
164
|
+
测试:新增 3 例(结果载荷必须携带阶段、新计划不继承暂停、重启后派发自动重建计划),全量 **65/65 通过**。
|
|
165
|
+
|
|
166
|
+
## 1.3.3 — 2026-10-26
|
|
167
|
+
|
|
168
|
+
真实进程(PID 29790,1.3.2)端到端派发已证明「预算到点 = 暂停而不是失败」:8 个子任务完成 4 个、**受阻 0 个**、其余 4 个保持 pending,面板显示「本轮预算用尽,可续跑」,没有再出现级联受阻。本轮派发又暴露三处收尾缺陷:
|
|
169
|
+
|
|
170
|
+
- **暂停计划被说成“不可续跑”**:`canResumePlan` 只认 `blocked`,而暂停留下的是 `pending`,于是载荷里 `resumeAvailable: false`,卡片提示「自动续跑额度已用尽,需要主 Agent 直接接手」——与事实相反(再派发一次就能继续)。现在 `paused` 也算可续跑,且暂停不消耗续跑额度(它是时间切片,不是返工)。
|
|
171
|
+
- **结果消息不带阶段,刷新后暂停会被回放成“正在做”**:结果载荷缺 `phase` 字段,`reconstructFromSession` 只能猜 `executing`。现在结果消息写入本轮 `phase`,刷新后原样回放 `paused`。
|
|
172
|
+
- **暂停仍把被杀子任务的「总执行超时」写进失败清单**:面板虽然显示正常,但载荷 `errors` 里留着 `["T4: 总执行超时", "总执行超时", …]`,模型会读成“有失败”。现在预算分支会剔除这些超时行,只保留「本轮执行预算(N 分钟)已到……」这一条说明。
|
|
173
|
+
|
|
174
|
+
测试:新增 3 例(暂停后可续跑且不扣额度、结果消息往返携带阶段、失败清单不含超时),全量 **62/62 通过**。
|
|
175
|
+
|
|
176
|
+
## 1.3.2 — 2026-10-26
|
|
177
|
+
|
|
178
|
+
真实进程(PID 24118,1.3.1)端到端派发暴露的剩余缺陷,全部来自“计划总预算”这条路径:
|
|
179
|
+
|
|
180
|
+
- **20 分钟总预算把正常长任务判成失败**:T1 真实跑了 840 秒才完成(1.2.1 的 300 秒上限下必死),T2 137 秒,随后总预算到点,正在跑的 T3 被记为「总执行超时」,T4–T9 级联受阻,面板再次显示「0~2/N 做好了、N 卡住了」。
|
|
181
|
+
- 修复:预算到点不再算失败——未完成子任务保持 `pending`(已完成的不重跑),计划阶段标记为新 `paused`,错误文案写明「本轮执行预算(N 分钟)已到:已完成 X/Y,其余 Z 项保留为未完成,再次派发即可续跑」。
|
|
182
|
+
- 面板与工具输出支持 `paused`:中文「预算用完,可续跑」(warn 色,不显示为「卡住了」),刷新后仍保持该阶段(按结果消息回放)。
|
|
183
|
+
- 自动续跑额度 2 → 4:一次派发最多 20 分钟,慢计划分多轮续跑,避免一整条计划被 20 分钟卡死。
|
|
184
|
+
- 规划器约束:子任务不得写「调用 dsh_workplan_dispatch / 重启或停止 dsh web / 安装升级插件」这类只有主 Agent 能执行的动作(本轮 T3「执行真实派发」对子 Agent 根本不可执行,必然失败);单个子任务应在 10 分钟内完成。
|
|
185
|
+
- 子 Agent 提示:明确“没有派发权限、不能重启服务”,遇这类步骤直接说明需要主 Agent 执行,禁止空等。
|
|
186
|
+
|
|
187
|
+
## 1.3.1 — 2026-10-26
|
|
188
|
+
|
|
189
|
+
让「卡住了」变成一个看得懂、走得通的提示,而不是死路。
|
|
190
|
+
|
|
191
|
+
- 仪表盘在受阻计划上直接给出续跑指引:`对主 Agent 说「续跑这个计划」,会从受阻处继续(已完成的不重跑)`,并显示剩余额度(如「还可续跑 2 次」)。
|
|
192
|
+
- 续跑额度用尽时,文案改为 `自动续跑额度已用尽,需要主 Agent 直接接手完成`,不再只显示「卡住了」。
|
|
193
|
+
- **修复假取消**:超过 10 分钟未派发的计划原先会被重新标记为 `cancelled`,而 `cancelled` 永远不可续跑——一张没人动过的计划会自己「死掉」。现在保持 `pending`,只额外给出 `stale: true` 标记,提示而不是关死。
|
|
194
|
+
- `reconstructFromSession` 的载荷补上 `resumeLimit`,与实时计划保持一致。
|
|
195
|
+
- 新增 `tests/client-resume-hint.test.ts`(3 个用例):有额度、额度用尽、pending 不被误显示为已取消。
|
|
196
|
+
|
|
197
|
+
## 1.3.0 — 2026-10-26
|
|
198
|
+
|
|
199
|
+
卡住(stuck)问题的系统性修复。所有改动都对应 1.2.1 及更早版本中可复现的“计划卡住”路径,回归测试见 `tests/workplan-stuck-recovery.test.ts`。
|
|
200
|
+
|
|
201
|
+
### 根因与修复
|
|
202
|
+
|
|
203
|
+
1. **固定 300 秒墙钟杀死了健康的子任务**(历史证据:22 次 `子任务 T? 超时(300000ms)`)。
|
|
204
|
+
- 现在把“绝对上限”和“无进展窗口”分开:默认绝对上限 900 秒(可配置 1s–30min),无进展窗口默认 300 秒(可配置 1s–15min)。
|
|
205
|
+
- 只要子 Agent 的会话日志还在增长,就不算卡住;只有日志停止增长才判定 wedged。
|
|
206
|
+
- 新增 `src/watchdog.ts`(`pickTimeoutReason` / `createProgressWatchdog`),轮询间隔自动按窗口长度缩放。
|
|
207
|
+
- 无进展导致的失败会自动重试一次(换一个新的子 Agent),达到绝对上限则不重试,避免烧掉整份计划预算。
|
|
208
|
+
|
|
209
|
+
2. **重新派发受阻计划是“假成功”**(历史证据:约 12 次 dispatch 完成 0 个子任务)。
|
|
210
|
+
- 以前所有子任务都是 `blocked`,`readySubtasks()` 返回空,派发器直接 `ok: true`,模型以为成功,计划再也动不了。
|
|
211
|
+
- 现在 `canResumePlan()` / `resetBlockedStatuses()` 支持从受阻处续跑:只重置受阻/进行中的子任务,已完成的不重跑,每份计划最多续跑 2 次(可配置 0–5)。
|
|
212
|
+
- 续跑额度用尽时 `dsh_workplan_dispatch` 返回 `ok: false` + 明确原因 + `hint`,让主 Agent 直接接手,而不是重复派发。
|
|
213
|
+
- 取消(cancelled)的计划永不被自动续跑。
|
|
214
|
+
|
|
215
|
+
3. **执行中不能取消**(`计划正在执行,不能取消`)。
|
|
216
|
+
- 现在取消会中止正在运行的派发:`abortedRunning: true`,计划进入 `cancelled`,在途结果仍会写成 durable 消息。
|
|
217
|
+
|
|
218
|
+
4. **`dispatchPromise` 永挂后永远回答 already running**。
|
|
219
|
+
- 超过“计划预算 + 120 秒”仍未返回时,派发工具返回 `ok: false`、`stale: true`,并提示主 Agent 直接接手,不再无限 wait-for-success。
|
|
220
|
+
|
|
221
|
+
5. **`[object Object]` 取代真实错误**(历史证据:1 次)。
|
|
222
|
+
- 新增 `describeError()`,把 Error / 字符串 / 嵌套对象信封 / Envelope 文本统一渲染成可读文字;`record.errors` 与子任务 `error` 全部走它。
|
|
223
|
+
|
|
224
|
+
6. **缺少元数据直接炸掉子 Agent 提示词**。
|
|
225
|
+
- `buildChildPrompt()` 对 `taskUnderstanding` / `contradictions` / `method` 等字段做了防御性读取;以前重建或旧版本计划缺字段会让每个子任务瞬间失败、整份计划被判受阻。
|
|
226
|
+
|
|
227
|
+
7. **构建产物漏拷 `watchdog.js`**:`npm run build` 之后 `lib/` 缺少新模块会直接导致插件加载失败,构建脚本已修正。
|
|
228
|
+
|
|
229
|
+
8. **续跑后不再写 durable 结果消息**:续跑会清除 `resultWritten`,保证刷新页面后看到的是最新结果,而不是旧的受阻快照。
|
|
230
|
+
|
|
231
|
+
### 可观测性
|
|
232
|
+
|
|
233
|
+
- 结果消息新增 `resumeCount` / `resumeAvailable`,并直接告诉主 Agent「可再次 dispatch 续跑(剩余 N 次)」或「额度已用尽,请直接接手」。
|
|
234
|
+
- 仪表盘 payload 新增 `heartbeatAt`、`resumeCount`、`resumeLimit`、`resumeAvailable`、`dispatchElapsedMs`。
|
|
235
|
+
- 派发工具的渲染文本会带出 `仍在执行(已 Ns)` 与失败时的「下一步」提示。
|
|
236
|
+
|
|
237
|
+
### 可配置项
|
|
238
|
+
|
|
239
|
+
| 配置键 | 环境变量 | 默认 | 范围 |
|
|
240
|
+
| --- | --- | --- | --- |
|
|
241
|
+
| `childTimeoutMs` | `DSH_WORKPLAN_CHILD_TIMEOUT_MS` | 900000 | 1000–1800000 |
|
|
242
|
+
| `idleTimeoutMs` | `DSH_WORKPLAN_IDLE_TIMEOUT_MS` | 300000 | 1000–900000 |
|
|
243
|
+
| `totalTimeoutMs` | `DSH_WORKPLAN_TOTAL_TIMEOUT_MS` | 1200000 | ≥ 60000 |
|
|
244
|
+
| `maxResumes` | `DSH_WORKPLAN_MAX_RESUMES` | 2 | 0–5 |
|
|
245
|
+
|
|
246
|
+
### 测试
|
|
247
|
+
|
|
248
|
+
- 新增 `tests/workplan-stuck-recovery.test.ts`(11 个用例):续跑判定、状态重置、结果文案、超时分类、watchdog 增长/停滞、错误渲染、端到端「停滞→受阻→续跑→完成」、额度耗尽、执行中取消、陈旧派发。
|
|
249
|
+
- 全量 `npm test`:51 个用例全部通过。
|
|
250
|
+
|
|
251
|
+
## 1.2.1 — 2026-10-26
|
|
252
|
+
|
|
253
|
+
- “做好了/已完成” now uses sky blue (`#0ea5e9`); “正在做” uses orange (`#f97316`) across the summary strip, donut, legend, task bars, mini bars, cards, pills and tool-turn accents.
|
|
254
|
+
- Other status colors (blocked/pending) are unchanged; no layout, interaction or dependency changes.
|
|
255
|
+
- Added a static regression test for the two status color mappings.
|
|
256
|
+
|
|
257
|
+
## 1.2.0 — 2026-10-26
|
|
258
|
+
|
|
259
|
+
- Task progress rows in “每个任务的进度” are now buttons: one click expands the matching task card and scrolls it into view.
|
|
260
|
+
- Task cards expose a stable DOM anchor; scrolling uses `scroll-margin-top` so the fixed header does not cover the title.
|
|
261
|
+
- Keyboard users can Tab to a task row and press Enter/Space; focus moves to the expanded card.
|
|
262
|
+
- No new dependency; theme tokens and responsive layout are unchanged.
|
|
263
|
+
|
|
264
|
+
## 1.1.1 — 2026-10-26
|
|
265
|
+
|
|
266
|
+
- Plan mode no longer skips decomposition. The plugin now generates a plan in plan mode and explicitly defers dispatch until the user leaves plan mode.
|
|
267
|
+
- While plan mode is active, only `dsh_workplan_dispatch` is denied, with a clear reason; every other tool stays available, so there is no guard deadlock.
|
|
268
|
+
- A plan produced in plan mode is preserved across turns instead of being cancelled before the user can approve it.
|
|
269
|
+
|
|
270
|
+
# Changelog
|
|
271
|
+
|
|
272
|
+
## 1.1.0 — 2026-10-26
|
|
273
|
+
|
|
274
|
+
- Dashboard now shows a fifth `任务` cell in the circled summary strip, so the plan count is visible next to work-item and status counts.
|
|
275
|
+
- Added a status donut and per-task progress bars (SVG/CSS, no new dependency) for a more visual, less text-heavy overview.
|
|
276
|
+
- Added a compact progress bar to each collapsed task card, so progress is visible without expanding.
|
|
277
|
+
- Mobile: five-cell strip becomes a readable 2-column grid, with the task cell spanning the full width; the charts stack in one column.
|
|
278
|
+
- Version metadata is now `1.1.0` in the plugin manifest and host `meta`.
|
|
279
|
+
|
|
280
|
+
## 1.0.0
|
|
281
|
+
|
|
282
|
+
- Initial TypeScript implementation: pre-step task decomposition, real department subagents, durable plan/result messages, `dsh_workplan_update` / `dsh_workplan_dispatch` / `dsh_workplan_cancel`, and the responsive `任务进度` dashboard.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-workplan contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Task Progress (dsh-workplan)
|
|
2
|
+
|
|
3
|
+
[](https://github.com/abbaababa/dsh-workplan/actions/workflows/verify.yml)
|
|
4
|
+
|
|
5
|
+
**中文(简体)说明:** [README.md](README.md)
|
|
6
|
+
|
|
7
|
+
> Plan first, dispatch real department subagents, then show the progress in plain language.
|
|
8
|
+
>
|
|
9
|
+
> **No app to install, no DSH source to patch, no page replacement.** You get three things: a plan in the conversation, real subagents doing the work, and a **Task progress** panel in the sidebar.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it actually does
|
|
14
|
+
|
|
15
|
+
For every new task with a real goal, the plugin inserts three beats:
|
|
16
|
+
|
|
17
|
+
1. **Plan before acting.** Before the model's first step, it writes a plan: stages → concrete work items → which department owns each one → how to do it → what counts as done → what could go wrong.
|
|
18
|
+
2. **Show the plan first.** The plan appears in the conversation as a task-decomposition block, so you can see the reasoning and acceptance criteria instead of a one-line promise.
|
|
19
|
+
3. **Dispatch real work.** Each work item is handed to a **real DSH subagent** (up to 5 at a time), and results are written back to the session. The main agent must dispatch and wait for the department results before it continues.
|
|
20
|
+
|
|
21
|
+
Greetings, thanks, and status questions ("how far along are we?") do not trigger a plan.
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
| Item | Detail |
|
|
26
|
+
|---|---|
|
|
27
|
+
| DeepSeek Harness | the official Web app (`dsh web` / `--profile web`) |
|
|
28
|
+
| Node.js | **22 or newer** |
|
|
29
|
+
| Network | npm registry access during install |
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
dsh plugin --profile web add dsh-workplan@1.4.3
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Restart the DSH Web process (or reload the page) so the bundle is picked up.
|
|
38
|
+
|
|
39
|
+
### From source (optional)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/abbaababa/dsh-workplan.git
|
|
43
|
+
cd dsh-workplan
|
|
44
|
+
npm ci && npm run build
|
|
45
|
+
dsh plugin --profile web add "$(pwd)"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Use it
|
|
49
|
+
|
|
50
|
+
1. Send a task with a real goal, e.g. "raise coverage in this directory to 80%".
|
|
51
|
+
2. The task-decomposition plan shows up in the conversation.
|
|
52
|
+
3. Keep talking normally; the main agent dispatches the plan and asks you whenever a decision is needed.
|
|
53
|
+
4. Open **Task progress** in the sidebar:
|
|
54
|
+
|
|
55
|
+
- five headline numbers: **tasks / work items / done / in progress / blocked**;
|
|
56
|
+
- plan cards grouped into **In progress** and **Completed**, each with a completion rate;
|
|
57
|
+
- expand any plan to see per-stage progress and who owns each work item.
|
|
58
|
+
|
|
59
|
+
Every number is derived from the same task state — the panel can no longer claim "0 in progress" while the list below shows work running. That inconsistency is exactly what 1.4.3 fixed.
|
|
60
|
+
|
|
61
|
+
## Model tools (you don't call these yourself)
|
|
62
|
+
|
|
63
|
+
| Tool | Purpose |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `dsh_workplan_dispatch` | Dispatch the current plan, wait for department subagents, write results back. Idempotent. |
|
|
66
|
+
| `dsh_workplan_update` | Main agent only: correct a subtask's status and evidence. |
|
|
67
|
+
| `dsh_workplan_cancel` | Cancel a plan that has not been dispatched; never fabricates completion. |
|
|
68
|
+
|
|
69
|
+
In **plan mode** the plan is shown for review and `dispatch` is explicitly refused with a reason.
|
|
70
|
+
|
|
71
|
+
## Budgets, timeouts, resumes
|
|
72
|
+
|
|
73
|
+
| Setting | Default | Environment variable |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| Dispatch budget per round | 20 min | `DSH_WORKPLAN_TOTAL_TIMEOUT_MS` |
|
|
76
|
+
| Single subagent ceiling | 15 min | `DSH_WORKPLAN_CHILD_TIMEOUT_MS` |
|
|
77
|
+
| No-progress window | 5 min | `DSH_WORKPLAN_IDLE_TIMEOUT_MS` |
|
|
78
|
+
| Automatic resumes | 4 | `DSH_WORKPLAN_MAX_RESUMES` |
|
|
79
|
+
| Subagent concurrency | 5 (fixed) | — |
|
|
80
|
+
|
|
81
|
+
Running out of budget is not a failure: the plan becomes **"budget used, resumable"**, finished work is not redone, and the remainder continues on the next dispatch. A stalled subtask blocks only itself.
|
|
82
|
+
|
|
83
|
+
Limits can also be set in the plugin config:
|
|
84
|
+
|
|
85
|
+
```jsonc
|
|
86
|
+
{ "totalTimeoutMs": 1800000 }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Security and privacy
|
|
90
|
+
|
|
91
|
+
- **No third-party runtime dependencies** — `dependencies` is empty.
|
|
92
|
+
- **No filesystem writes** — no `writeFileSync` / `appendFileSync` / `mkdirSync` in `src/`; results go into the DSH session record.
|
|
93
|
+
- **No outbound traffic** — no `child_process`, no external `fetch`/`http.request`; the one `fetch` is the browser asking its own origin for `/dsh-workplan/plans`.
|
|
94
|
+
- **The panel route is authenticated** — unauthenticated requests get 401.
|
|
95
|
+
- **Chart.js is vendored** (`vendor/chart.umd.js`, MIT); no CDN script is loaded.
|
|
96
|
+
|
|
97
|
+
Verify it yourself:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm view dsh-workplan@1.4.3 dependencies
|
|
101
|
+
grep -RInE "writeFileSync|child_process|eval\(" src
|
|
102
|
+
npm audit --omit=dev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## What 1.4.3 fixes
|
|
106
|
+
|
|
107
|
+
1.4.3 is a counting-consistency release. The plugin used to keep two sources of truth — live subtask state and a cached stats snapshot — so the panel could disagree with reality.
|
|
108
|
+
|
|
109
|
+
| Defect | Symptom | Cause |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| D1 | numbers frozen for minutes | stats recomputed only after a whole wave of subagents finished |
|
|
112
|
+
| D2 | two numbers in one payload disagree | counts came from the cache, department totals from live state |
|
|
113
|
+
| D3 | "not started" item showing 100% with a stale owner | budget reset bypassed the state machine |
|
|
114
|
+
| D4 | the same failure reported at 100% or 0% | two code paths disagreed on blocked progress |
|
|
115
|
+
|
|
116
|
+
The fix removes the cached snapshot: every number is derived at serialisation time by one function, guarded by 7 invariants and mutation tests. Full report: [docs/fix-report-derived-counts-1.4.3.md](docs/fix-report-derived-counts-1.4.3.md).
|
|
117
|
+
|
|
118
|
+
## FAQ
|
|
119
|
+
|
|
120
|
+
**Nothing happens after installing.** Restart DSH Web; confirm `dsh-workplan` is in `~/.dsh/profiles/web/package.json`.
|
|
121
|
+
|
|
122
|
+
**The panel is empty.** It shows plans for the current session only.
|
|
123
|
+
|
|
124
|
+
**Numbers are lower than before.** That is the fix: 1.4.3 derives them from real state, and the old numbers were frequently inflated.
|
|
125
|
+
|
|
126
|
+
**A subagent stopped moving.** Five minutes without new events marks it blocked, in isolation. Tell the main agent to resume the plan.
|
|
127
|
+
|
|
128
|
+
## Uninstall
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
dsh plugin --profile web remove dsh-workplan
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm ci
|
|
138
|
+
npm run check # tsc --noEmit
|
|
139
|
+
npm test # 90 tests, including count invariants and mutation tests
|
|
140
|
+
npm run build # src/*.ts → lib/*.js, client.js
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`lib/` and `client.js` are committed build output; CI rebuilds them and fails if they drift.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT — see [LICENSE](LICENSE). Chart.js is MIT as well.
|
package/README.md
CHANGED
|
@@ -1,3 +1,162 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 任务进度(dsh-workplan)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/abbaababa/dsh-workplan/actions/workflows/verify.yml)
|
|
4
|
+
|
|
5
|
+
**English (US) — plain-language guide:** [README.en.md](README.en.md)
|
|
6
|
+
|
|
7
|
+
> 给 **DeepSeek Harness(DSH)** 加一个「先拆任务、再真的派人去做、最后让你一眼看懂进度」的能力。
|
|
8
|
+
>
|
|
9
|
+
> **不用装 App,不改 DSH 源码,不替换页面。** 装上以后多三样东西:对话里的计划、真的部门子 Agent、左侧栏一个「任务进度」面板。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 它到底做了什么(用大白话讲)
|
|
14
|
+
|
|
15
|
+
平时你给 AI 一个复杂任务,它经常「一边想一边做」,做到一半你才发现方向不对。
|
|
16
|
+
|
|
17
|
+
这个插件把节奏改成三拍:
|
|
18
|
+
|
|
19
|
+
1. **先排计划**:你发出任务后、AI 真正动手前,它先写一份计划——分几个阶段、每个阶段几件具体工作、每件工作交给哪个部门、怎么做、什么算做完、可能卡在哪。
|
|
20
|
+
2. **计划先给你看**:这份计划以「任务分解」的形式出现在对话里,包含每一步的依据与验收标准,而不是一句话糊过去。
|
|
21
|
+
3. **真的派人去做**:确认后,每一件具体工作交给一个**真实的 DSH 子 Agent** 去执行(最多 5 个同时跑),结果写回会话;你在左侧栏「任务进度」里随时能看到谁在做、做到哪、哪件卡住了。
|
|
22
|
+
|
|
23
|
+
计划不是装饰品:主 Agent 必须先派发、等到部门结果回来,才能继续往下答。
|
|
24
|
+
|
|
25
|
+
## 装之前你需要什么
|
|
26
|
+
|
|
27
|
+
| 要求 | 说明 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| DeepSeek Harness | 官方 Web 版(`dsh web` / `--profile web` 跑的这套) |
|
|
30
|
+
| Node.js | **22 或更高**(`node -v` 自查) |
|
|
31
|
+
| 网络 | 安装时需要能访问 npm registry |
|
|
32
|
+
|
|
33
|
+
## 一键安装
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
dsh plugin --profile web add dsh-workplan@1.4.3
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
装完**重启一次 DSH Web**(或重新载入当前页面)让插件生效。
|
|
40
|
+
|
|
41
|
+
没有 `dsh` 命令的话,先确认它在 PATH 里;用 `npx` 起的 DSH 可以直接用完整路径调用同一个 `dsh`。
|
|
42
|
+
|
|
43
|
+
### 从源码安装(可选,想改代码时用)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/abbaababa/dsh-workplan.git
|
|
47
|
+
cd dsh-workplan
|
|
48
|
+
npm ci && npm run build
|
|
49
|
+
dsh plugin --profile web add "$(pwd)"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 装好以后怎么用(小白版)
|
|
53
|
+
|
|
54
|
+
1. 打开 DSH Web,随便发一个**有实际目标**的任务,例如「帮我把这个目录的测试补到 80% 覆盖」。
|
|
55
|
+
2. 等一两秒,对话里会出现一块【任务分解】:阶段、子任务、部门、验收标准齐全。
|
|
56
|
+
3. 你只需要正常对话;主 Agent 会按规则派发计划,遇到要拍板的地方会问你。
|
|
57
|
+
4. 点左侧栏的**「任务进度」**,就能看到:
|
|
58
|
+
|
|
59
|
+
- 顶部五个数字:**几个任务 / 分配了多少件工作 / 做好了 / 正在做 / 卡住了**;
|
|
60
|
+
- 计划卡:分「进行中」「已完成」两组,右上角是完成率;
|
|
61
|
+
- 展开任意计划:每个阶段的进度、每件具体工作由哪个部门负责、当前状态。
|
|
62
|
+
|
|
63
|
+
5. 进度数字背后的每一行,都来自同一份任务状态——**不会再出现「显示 0 个正在做、但下面明明写着正在做」这种自相矛盾**(1.4.3 专门修的就是这个,见下文)。
|
|
64
|
+
|
|
65
|
+
纯打招呼、道谢、问「做到哪了」不会触发拆解,不会平白多出一堆子 Agent。
|
|
66
|
+
|
|
67
|
+
## 三个模型工具(你不用直接调用)
|
|
68
|
+
|
|
69
|
+
| 工具 | 作用 |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `dsh_workplan_dispatch` | 派发当前计划,等各部门子 Agent 完成,把结果写回会话。重复调用是幂等的。 |
|
|
72
|
+
| `dsh_workplan_update` | 主 Agent 核对结果、修正某个子任务状态与证据。子 Agent 不能调用。 |
|
|
73
|
+
| `dsh_workplan_cancel` | 取消还没派发的计划,不会伪造任何「已完成」。 |
|
|
74
|
+
|
|
75
|
+
在 **plan 模式**下只会给你看计划,`dispatch` 会被明确拦下并说明原因,不会偷偷开跑。
|
|
76
|
+
|
|
77
|
+
## 预算、超时与续跑(知道这些就不会误以为卡死)
|
|
78
|
+
|
|
79
|
+
| 项 | 默认值 | 怎么改 |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| 单轮派发预算 | 20 分钟 | `DSH_WORKPLAN_TOTAL_TIMEOUT_MS` |
|
|
82
|
+
| 单个子 Agent 上限 | 15 分钟 | `DSH_WORKPLAN_CHILD_TIMEOUT_MS` |
|
|
83
|
+
| 子 Agent 无进展判定 | 5 分钟 | `DSH_WORKPLAN_IDLE_TIMEOUT_MS` |
|
|
84
|
+
| 自动续跑次数 | 4 次 | `DSH_WORKPLAN_MAX_RESUMES` |
|
|
85
|
+
| 并发子 Agent | 5 个(固定) | — |
|
|
86
|
+
|
|
87
|
+
预算用尽不是失败:计划会变成「**本轮预算用尽,可续跑**」,已完成的不重跑,剩下的下次接着做。被判定卡住的子任务只卡它自己,不会拖垮整个计划。
|
|
88
|
+
|
|
89
|
+
环境变量写在启动 DSH 的那个 shell 里,或者写进插件配置:
|
|
90
|
+
|
|
91
|
+
```jsonc
|
|
92
|
+
// cordis.patch.yml 里的 config 字段
|
|
93
|
+
{ "totalTimeoutMs": 1800000 } // 30 分钟
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 安全与隐私(可以自己复核)
|
|
97
|
+
|
|
98
|
+
- **没有第三方运行时依赖**:`dependencies` 是空的;装出来的只有这个插件自己的 JS、配置、图标和说明。
|
|
99
|
+
- **不写你的磁盘**:源码里没有任何 `writeFileSync` / `appendFileSync` / `mkdirSync`(可 `grep` 验证);结果只写进 DSH 会话记录。
|
|
100
|
+
- **不向外发数据**:源码里没有 `child_process`、没有对外 `fetch`/`http.request`;唯一的一次 `fetch` 是浏览器端请求**同源**的 `/dsh-workplan/plans`。
|
|
101
|
+
- **面板数据要登录**:该路由走 DSH 自己的鉴权,未登录直接返回 401。
|
|
102
|
+
- **图表库是本地自带**:`vendor/chart.umd.js`(Chart.js 4.6.1,MIT)随包分发,不加载任何 CDN 脚本。
|
|
103
|
+
|
|
104
|
+
自己复核:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm view dsh-workplan@1.4.3 dependencies # 应为空
|
|
108
|
+
grep -RInE "writeFileSync|child_process|eval\(" src # 应无命中
|
|
109
|
+
npm audit --omit=dev
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 1.4.3 修了什么(为什么值得升级)
|
|
113
|
+
|
|
114
|
+
1.4.3 是一次**计数一致性**修复:插件曾同时维护「实时任务状态」和「缓存的统计快照」两份真相,导致面板数字和实际状态对不上(截图里「0 个正在做」但下面有任务正在做,就是它)。
|
|
115
|
+
|
|
116
|
+
| 缺陷 | 表现 | 根因 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| D1 | 面板数字长时间不更新 | 统计只在整批子 Agent 跑完后才重算,运行期间冻结 |
|
|
119
|
+
| D2 | 同一份数据里两个数字互相矛盾 | 计数来自缓存,部门聚合来自实时状态 |
|
|
120
|
+
| D3 | 「还没开始」的任务却显示 100%、带着上一次的执行者 | 预算重置绕过了状态机,旧字段没清 |
|
|
121
|
+
| D4 | 同一类失败,进度有时 100 有时 0 | 两处代码对「卡住」的进度语义不一致 |
|
|
122
|
+
|
|
123
|
+
修法是**删掉缓存快照,只保留一份状态源**:所有数字在序列化时由同一函数现场派生,并配了 7 条不变量 + 变异测试防止回退。完整报告:[docs/fix-report-derived-counts-1.4.3.md](docs/fix-report-derived-counts-1.4.3.md)。
|
|
124
|
+
|
|
125
|
+
## 常见问题
|
|
126
|
+
|
|
127
|
+
**装完没反应?**
|
|
128
|
+
先重启 DSH Web;再确认 `~/.dsh/profiles/web/package.json` 的依赖里有 `dsh-workplan`。
|
|
129
|
+
|
|
130
|
+
**面板一直是空的?**
|
|
131
|
+
面板只显示**当前会话**的计划。换一个会话、或这个会话还没产生计划时是空的,这是正常的。
|
|
132
|
+
|
|
133
|
+
**数字和以前不一样了?**
|
|
134
|
+
1.4.3 之后数字来自真实状态而不是缓存,所以会比旧版本「更小、但更准」(旧版本的完成率经常虚高)。
|
|
135
|
+
|
|
136
|
+
**子 Agent 卡住不动?**
|
|
137
|
+
5 分钟没有任何新事件会被判定卡住并单独标红;对主 Agent 说「续跑这个计划」即可从受阻处继续。
|
|
138
|
+
|
|
139
|
+
## 卸载
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
dsh plugin --profile web remove dsh-workplan
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 开发与验证
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm ci
|
|
149
|
+
npm run check # tsc --noEmit
|
|
150
|
+
npm test # 90 个测试,含计数不变量与变异测试
|
|
151
|
+
npm run build # 编译 src/*.ts → lib/*.js、client.js
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`lib/` 与 `client.js` 是**提交进仓库的构建产物**,CI 会重建并校验二者无差异。
|
|
155
|
+
|
|
156
|
+
## 许可证
|
|
157
|
+
|
|
158
|
+
MIT,见 [LICENSE](LICENSE)。Chart.js 亦为 MIT。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
**English (US) — plain-language guide:** [README.en.md](README.en.md)
|