dsh-workplan 0.0.0-stage → 1.5.0
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 +334 -0
- package/LICENSE +21 -0
- package/README.en.md +165 -0
- package/README.md +179 -2
- package/client.js +2223 -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 +75 -4
- package/src/client.ts +2603 -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,334 @@
|
|
|
1
|
+
## 1.5.0 — 2026-10-28
|
|
2
|
+
|
|
3
|
+
**「进度必须一眼看得见」的图表化版本**。1.4.3 把数字收敛成单一来源,但计划卡折叠后仍然只有一行
|
|
4
|
+
12px 灰字(`本计划 0/8 做好了` + 54×5px 迷你条 + `0 卡住了`),0% 时迷你条宽度为 0、轨道色与
|
|
5
|
+
折叠按钮底色同 token,等于**没有图**;百分比只存在于 `title` 悬停提示里;组头在 0/0 时印
|
|
6
|
+
`未完成 0 / 已完成 0(完成率暂无数据)`。用户以截图指出三处画圈区域「不直观、不一目了然、不醒目」,
|
|
7
|
+
要求「必须通过图表等形式突出显示」。
|
|
8
|
+
|
|
9
|
+
### 改动(只动展示层,`plan.ts` / `runtime.ts` / `host.ts` 逻辑零改动)
|
|
10
|
+
|
|
11
|
+
- **折叠卡摘要改为图表**(`src/client.ts`,默认视图即可读,无需展开、无需悬停):
|
|
12
|
+
44px SVG 环 + 24px 百分比数字 + `本计划 X/Y 做好了` + 一行「现在在做什么」(卡住优先→正在做→
|
|
13
|
+
全部做完/还没开始,带任务名)+ 8px 四段构成条(按件数分宽,`done/active/blocked/pending`)+
|
|
14
|
+
11px 状态图例(`做好了 0 · 正在做 1 · 卡住了 0 · 还没开始 7`)+ 6px 阶段步进条
|
|
15
|
+
(每阶段一段,当前阶段描边,`data-stage-id` / `data-tone` / `data-current`)。
|
|
16
|
+
- **展开后新增「分步进度」**:逐阶段行(名称、`done/total`、4px 细条、状态)+加权总体进度,
|
|
17
|
+
替换旧的一根计划级进度条;折叠态不挂载,保持卡头紧凑。
|
|
18
|
+
- **0% 不再等于「没有图」**:四层信号同时存在——环的零轨加粗到 `rgba(100,116,139,.80)`(≈3.14:1,
|
|
19
|
+
`data-zero="true"`)、24px「0%」、整条灰色「还没开始」构成条、虚线阶段槽 + 当前阶段描边;
|
|
20
|
+
0% 不画 SVG 弧,避免圆头端点在 12 点方向留下与「已完成」混淆的小点。
|
|
21
|
+
- **组头收口**:0% 组环零轨同样加粗(`data-zero="true"`);0/0(没有工作项)时**删除**
|
|
22
|
+
`未完成 0 / 已完成 0(完成率暂无数据)` 整句,只保留环上的 `—` 与组内空态;100% 组
|
|
23
|
+
(`未完成 0 / 已完成 3(完成率 100%)`)整句保留,因为那正是用户要的结论。
|
|
24
|
+
- **零新增依赖**:进度全部由既有 `session.stats` / `session.stages` / `session.statuses` 派生,
|
|
25
|
+
纯函数组件,无 `useEffect`、无定时器、不引用 chart.js;`package.json` 无 `dependencies`。
|
|
26
|
+
注意 `node_modules` 里仍能看到 `chart.js@4.6.1`、`@kurkle/color@0.3.4` 标为 extraneous:
|
|
27
|
+
它们是 1.4.3 之后那次「移除 chart.js 依赖」改动未执行 `npm prune` 的残留,不出现在
|
|
28
|
+
`package.json`、不参与 `npm run build`,运行时图表库来自 `vendor/chart.umd.js` 路由。
|
|
29
|
+
- **版本**:`1.4.3 → 1.5.0`(minor,新增用户可见能力);`package.json` / `package-lock.json` 两处 /
|
|
30
|
+
`src/host.ts` 的 `meta.version` 同步,`lib/host.js`、`dist/host.js` 由 `npm run build` 生成。
|
|
31
|
+
|
|
32
|
+
### 验证
|
|
33
|
+
|
|
34
|
+
- `npm run check` → exit 0(strict + noImplicitAny)。
|
|
35
|
+
- `npm test` → **99/99 通过**(1.4.3 基线 90 项 + 新增 `tests/progress-visual.test.ts` 8 项 +
|
|
36
|
+
`tests/version-sync.test.ts` 1 项)。
|
|
37
|
+
新增用例锁定:0/8 的四个可见信号、3/8→38% 且构成条件数之和等于 total、卡住项
|
|
38
|
+
`data-alarm="true"` 且阶段 `data-tone="blocked"`、三个阶段恰好一个 `data-current="true"`、
|
|
39
|
+
图例恰好四项且顺序固定、折叠态不挂载分步面板、脏数据不产生 NaN/Infinity/undefined 且不超过 100%、
|
|
40
|
+
卡头不引入 canvas 或图表库。`version-sync` 锁定 `package.json` = `package-lock.json` 两处 =
|
|
41
|
+
`src/host.ts` 的 `meta.version`,升级时不需要改测试。
|
|
42
|
+
- 同步修改的既有断言:`tests/group-summary.test.ts`(两处 `完成率暂无数据` 改为「不再出现」)、
|
|
43
|
+
`tests/session-collapse.test.ts`(`0 卡住了` → 图例 `卡住了 0` + 可见 `100%`)。
|
|
44
|
+
|
|
45
|
+
### 回滚
|
|
46
|
+
|
|
47
|
+
- 改造前整树快照:`.backups/v1.4.3-pre-progress-viz-20261028-133958.tar.gz`
|
|
48
|
+
(SHA-256 `b8ccc0efa7c307160739ffecdb4af44daab44b3a0f0c779c74d9d0783f6993c7`)。
|
|
49
|
+
- 代码完成、版本号未升时的中间快照:`.backups/v1.4.3-progress-viz-code-20261028-142153.tar.gz`
|
|
50
|
+
(SHA-256 `c79513f5c0279e3bb291d536fba7a631fa8018e27bc7d4fca500fbabd9e602da`)。
|
|
51
|
+
- 恢复后必须 `npm run build` 并重启 DSH web profile(宿主启动时加载 `lib/host.js`,刷新浏览器不够)。
|
|
52
|
+
|
|
53
|
+
## 1.4.3 — 2026-10-28
|
|
54
|
+
|
|
55
|
+
**「同一个数字只能有一个来源」的根治版**。1.4.2 及更早版本里,同一个计划的数字存在两套真相源:
|
|
56
|
+
实时状态列表 `record.statuses` 与缓存在记录上的 `record.stats`。缓存只在**每批子任务全部跑完之后**
|
|
57
|
+
刷新一次,而看板头部读缓存、阶段徽标与卡片明细读列表,于是同一张卡片上会同时出现
|
|
58
|
+
「0 正在做」和「正在做」;预算用尽被重置成 pending 的子任务还带着上一轮的 `progress: 100`,
|
|
59
|
+
于是「0/3 做好」的阶段旁边挂着「正在做」、计划进度条显示 100% 而完成数是 0。
|
|
60
|
+
|
|
61
|
+
本版把计数改成**纯派生**:`derivePlanSnapshot(statuses)` 一次遍历同时产出 `statuses / stats / departments`,
|
|
62
|
+
再也没有第二份可被写坏的数字。
|
|
63
|
+
|
|
64
|
+
### 缺陷(4 条,全部有可复现证据)
|
|
65
|
+
|
|
66
|
+
用户报告(会话 `session-91ec3273-…`,19:02 截图):顶部汇总「0 正在做」,而计划卡里第 3 步挂着「正在做」。
|
|
67
|
+
以同一份会话数据重放,确认差异为**真实缺陷**而非观感问题:
|
|
68
|
+
|
|
69
|
+
- **D1 计数缓存冻结**(根因):`writeSubtaskStatus()` 只写 `record.statuses`,不写 `record.stats`;
|
|
70
|
+
`record.stats` 只在 `while(true)` 批次循环里、**整批子任务跑完之后**才重算。批次运行期间缓存一直是旧值,
|
|
71
|
+
`serialisePlan()` 又优先读缓存(`record.stats ?? computeStats(statuses)`,而 `stats` 恒已初始化,
|
|
72
|
+
回退分支永不触发)。实测:真实状态 `done 2 / 正在做 1 / 待做 0`,缓存仍报 `done 1 / 正在做 0 / 待做 2`,
|
|
73
|
+
进度条 33% 而真实进度 70%。
|
|
74
|
+
- **D2 同一报文自相矛盾**:`serialisePlan()` 里 `stats` 取自缓存、`departments` 却由 `statuses` 现场聚合,
|
|
75
|
+
一个 GET 响应内 `stats.inProgress = 0` 与 `design.inProgress = 1` 并存。
|
|
76
|
+
- **D3 重置绕过状态机**:预算用尽时用裸对象展开重置为 pending(`progress: item.progress ?? 0`),
|
|
77
|
+
绕过 `applyStatus()` 的伴随字段清理,留下 `pending + progress: 100 + childId + startedAt`。
|
|
78
|
+
连带后果:旧公式把每条 `progress` 直接平均(不计状态),12 项里 11 项完成也算出 **100%**。
|
|
79
|
+
- **D4 同一失败两种语义**:异常收敛分支把 blocked 写成 `progress: item.progress ?? 0`,
|
|
80
|
+
而其余 4 处 blocked 写入点都写 `progress: 100`。
|
|
81
|
+
|
|
82
|
+
### 修复
|
|
83
|
+
|
|
84
|
+
- **单一派生源**:`src/plan.ts` 新增 `StatusEntry / Stats / DepartmentSummary / PlanSnapshot` 与
|
|
85
|
+
`normaliseStatuses`、`deriveAggregates`、`derivePlanSnapshot`、`aggregateInvariantViolations`。
|
|
86
|
+
规则写死在一个地方:未知状态降级为 pending、pending 强制 `progress=0` 且清空 `childId/startedAt/finishedAt`、
|
|
87
|
+
done 强制 100、进度加权 done=100、blocked/in_progress=自身进度的 clamp、pending=0。
|
|
88
|
+
- **删除 `record.stats`**:`src/runtime.ts` 移除全部写入与读取点(16 处),`serialisePlan` /
|
|
89
|
+
`buildRoundResult` / `dispatchTool` / `updateStatus` / `cancelPlan` 全部改为「先派生一次,再用同一个快照填字段」。
|
|
90
|
+
`grep 'record.stats' src/runtime.ts` 现在只剩一条注释。
|
|
91
|
+
- **状态机收口**:预算用尽重置改为逐项 `applyStatus(..., { status: 'pending', error: '本轮预算用尽,等待续跑' })`;
|
|
92
|
+
`applyStatus` 的 pending 分支同时清空 `progress/startedAt/finishedAt/childId`(保留显式传入的 error)。
|
|
93
|
+
- **blocked 统一为 100**:异常收敛分支与其余写入点对齐,同一失败不再有两种进度。
|
|
94
|
+
- **构建卫生**:`build` 先 `rm -rf dist`、`test` 先 `rm -rf dist-tests`,并新增 `clean` 脚本。
|
|
95
|
+
旧编译物残留曾让 `npm test` 报 `ERR_MODULE_NOT_FOUND ... dist-tests/src/state.js`(本次已实测复现并修掉)。
|
|
96
|
+
- **移出失控产物**:子 Agent 遗留、未被任何生产代码引用的 `src/state.ts` 与 `tests/state-machine.test.ts`
|
|
97
|
+
移到 `docs/design-proposals/` 作参考(它与 `applyStatus` 重复实现同一语义,接入即再造双真相源);
|
|
98
|
+
`docs/design-derived-counts.reference.ts` 在设计落地后删除,`scripts/prove-derived-counts.ts`
|
|
99
|
+
改为直接导入 `src/plan.ts`,杜绝「设计与实现各算一套」。
|
|
100
|
+
|
|
101
|
+
### 验证
|
|
102
|
+
|
|
103
|
+
- `npm run check`:exit 0。
|
|
104
|
+
- `npm test`:**90 / 90 通过**(79 项原有 + 11 项新增 `tests/derived-counts.test.ts`)。
|
|
105
|
+
新增用例含**变异测试**:手工把 `stats.done +1`、`total +1`、`pending -1`、`progress=101`、部门 `done/total`
|
|
106
|
+
改坏,不变量检查器必须报错——证明断言有效,而不是复述当前行为。
|
|
107
|
+
- `node --experimental-strip-types scripts/repro-inconsistent-counts.ts --expect-fixed`:**全部不变量 PASS**
|
|
108
|
+
(场景 A 顶部 2 完成 / 1 正在做、卡片 70% / 正在做 1 / 待做 0、甜甜圈 67;场景 B 暂停项 `progress=0`、
|
|
109
|
+
`childId=null`、`stats.progress=0` 与甜甜圈一致;场景 C resume 后 blocked=0)。
|
|
110
|
+
同一脚本的默认模式(断言旧缺陷存在)现在 12 条全部失败——符合预期,缺陷已被消除。
|
|
111
|
+
- `node --experimental-strip-types scripts/prove-derived-counts.ts`:13 条断言全通过,含 500 组随机状态序列的
|
|
112
|
+
不变量与幂等检查。
|
|
113
|
+
|
|
114
|
+
### 兼容性
|
|
115
|
+
|
|
116
|
+
- 对外载荷字段不变:`statuses`、`stats`(`total/done/blocked/inProgress/pending/progress`)、
|
|
117
|
+
`departments` 同名同义;工具 JSON Schema 未改。
|
|
118
|
+
- `stats` 数值语义修正后可能与 1.4.2 的历史数字不同(这正是缺陷修复本身),旧结果消息仍可回放读取。
|
|
119
|
+
- `src/plan.ts` 的 `computeStats()` 保留为 `derivePlanSnapshot()` 的**别名**(标注 `@deprecated`),
|
|
120
|
+
外部调用者不需要改动即可获得正确结果。
|
|
121
|
+
|
|
122
|
+
### 回滚
|
|
123
|
+
|
|
124
|
+
**1.4.3 是本项目首次公开发布**,更早的 1.0.0–1.4.2 只在本地开发,没有发布到 npm,也没有对应的 git 标签,
|
|
125
|
+
因此公开仓库里没有 1.4.2 的可回滚版本。如果你是从本地开发副本升级上来的,回滚到升级前那份副本即可:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm ci && npm test # 回到升级前的行为,先确认它仍然可构建、测试通过
|
|
129
|
+
dsh plugin --profile web add /path/to/the/older/copy
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
已安装的运行实例需要重启 DSH(或重新加载 `web` profile)才会使用本版代码。
|
|
133
|
+
|
|
134
|
+
## 1.4.2 — 2026-10-27
|
|
135
|
+
|
|
136
|
+
运行态验收(用户重启后的真实页面复核,7 个子任务)驱动的一轮修正,全部为**可复现证据**下的收口。
|
|
137
|
+
|
|
138
|
+
- **图表文字在浅色主题下几乎不可见(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(深)**。这是本轮唯一的功能级缺陷。
|
|
139
|
+
- **两处「完成率」口径打架**:图表按**计划数**算(1/7 → 14%),组头摘要按**工作项**算(1/2 → 50%),同屏两个百分比互相打脸。修正:图表标题写明「两类计划数量占比」「进行中计划完成度(按工作项)」,口径写进标题,不再靠读者猜。
|
|
140
|
+
- **「进行中」一词自相矛盾**:组头摘要原写「进行中 N」,而 N 实际是「未完成件数」(含还没开始的),与同一按钮 aria-label 里的「正在做 0 件、还没开始 3 件」冲突。改为 **「未完成 N / 已完成 M(完成率 x%)」**,与 aria 三态明细口径一致。
|
|
141
|
+
- **主题切换后图表文字不变色**:`ink` 只在建图那一刻读取一次,切到深色主题后图表文字仍是浅色主题的颜色。改用 `MutationObserver` 监听 `body` 的 `data-ds-dark-theme` 属性重建图表(零依赖),切换主题即刷新文字色。
|
|
142
|
+
- **自动展开(设计稿 §7.3 补齐)**:①最后一个进行中任务完成时,已完成组自动展开一次;②用户正展开的卡片被移入已完成组时,该组自动展开。依赖均为布尔值,4 秒轮询不会反复弹开。
|
|
143
|
+
|
|
144
|
+
- **小字对比度未达标(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**,色相保留。
|
|
145
|
+
- **环形图弧线深色主题失效**:`--dsh-wp-done-ink` 硬编码 `#0284c7`,对深色底约 2.4:1;改挂同一个自适应色。
|
|
146
|
+
- **焦点环 2.66:1**:组头按钮 `:focus-visible` 用 `--dsh-wp-done`;改为主文本色(浅 18.12 / 深 16.30)。
|
|
147
|
+
- **首屏看不到任务卡(已按用户选择 B 修完)**:`GroupCharts` 两张卡各约 246px、总览块(完成率环 + 每计划进度条)也排在两组之前,首屏被三块内容占满。现顺序为:5 格数字统计条 → 一行口径说明 → 「进行中」组 → 「已完成」组 → 总览块 → 两张图表。打开面板第一屏即是进行中任务卡,所有图表下移到两组之后。
|
|
148
|
+
- **旧浏览器兜底**:`color-mix` 在 Safari <16.2 / Chrome <111 不可用,原写法会让 `--dsh-wp-done-text` 成为无效值。改为先声明主文本色兜底、再用 `@supports (color: color-mix(...))` 覆盖,旧浏览器只丢色相、不丢可读性。
|
|
149
|
+
- **宿主元数据版本滞后**:`src/host.ts` 与 `lib/host.js` 仍写 `1.4.1`,与 `package.json` 的 1.4.2 不一致;已同步为 1.4.2。
|
|
150
|
+
- **条形图静默吞掉第 7 个及以后的计划**:`groups.live.slice(0, 6)` 截断后没有任何提示,读者会以为图里就是全部。改为在图表下方注明「另有 N 个进行中计划未画入图表」(中英双语,零新增依赖)。
|
|
151
|
+
**验证**:`npm test` **79/79 通过**;`npm run build` 无 TS 报错。
|
|
152
|
+
|
|
153
|
+
## 1.4.1 — 2026-10-27
|
|
154
|
+
|
|
155
|
+
「一目了然 + 精美绝伦」的收口版本。1.4.0 存在的问题全部由本轮的**实测证据**驱动修正。
|
|
156
|
+
|
|
157
|
+
- **致命缺陷修复:图表永远不出现**。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`。
|
|
158
|
+
- **对比度达标(WCAG AA)**:组头计数徽章原为 12px 白字压在橙/蓝硬编码渐变上,实测 **2.26–2.80:1(进行中)/ 2.14–4.10:1(已完成)**,全部不合格;组头副标题 tertiary 实测 3.56:1 也不合格。改为「主文本色 + 半透明品牌底 + 1px 内描边」,品牌色只做底与描边。组头标题实测 **18.15:1(浅)/ 16.28:1(深)**,本就合格,保持主文本色。
|
|
159
|
+
- **组头完成度摘要**:新增 `groupSummary` / `groupSummaryNoRate` 与 `groupAria` 文案,组头以纯文本给出「进行中 N / 已完成 M(完成率 x%)」,分母为 0 时输出「完成率暂无数据」而非 NaN;读屏可完整念出计划数、件数、完成率与三态明细。折叠态同样可读。
|
|
160
|
+
- **折叠保留 DOM**:折叠走 `hidden`(布尔 true,不是空字符串)+ `--folded` CSS 类隐藏,卡片节点不卸载;`aria-expanded` / `aria-controls` 指向真实存在的节点。
|
|
161
|
+
- **HTML 内容模型修正**:折叠态组头是 `<button>`,原 `<h2>` 嵌在按钮内不合法;改用 `<span role="heading" aria-level="2">`,文档大纲语义不变。
|
|
162
|
+
- **视觉精修**(只加细节,不改结构):组左栏 3px 竖向渐变强调条(位置+形状编码,非颜色唯一)、卡片内阴影与顶部高光、组头悬停反馈、图表标题色块、数字统一 `tabular-nums`;装饰性动效全部在 `prefers-reduced-motion` 下关闭。
|
|
163
|
+
|
|
164
|
+
**验证**:`npm test` **73/73 通过**(T5/T6 新增 6 例);`npm run build` 无 TS 报错;渲染探针见上。
|
|
165
|
+
|
|
166
|
+
**未验证**:真实浏览器目视、窄侧栏换行、深色模式观感 —— 需重启 `dsh web` 后人工确认。
|
|
167
|
+
|
|
168
|
+
## 1.4.0 — 2026-10-27
|
|
169
|
+
|
|
170
|
+
侧栏「任务进度」一眼分清「进行中 / 已完成」。(用户确认口径:严格两组;允许引入图表库。)
|
|
171
|
+
|
|
172
|
+
- **分组**:面板改成两组 —— 「进行中」(橙色,左侧橙色竖条 + 呼吸圆点)与「已完成」(蓝色)。判定只认 `phase === 'done'` 进「已完成」;`blocked / degraded / paused` 置顶并显示「需处理 N」红色徽章;`planning / executing / integrating` 次之;`pending` 最后。已取消计划继续不显示(与 1.3.7 一致)。
|
|
173
|
+
- **图表**:引入 `chart.js@4.6.1`,以 `vendor/chart.umd.js` 随包分发(MIT,未走打包器——本插件客户端是手写的 `window.__ModuleLoader__` 模块工厂,无 bundler)。新增「两类任务占比」环形图(渐变)与「进行中计划完成度」横向条形图,均带数值与无障碍标签。
|
|
174
|
+
- **折叠**(对齐设计稿 `docs/status-grouping-ia.md` §6.4 #1/#6):存在进行中任务时,「已完成」组默认折叠(组头可点、`aria-expanded` 同步、chevron 形状编码),避免历史任务抢走视觉权重;无进行中任务时默认展开。折叠态只在本组件内存里,不写偏好,刷新即回默认。
|
|
175
|
+
- **降级**:图表脚本未加载或宿主未重启时,首帧即渲染纯 CSS 双色占比条+图例,不出现空画布;`prefers-reduced-motion` 下关闭动效与脉冲。
|
|
176
|
+
- **宿主**:新增只读路由 `/dsh-workplan/vendor/chart.umd.js`(`text/javascript`,1 小时缓存);其余路由、工具、数据结构一字未动。
|
|
177
|
+
- **兼容修复**:`GroupCharts` 原用 `useRef`,而宿主注入的 React 兼容层未提供该 hook,`npm test` 实测抛 `TypeError: useRef is not a function`(3 个渲染用例失败)。改为「状态即 ref」(`useState` + 回调 ref,节点变化才更新),不再依赖 `useRef`;改后 `npm test` **67/67 通过**。
|
|
178
|
+
- **未验证**:本版本只完成编译与产物校验(`client.js` 含分组/图表/降级标记,`lib/host.js` 版本为 `1.4.0`)。**真实界面渲染需重启 `dsh web` 并刷新页面后才能看到**,尚未做视觉走查与浏览器截图验证。
|
|
179
|
+
|
|
180
|
+
## 1.3.7 — 2026-10-26
|
|
181
|
+
|
|
182
|
+
真机重启后当场复现,1.3.6 只修了一半。
|
|
183
|
+
|
|
184
|
+
- **复现**:重启后派发旧计划 `wp-8bc5c900`,返回 `Error: planId 与当前计划不匹配`。
|
|
185
|
+
- **根因**:1.3.6 只在「内存里没有记录」时重建。但重启后下一个回合的规划器会立刻生成新计划,内存马上就有一条别的计划记录,于是 `if (!record?.plan)` 为假,重建根本没跑,直接落到 planId 比对失败。重启窗口里内存为空的状态只存在几秒,实际用户永远撞不到那一半。
|
|
186
|
+
- **修复**:`dispatchTool` 现在在「内存无记录」**或**「调用方点名了另一份计划」时都重建(重建期间有派发在跑则不动),按 planId 从会话存档精确取回。点名一份存档里存在的计划,任何时候都应可派发。
|
|
187
|
+
- **验证**:新增用例「内存里是另一份计划时,按 planId 派发旧计划照样重建并继续跑」;`node --test` **67/67 通过**。用真实会话存档(2677 条事件)重放:不传 planId → 最新计划 `wp-0fd49b2d`、`pending`;传 `wp-8bc5c900` → `paused`、9 完成 / 2 未完成(T10、T11);传不存在的 planId → `null`。
|
|
188
|
+
|
|
189
|
+
## 1.3.6 — 2026-10-26
|
|
190
|
+
|
|
191
|
+
真机重启后暴露的新缺陷:**重启前暂停的旧计划会变得不可派发**。
|
|
192
|
+
|
|
193
|
+
- 现象(代码推定 + 单测复现):`reconstructFromSession` 只回放“最新那条计划消息”。重启后下一个回合自动生成了新计划,此时再派发重启前那份暂停计划,重建得到的是新计划,`dispatchTool` 随即抛 `PLAN_ID_MISMATCH`。磁盘上还有未完成的工作,却没有任何入口能继续它——又是一种“卡死”。
|
|
194
|
+
- 修复:`reconstructFromSession(session, targetPlanId?)` 现在为每条计划保留一份快照(状态、`resultPhase`、`resumeCount`、取消标记),派发时按 `planId` 精确取回;不传 `planId` 时行为不变(仍取最新计划,仪表盘语义不变)。
|
|
195
|
+
- 测试:新增「重启后仍能派发重启前暂停的旧计划」用例(两条计划在会话里、记忆为空、派发旧计划并跑完待续子任务)。`node --test` **66/66 通过**。
|
|
196
|
+
|
|
197
|
+
## 1.3.5 — 2026-10-26
|
|
198
|
+
|
|
199
|
+
真实进程(PID 7752,1.3.4)11 子任务派发:**完成 9、受阻 0、待续 2**,`phase: "paused"`、`resumeAvailable: true`、`errors` 只有一条预算说明——暂停语义与载荷字段在 1.3.4 真实进程再次成立。同一轮子任务查出两个真问题:
|
|
200
|
+
|
|
201
|
+
- **调用点变异存活(T4 实测)**:把 `buildRoundResult(record, …)` 改成 `buildRoundResult({ ...record, phase: undefined }, …)`,65/65 仍然全绿——所有用例都直接调函数本体,没有一条断言「session 里真正追加的那条结果消息」。现补端到端断言:重启重建用例派发后从会话取出 `workplan-result` 消息,断言 `phase === 'integrating'` 且键存在。同一变异现在会让用例变红(实测 64 pass / 1 fail)。
|
|
202
|
+
- **修复点 1 尚未在真实进程被触发(T7 实测)**:本轮派发对象是重启**之后**新建的计划(20:54:05 建、进程 20:48:23 启动),内存本来就热,`if (!record?.plan) reconstructFromSession(...)` 分支根本没进入。时间线证明「没报 NO_PLAN」≠「重建生效」。该修复目前仍只有代码 + 单测证据,需「先建计划、后重启、再派发」才能验证;验证动作安排在下一轮。
|
|
203
|
+
- **修复点 2 已取得运行时证据(T8)**:用真实事件流 + 进程实际加载的 `lib/runtime.js` 重放 20:54:18 派发边界 → 新计划 `phase: pending`、`resumeCount: 0`;把同一事件流喂给「删掉重置三行」的对照产物 → 立刻退回 `phase: paused`、`resumeAvailable: true`。对照组精确复现旧症状,说明这三行是承重的。
|
|
204
|
+
- **文档事实核对(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…`)。此处仅记录事实,不改写历史快照。
|
|
205
|
+
|
|
206
|
+
测试:65 例全部通过,其中「重启重建」一例新增端到端载荷断言。
|
|
207
|
+
|
|
208
|
+
## 1.3.4 — 2026-10-26
|
|
209
|
+
|
|
210
|
+
真实进程(PID 96748,1.3.3)的 11 子任务派发结果证明 1.3.3 的三处修正全部生效:`phase: "paused"` 进了载荷、`resumeAvailable: true`、`errors` 只剩一条预算说明、**受阻 0 / 完成 9 / 待续 2**。同一轮的子任务又查出三个新缺陷,本版修掉:
|
|
211
|
+
|
|
212
|
+
- **暂停后重启 `dsh web`,计划再也派发不了**:`dispatchTool` 只读内存记录,重启后内存为空就直接报 `NO_PLAN`「当前会话没有可派发的计划」——而卡片还在界面上。用户照我们的话重启一次,就把可续跑的计划变成了永久不可续跑。现在派发前会 `reconstructFromSession` 从会话存档重建记录并登记回内存。
|
|
213
|
+
- **上一份计划的暂停会污染下一份计划**:`reconstructFromSession` 的 `resultPhase` 在新计划消息到达时没有清零,于是同一会话里新建的计划在首次派发前就显示「预算用完」。现在新计划到达时重置 `resultPhase`/`resultSeen`/`resumeCount`。
|
|
214
|
+
- **结果写入处的接线没有测试**:1.3.3 的变异测试(B2:把 `phase: record.phase` 改成 `undefined`)存活,62 个测试全绿——没有任何用例覆盖调用点。现在结果消息构造抽成导出的 `buildRoundResult(record, sessionId, limits)`,并新增用例;同一变异现在会让测试变红(已实测)。
|
|
215
|
+
|
|
216
|
+
测试:新增 3 例(结果载荷必须携带阶段、新计划不继承暂停、重启后派发自动重建计划),全量 **65/65 通过**。
|
|
217
|
+
|
|
218
|
+
## 1.3.3 — 2026-10-26
|
|
219
|
+
|
|
220
|
+
真实进程(PID 29790,1.3.2)端到端派发已证明「预算到点 = 暂停而不是失败」:8 个子任务完成 4 个、**受阻 0 个**、其余 4 个保持 pending,面板显示「本轮预算用尽,可续跑」,没有再出现级联受阻。本轮派发又暴露三处收尾缺陷:
|
|
221
|
+
|
|
222
|
+
- **暂停计划被说成“不可续跑”**:`canResumePlan` 只认 `blocked`,而暂停留下的是 `pending`,于是载荷里 `resumeAvailable: false`,卡片提示「自动续跑额度已用尽,需要主 Agent 直接接手」——与事实相反(再派发一次就能继续)。现在 `paused` 也算可续跑,且暂停不消耗续跑额度(它是时间切片,不是返工)。
|
|
223
|
+
- **结果消息不带阶段,刷新后暂停会被回放成“正在做”**:结果载荷缺 `phase` 字段,`reconstructFromSession` 只能猜 `executing`。现在结果消息写入本轮 `phase`,刷新后原样回放 `paused`。
|
|
224
|
+
- **暂停仍把被杀子任务的「总执行超时」写进失败清单**:面板虽然显示正常,但载荷 `errors` 里留着 `["T4: 总执行超时", "总执行超时", …]`,模型会读成“有失败”。现在预算分支会剔除这些超时行,只保留「本轮执行预算(N 分钟)已到……」这一条说明。
|
|
225
|
+
|
|
226
|
+
测试:新增 3 例(暂停后可续跑且不扣额度、结果消息往返携带阶段、失败清单不含超时),全量 **62/62 通过**。
|
|
227
|
+
|
|
228
|
+
## 1.3.2 — 2026-10-26
|
|
229
|
+
|
|
230
|
+
真实进程(PID 24118,1.3.1)端到端派发暴露的剩余缺陷,全部来自“计划总预算”这条路径:
|
|
231
|
+
|
|
232
|
+
- **20 分钟总预算把正常长任务判成失败**:T1 真实跑了 840 秒才完成(1.2.1 的 300 秒上限下必死),T2 137 秒,随后总预算到点,正在跑的 T3 被记为「总执行超时」,T4–T9 级联受阻,面板再次显示「0~2/N 做好了、N 卡住了」。
|
|
233
|
+
- 修复:预算到点不再算失败——未完成子任务保持 `pending`(已完成的不重跑),计划阶段标记为新 `paused`,错误文案写明「本轮执行预算(N 分钟)已到:已完成 X/Y,其余 Z 项保留为未完成,再次派发即可续跑」。
|
|
234
|
+
- 面板与工具输出支持 `paused`:中文「预算用完,可续跑」(warn 色,不显示为「卡住了」),刷新后仍保持该阶段(按结果消息回放)。
|
|
235
|
+
- 自动续跑额度 2 → 4:一次派发最多 20 分钟,慢计划分多轮续跑,避免一整条计划被 20 分钟卡死。
|
|
236
|
+
- 规划器约束:子任务不得写「调用 dsh_workplan_dispatch / 重启或停止 dsh web / 安装升级插件」这类只有主 Agent 能执行的动作(本轮 T3「执行真实派发」对子 Agent 根本不可执行,必然失败);单个子任务应在 10 分钟内完成。
|
|
237
|
+
- 子 Agent 提示:明确“没有派发权限、不能重启服务”,遇这类步骤直接说明需要主 Agent 执行,禁止空等。
|
|
238
|
+
|
|
239
|
+
## 1.3.1 — 2026-10-26
|
|
240
|
+
|
|
241
|
+
让「卡住了」变成一个看得懂、走得通的提示,而不是死路。
|
|
242
|
+
|
|
243
|
+
- 仪表盘在受阻计划上直接给出续跑指引:`对主 Agent 说「续跑这个计划」,会从受阻处继续(已完成的不重跑)`,并显示剩余额度(如「还可续跑 2 次」)。
|
|
244
|
+
- 续跑额度用尽时,文案改为 `自动续跑额度已用尽,需要主 Agent 直接接手完成`,不再只显示「卡住了」。
|
|
245
|
+
- **修复假取消**:超过 10 分钟未派发的计划原先会被重新标记为 `cancelled`,而 `cancelled` 永远不可续跑——一张没人动过的计划会自己「死掉」。现在保持 `pending`,只额外给出 `stale: true` 标记,提示而不是关死。
|
|
246
|
+
- `reconstructFromSession` 的载荷补上 `resumeLimit`,与实时计划保持一致。
|
|
247
|
+
- 新增 `tests/client-resume-hint.test.ts`(3 个用例):有额度、额度用尽、pending 不被误显示为已取消。
|
|
248
|
+
|
|
249
|
+
## 1.3.0 — 2026-10-26
|
|
250
|
+
|
|
251
|
+
卡住(stuck)问题的系统性修复。所有改动都对应 1.2.1 及更早版本中可复现的“计划卡住”路径,回归测试见 `tests/workplan-stuck-recovery.test.ts`。
|
|
252
|
+
|
|
253
|
+
### 根因与修复
|
|
254
|
+
|
|
255
|
+
1. **固定 300 秒墙钟杀死了健康的子任务**(历史证据:22 次 `子任务 T? 超时(300000ms)`)。
|
|
256
|
+
- 现在把“绝对上限”和“无进展窗口”分开:默认绝对上限 900 秒(可配置 1s–30min),无进展窗口默认 300 秒(可配置 1s–15min)。
|
|
257
|
+
- 只要子 Agent 的会话日志还在增长,就不算卡住;只有日志停止增长才判定 wedged。
|
|
258
|
+
- 新增 `src/watchdog.ts`(`pickTimeoutReason` / `createProgressWatchdog`),轮询间隔自动按窗口长度缩放。
|
|
259
|
+
- 无进展导致的失败会自动重试一次(换一个新的子 Agent),达到绝对上限则不重试,避免烧掉整份计划预算。
|
|
260
|
+
|
|
261
|
+
2. **重新派发受阻计划是“假成功”**(历史证据:约 12 次 dispatch 完成 0 个子任务)。
|
|
262
|
+
- 以前所有子任务都是 `blocked`,`readySubtasks()` 返回空,派发器直接 `ok: true`,模型以为成功,计划再也动不了。
|
|
263
|
+
- 现在 `canResumePlan()` / `resetBlockedStatuses()` 支持从受阻处续跑:只重置受阻/进行中的子任务,已完成的不重跑,每份计划最多续跑 2 次(可配置 0–5)。
|
|
264
|
+
- 续跑额度用尽时 `dsh_workplan_dispatch` 返回 `ok: false` + 明确原因 + `hint`,让主 Agent 直接接手,而不是重复派发。
|
|
265
|
+
- 取消(cancelled)的计划永不被自动续跑。
|
|
266
|
+
|
|
267
|
+
3. **执行中不能取消**(`计划正在执行,不能取消`)。
|
|
268
|
+
- 现在取消会中止正在运行的派发:`abortedRunning: true`,计划进入 `cancelled`,在途结果仍会写成 durable 消息。
|
|
269
|
+
|
|
270
|
+
4. **`dispatchPromise` 永挂后永远回答 already running**。
|
|
271
|
+
- 超过“计划预算 + 120 秒”仍未返回时,派发工具返回 `ok: false`、`stale: true`,并提示主 Agent 直接接手,不再无限 wait-for-success。
|
|
272
|
+
|
|
273
|
+
5. **`[object Object]` 取代真实错误**(历史证据:1 次)。
|
|
274
|
+
- 新增 `describeError()`,把 Error / 字符串 / 嵌套对象信封 / Envelope 文本统一渲染成可读文字;`record.errors` 与子任务 `error` 全部走它。
|
|
275
|
+
|
|
276
|
+
6. **缺少元数据直接炸掉子 Agent 提示词**。
|
|
277
|
+
- `buildChildPrompt()` 对 `taskUnderstanding` / `contradictions` / `method` 等字段做了防御性读取;以前重建或旧版本计划缺字段会让每个子任务瞬间失败、整份计划被判受阻。
|
|
278
|
+
|
|
279
|
+
7. **构建产物漏拷 `watchdog.js`**:`npm run build` 之后 `lib/` 缺少新模块会直接导致插件加载失败,构建脚本已修正。
|
|
280
|
+
|
|
281
|
+
8. **续跑后不再写 durable 结果消息**:续跑会清除 `resultWritten`,保证刷新页面后看到的是最新结果,而不是旧的受阻快照。
|
|
282
|
+
|
|
283
|
+
### 可观测性
|
|
284
|
+
|
|
285
|
+
- 结果消息新增 `resumeCount` / `resumeAvailable`,并直接告诉主 Agent「可再次 dispatch 续跑(剩余 N 次)」或「额度已用尽,请直接接手」。
|
|
286
|
+
- 仪表盘 payload 新增 `heartbeatAt`、`resumeCount`、`resumeLimit`、`resumeAvailable`、`dispatchElapsedMs`。
|
|
287
|
+
- 派发工具的渲染文本会带出 `仍在执行(已 Ns)` 与失败时的「下一步」提示。
|
|
288
|
+
|
|
289
|
+
### 可配置项
|
|
290
|
+
|
|
291
|
+
| 配置键 | 环境变量 | 默认 | 范围 |
|
|
292
|
+
| --- | --- | --- | --- |
|
|
293
|
+
| `childTimeoutMs` | `DSH_WORKPLAN_CHILD_TIMEOUT_MS` | 900000 | 1000–1800000 |
|
|
294
|
+
| `idleTimeoutMs` | `DSH_WORKPLAN_IDLE_TIMEOUT_MS` | 300000 | 1000–900000 |
|
|
295
|
+
| `totalTimeoutMs` | `DSH_WORKPLAN_TOTAL_TIMEOUT_MS` | 1200000 | ≥ 60000 |
|
|
296
|
+
| `maxResumes` | `DSH_WORKPLAN_MAX_RESUMES` | 2 | 0–5 |
|
|
297
|
+
|
|
298
|
+
### 测试
|
|
299
|
+
|
|
300
|
+
- 新增 `tests/workplan-stuck-recovery.test.ts`(11 个用例):续跑判定、状态重置、结果文案、超时分类、watchdog 增长/停滞、错误渲染、端到端「停滞→受阻→续跑→完成」、额度耗尽、执行中取消、陈旧派发。
|
|
301
|
+
- 全量 `npm test`:51 个用例全部通过。
|
|
302
|
+
|
|
303
|
+
## 1.2.1 — 2026-10-26
|
|
304
|
+
|
|
305
|
+
- “做好了/已完成” now uses sky blue (`#0ea5e9`); “正在做” uses orange (`#f97316`) across the summary strip, donut, legend, task bars, mini bars, cards, pills and tool-turn accents.
|
|
306
|
+
- Other status colors (blocked/pending) are unchanged; no layout, interaction or dependency changes.
|
|
307
|
+
- Added a static regression test for the two status color mappings.
|
|
308
|
+
|
|
309
|
+
## 1.2.0 — 2026-10-26
|
|
310
|
+
|
|
311
|
+
- Task progress rows in “每个任务的进度” are now buttons: one click expands the matching task card and scrolls it into view.
|
|
312
|
+
- Task cards expose a stable DOM anchor; scrolling uses `scroll-margin-top` so the fixed header does not cover the title.
|
|
313
|
+
- Keyboard users can Tab to a task row and press Enter/Space; focus moves to the expanded card.
|
|
314
|
+
- No new dependency; theme tokens and responsive layout are unchanged.
|
|
315
|
+
|
|
316
|
+
## 1.1.1 — 2026-10-26
|
|
317
|
+
|
|
318
|
+
- 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.
|
|
319
|
+
- 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.
|
|
320
|
+
- A plan produced in plan mode is preserved across turns instead of being cancelled before the user can approve it.
|
|
321
|
+
|
|
322
|
+
# Changelog
|
|
323
|
+
|
|
324
|
+
## 1.1.0 — 2026-10-26
|
|
325
|
+
|
|
326
|
+
- Dashboard now shows a fifth `任务` cell in the circled summary strip, so the plan count is visible next to work-item and status counts.
|
|
327
|
+
- Added a status donut and per-task progress bars (SVG/CSS, no new dependency) for a more visual, less text-heavy overview.
|
|
328
|
+
- Added a compact progress bar to each collapsed task card, so progress is visible without expanding.
|
|
329
|
+
- Mobile: five-cell strip becomes a readable 2-column grid, with the task cell spanning the full width; the charts stack in one column.
|
|
330
|
+
- Version metadata is now `1.1.0` in the plugin manifest and host `meta`.
|
|
331
|
+
|
|
332
|
+
## 1.0.0
|
|
333
|
+
|
|
334
|
+
- 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,165 @@
|
|
|
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.5.0 changes (progress you can see)
|
|
106
|
+
|
|
107
|
+
1.5.0 is presentation-only: the collapsed plan card swaps its single grey line ("本计划 0/8 做好了" plus a 54×5px bar) for an actual chart.
|
|
108
|
+
|
|
109
|
+
| Where | Before | After |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| collapsed plan card | 12px grey text + mini bar + "0 卡住了"; the percentage existed only in the hover `title` | 44px ring + 24px percentage + `本计划 X/Y 做好了` + one "what is happening now" line (blocked first, with the task name) + 8px four-segment composition bar + status legend + stage stepper |
|
|
112
|
+
| expanded panel | one plan-level progress bar | "Step progress": one row per stage (name, `done/total`, thin bar, state) plus a weighted overall figure |
|
|
113
|
+
| a 0% plan | the bar fill was 0px wide and its track used the same token as the card background — effectively no chart | four signals: a thickened ring track, a 24px "0%", a full grey "not started" bar, and dashed stage slots with the current stage outlined |
|
|
114
|
+
| a group with 0 work items | `未完成 0 / 已完成 0(完成率暂无数据)` | that sentence is gone; only the `—` in the ring remains (the in-group empty state still renders when expanded) |
|
|
115
|
+
|
|
116
|
+
None of the counts disappeared — the legend always carries all four buckets with their numbers.
|
|
117
|
+
|
|
118
|
+
**How to see it**: the front end ships in `client.js`, which DSH Web loads at start-up, so restart DSH Web and then reload <http://127.0.0.1:3080>. For an offline comparison open [docs/preview/progress-visual-1.5.0.html](docs/preview/progress-visual-1.5.0.html) (`npm run preview` regenerates it with the same CSS for both versions).
|
|
119
|
+
|
|
120
|
+
No new dependencies, `plan.ts` / `runtime.ts` untouched; rollback is documented in [CHANGELOG.md](CHANGELOG.md).
|
|
121
|
+
|
|
122
|
+
## What 1.4.3 fixes
|
|
123
|
+
|
|
124
|
+
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.
|
|
125
|
+
|
|
126
|
+
| Defect | Symptom | Cause |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| D1 | numbers frozen for minutes | stats recomputed only after a whole wave of subagents finished |
|
|
129
|
+
| D2 | two numbers in one payload disagree | counts came from the cache, department totals from live state |
|
|
130
|
+
| D3 | "not started" item showing 100% with a stale owner | budget reset bypassed the state machine |
|
|
131
|
+
| D4 | the same failure reported at 100% or 0% | two code paths disagreed on blocked progress |
|
|
132
|
+
|
|
133
|
+
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).
|
|
134
|
+
|
|
135
|
+
## FAQ
|
|
136
|
+
|
|
137
|
+
**Nothing happens after installing.** Restart DSH Web; confirm `dsh-workplan` is in `~/.dsh/profiles/web/package.json`.
|
|
138
|
+
|
|
139
|
+
**The panel is empty.** It shows plans for the current session only.
|
|
140
|
+
|
|
141
|
+
**Numbers are lower than before.** That is the fix: 1.4.3 derives them from real state, and the old numbers were frequently inflated.
|
|
142
|
+
|
|
143
|
+
**A subagent stopped moving.** Five minutes without new events marks it blocked, in isolation. Tell the main agent to resume the plan.
|
|
144
|
+
|
|
145
|
+
## Uninstall
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
dsh plugin --profile web remove dsh-workplan
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Development
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npm ci
|
|
155
|
+
npm run check # tsc --noEmit
|
|
156
|
+
npm test # 99 tests, including count invariants, mutation tests and the progress chart
|
|
157
|
+
npm run build # src/*.ts → lib/*.js, client.js
|
|
158
|
+
npm run preview # regenerate the 1.4.3 / 1.5.0 card comparison under docs/preview
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`lib/` and `client.js` are committed build output; CI rebuilds them and fails if they drift.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT — see [LICENSE](LICENSE). Chart.js is MIT as well.
|