dsh-taskboard-kit
v0.9.2
Published
dsh (DeepSeek Harness) taskboard kit: one local task board per workspace. Agents create/claim/progress tasks with model tools; humans watch and drive the same board in a kanban tab — a single JSON file in the workspace is the only source of truth, no serv
Maintainers
Readme
dsh-taskboard-kit
给每个 dsh workspace 一块本地任务看板:Agent 用 model tools 创建、认领、推进任务,人类在 dsh web 的「看板」页签里看同一块板、也能直接操作。看板的唯一真实来源是 workspace 里的一个 JSON 文件——没有服务端、没有账号体系、不需要部署任何东西。
兼容性
- dsh ≥ 0.1.7 — 自 v0.5.3 起完整支持:会话 cwd 改吃
sessionId插槽 prop(0.1.7 从会话列表 state 里移除了current);看板入口适配 0.1.7 的紧凑居中 dock 布局;注入的看板通知改用 0.1.7 持久化层要求的 v4 生产者 source kind(plugin:taskboard-kit)。旧版 dsh 通过兜底逻辑继续可用。
包含什么
- 8 个 model tools,Agent 在该 workspace 的任何会话里都能调用:
taskboard_inbox— 会话开始第一步:现在压在你身上的事,按急迫度排序,每条都带该敲的命令taskboard_list— 列出任务(按状态 / 列 / 负责人 / 在等谁过滤)taskboard_create— 新建任务,可选直接指派给谁taskboard_claim— 从待认领池原子认领(并发下恰好一人成功;在等谁的卡不可认领)taskboard_update— 开始 / 暂停 / 提交审核(指定审核人)/ 通过 / 打回 / 完成 / 关闭 / 重开 / block / unblock、改派、改字段(含价值度)、附注taskboard_comment— 给任务留言(实现发现 / 交接说明 / 测试反馈),不改任务状态taskboard_get— 任务全文 + 事件时间线 + 留言串 + 列龄 / SLA / 审核人 / 在等谁taskboard_roster— 名册:谁真的在场(最后一次动手是什么时候、别名)
- 「看板」会话页签,三个可切换的视角(共享同一份数据):
- 按进度:六条泳道(待认领 · 已指派 · 进行中 · 待审核 · 待收口 · 已结清),卡片带优先级 / 价值度 / 负责人 / 当前列停留时长 / 陈旧点 / 审核人 / 在等谁;顶部一条 「◷ N 张卡在等你决定」 strip(完整问题原文 + 一键「回复并解除等待」);详情抽屉里有事件流水、留言与协作事实,一键认领 / 开始 / 提交 / 通过 / 打回 / 收口 / 改派。
- 按负责人:一个负责人一条泳道(别名自动归并),一眼看清每个人头上挂了什么。
- 统计:宏观读数——KPI(未结清 / 待收口 / 进行中 / 卡住 / 中位周期 / 收口延迟 / 打回率)、 每日新建 vs 结清(叠未结清累计折线)、状态与优先级分布、各负责人负载与周期、各列停留时长。
- 「开启看板」向导:尚未开启看板的工作区给一键开启——建
.dsh/taskboard.json并写入本工作区的.dsh/BOARD-PROTOCOL.md(规范)。两者都已存在时都不会被覆盖。 - 抽屉里的 markdown(v0.7.0):详情与评论按 markdown 渲染——标题、列表(按缩进嵌套,上限两级)、GFM 表格、代码块、引用、链接、图片(
,只放行 http/https)与只读任务复选框(- [ ]/- [x])。渲染管线先转义、只注入自造标签,任何 Agent 写的卡都只是数据;渲染实拍见docs/images/markdown-fidelity.png。 - 多 Agent 协作规范:完整规范见
docs/COLLABORATION.md;它的精简版在会话开始写入系统提示词,CLI--help与面板「? 指南」里的片段同源。 - 会话开始感知:Agent 收到的是自己那一份行动清单(不是我欠审核、谁在等我、哪张卡被打回、哪张派出去没人接),不是一句干巴巴的计数。
- 看板变化推送:对每个 live 会话的板文件做 fs.watch——任务被指派给你、你的任务有了审核结论、有人把审核 hand off 给你、有人开始等你、有卡开始等人类、你的任务有新留言时自动通知(只注入上下文,绝不唤醒);5 秒风暴窗口内的连续变化合并成一条通知。
- 时钟自检(v0.5.4):文件 watcher 永远看不到「三天没人碰这张卡」——所以另有一条按时的自检:只看压在你身上的事(欠审核、有人等你、被打回没动、自己的卡陈旧、派出去的活成了孤儿、等人类超时),每 5 分钟一轮、每 workspace 30 分钟最多催一次,并附上该敲的命令。
- 等人类 = 真的叫得动人类(v0.5.4):卡片
block --on human时进面板「等你」清单;配了TASKBOARD_NOTIFY_CMD就外发通知(msg9 / 桌面通知 / webhook 随你接);等超 24h 自动升级催办。
板文件
每个 workspace 一块:<workspace>/.dsh/taskboard.json(形状见英文版 README,字段自解释)。
文件跟着 workspace 走,所以在同一目录干活的每个 harness、每个人都能读写同一块板——提交进 git 或 gitignore 都可以。所有变更经过锁文件(过期锁自动回收)+ tmp+rename 原子写,并发 Agent 和面板同时操作不会写花文件;claim 在锁内裁决:N 个并发认领恰好一个成功,其余收到冲突。
升级到 0.6.0(用过旧版看板的话,这一段请读)
done 不再是终点,closed 才是。 0.6.0 之前流程停在 done、closed 表示「废弃」,
于是「干完了但没人收口」这件事在规范里根本不存在:卡永远躺在 done,没有任何规则说
"有人欠这一步"。
现在 done 的含义是干完且审核通过、但仍欠一次收口:卡还在看板上、还计入未结清,
直到有人 close 它。由此带来的变化:
- 面板顶部新增「待收口」条,列出所有 done 未结清的卡,一键收口;
- done 超过 72h 会被判为陈旧(以前永不算陈旧);
taskboard_inbox会点名卡主「你的卡已 done 但没收口」,并给出该敲的命令;- 统计里的中位周期改成量
created → done(活),收口延迟单独量done → closed(流程)。 拆成两个数是因为合成一个时,只要收口一拖,快板子也会显示成慢板子。
「这事不做了」仍然走 close——在 note 里写清原因;没有单独的 abandoned 状态。
旧板文件无感加载(cancelled → closed,缺的 value / comments / actors 在下次写入时补齐)。
如果你现有的板子里积了一批 done 卡,它们现在会显示为「待收口」的工作——这是预期读数,不是 bug。
状态模型
| 列 | 规则 |
|---|---|
| 待认领 | open 且无负责人且未在等谁——任何人可 claim |
| 已指派 | open 且有负责人——已委派、未开始 |
| 进行中 | 已认领或已开始 |
| 待审核 | 已提交(submit),等 reviewer 通过 / 打回 |
| 待收口 | 审核通过(或直接 done)——不是终态,还欠一次收口 |
| 已结清 | 已收口——close(面板默认折叠,toggle 可显示) |
两条与状态正交的协作轴(v0.5.4,都不新增状态):
| 字段 | 含义 | 谁写 |
|---|---|---|
| reviewer | 谁欠这次审核。reviewer 本人 / 卡主 / 人类才能 approve|reject;不能审自己的活 | submit(默认解析:卡主 → 最近活跃的其他 Agent → 人类) |
| waiting_on | 这张卡在等谁(human/agent/external + who + question + since)。等谁的卡不能被认领 | block / unblock |
板级还有 actors 名册(谁出现过、最后一次动手、别名 dsh ≡ dsh-agent)——用来回答「这活派给一个已经不在场的 Agent 了吗」。
只有 closed 是终态:done 的含义是「干完且审核通过、但还没结清」,卡仍留在看板上、仍算未结清,
需要卡主 / PO / 人类收口 close。分两步是因为审核通过 ≠ 事情了结(可能还要部署、等上游、补文档)。
「这事不做了」也走 close,但必须在 note 里写清原因——没有单独的 abandoned 状态,
「做完了」与「放弃了」的差别只存在于留言里。面板顶部有「待收口」条,一键收口,防止干完了没人收口。
流转图:open → in_progress → review → done → closed;done | closed → open(reopen)。按动作说:open → in_progress(claim / start)、in_progress → open(stop)、in_progress → review(submit)、review → done(approve)、review → in_progress(reject)、open|in_progress|review → done、open|in_progress|review|done → closed(close,旧名 cancel 是它的别名)、done|closed → open(reopen)。状态名刻意对齐 msg9 任务模型,未来接服务端看板时语义不变。旧版本写出的板文件无感加载:cancelled 状态/日志事件归一为 closed,缺的 value / comments 字段自动补齐。
每个任务还有价值度(value points,斐波那契刻度 ½ / 1 / 2 / 3 / 5 / 8,null = 未评估),用来回答"这张卡值多少"——创建或更新时设置,CLI 里 --value 1/2 表示 ½、--value none 清除。
安装
dsh plugin --profile web add [email protected]
# 重启 dsh web,打开任意会话,「看板」页签就在务必写死版本号。 dsh plugin 转发给 pnpm 11,而 pnpm 11 默认带一道供应链
minimumReleaseAge 闸门:刚发布几分钟的版本会被拦下,此时不带版本的
add dsh-taskboard-kit 会静默装上一个旧版本 —— 你拿到的看板里 done 仍被当成终点,
而且没有任何提示。写上 @0.7.4 就是让这次安装绕过闸门、装到你指定的版本。
等某个版本发布满一天后,不带版本也能装到它。
v0.9.2 —— 修正:看板列宽刚性 1310px(窄屏裁列 / 宽屏留白)
- 修正(T-85):
styles.column是width: 240 + flexShrink: 0⇒ 一整排刚性 1310px ✗ —— 面板比它窄就裁掉右侧的列(1280 裁 317px、1440 裁 157px),比它宽就右侧留白(1680 留白 83px、2560 留白 963px)✗。 现在column改为flex: 1 1 240px; min-width: 168px✓(宽屏等分铺满 ✓、窄屏收缩到 168 ✓);宽度规格收进TB_LANE常量作为唯一来源 ✓;overflowX:auto保留但降级为兜底 ✓;没有用overflow:hidden糊 ✗。 这是既有缺陷,不是 v0.9.x 的回归 ✓(v0.9.0 与 v0.9.1 在五个宽度上逐值相同 ✓,lanes/column源码一字未改 ✓)。 - 工具:新增第 18 个测试套件
tests/layout-invariants.test.mjs✓ —— 真无头 Chrome 挂构建产物,四个宽度断言: 整页不横溢 · 泳道行不被裁 · 末列右边界 ≤ 视口 · 行必须铺满 · 列宽 ≥ 下限 · 面板内无越界元素 ✓。 关键:文档级scrollWidth === clientWidth改前也是 true ✗(溢出被关在泳道里)⇒ 只断言"整页不溢出"拦不住这个 bug ✗ ⇒ 必须断言"行内不被裁 + 必须铺满" ✓。 红绿对照:指向 v0.9.0/v0.9.1 的client.js⇒ 真红 ✓(裁 310/150px、留白 90/970px);仓内塞回flex:0 0 240px或width:100vw⇒ 红 ✓,且先断言 saboteur 真的生效 ✓。 - 已知后果(有意接受):「按负责人」视角在只有一个负责人时那一列会铺满面板(1280 下 976px ✓)—— 这是"必须铺满"的直接结果 ✓;不要改回
flexShrink: 0✗(若要限宽应显式加max-width,代价是重新引入留白 ✗)。见docs/DESIGN.md✓。
版本口径(沿用 v0.9.1 起的规则):0.x 下修正与内部改动走 patch ✓ —— 本版全是修正与工具,故为 0.9.2 ✓。
v0.9.1 —— 320px 抽屉头部修复 · 移除「待收口」条 · 提交前守卫
版本口径(本版起执行):0.x 下修正与内部改动走 patch ✓;只有新能力或需要用户知晓的行为变更才走 minor ✓ —— 一次 minor 应当是一个「能力台阶」,不是「攒了一堆修复」。
- 修正(T-75):320px 视口下抽屉头部标题被压成竖排、头部高达 953.95px ✗ ⇒ 117.98px(标题 175 → 15px ✓);
560/900 与改前完全一致(60.89 / 40.59px)⇒ 宽档零回归 ✓。
改法为实测选定:
flex-wrap: wrap+ 标题flex-basis: 160px(两条必须一起上 —— 单独flex-basis: 0时标题 hypothetical size 也是 0,换行算法永远认为放得下 ✗;flex-basis: 100%会把 560/900 改到 102.39px ✗,已用数据否决 ✓)。 - 变更(T-77):移除顶部「待收口」条 ✗(实测 1 张 done 时 147.59 / 100.80 / 75.80px、4 张时 332 / 235 / 210px ⇒ 现为 0,看板内容上移 −154 / −107 / −82px ✓)。
那句规则说明移入
done列头的title✓;卡面权威口径与两条收口路径都保留 ✓(抽屉里那枚「收口结清」与列里那枚按钮是同一个action: close)⇒ 代价只是收口从 1 次点击变成 2 次 ✓。 - 工具(T-79 / T-82):新增提交前守卫 ——
npm run check:commit/npm run commit:guarded: ① 源码 ↔ 产物一致(构建退出码必须为 0;构建不得产生新改动;工作区lib/= 索引lib/✓); ② 有并发写时拒绝提交(两次采样 =porcelain+ 被跟踪文件内容哈希 ✓ —— 只用porcelain拦不住"已修改态的文件被继续写" ✗); ③ 索引为空时说人话("是不是忘了git add" ✓,且在构建之前 ⇒ 注定失败的提交不动产物 ✓)。 可选仓内 git hook:npm run install-hooks/uninstall-hooks(只写 repo-localcore.hooksPath,绝不改全局 ✓;逃生门git commit --no-verify保持可用 ✓)。 - 文档:
docs/DESIGN.md两条设计系统事实 ——ACCENT是宿主的反相单色(要蓝色强调请用LINK✓);模板串里的 CSS 不受tsc检查(注释写在*/之后会吃掉整条规则 ⇒ 需骨架合法性判据 ✓)。docs/COLLABORATION.md §13.5记 hook 的三条硬纪律 ✓。
验证:npm test 351 项 / 17 套件全绿 ✓ + typecheck 干净 + action-parity 全绿 ✓;
负面对照均真红:拆掉 T-75 修复 ⇒ 头部回到 953.95px 并再次竖排 ✓;把 settle strip 塞回 ⇒ 对应断言 FAIL ✓;守卫的"红时不提交"用真 git 仓库量 git log 不增长 ✓;
加载面用页面级 sha256(不是 rev 请求 200 —— 那条口径已被证明无效 ✗)。
v0.9.0 —— 权限闸门补漏 + 卡详情与看板列表的密度重塑
本版起每条都标类别(能力 / 修正 / 文档 / 工具) —— 否则「版本数」会被当成「交付量」读。
- 修正 · 闸门侧门(T-69):
block把卡挂到一张已经在等的卡上 = 替换那段等待 ⇒ 现在走与unblock同一道归属门,并先显式记一条unblocked再记blocked;首次挂起不受约束(卡上本来没有等待 ⇒ 谁都能挂)。起因:一天内 4 次同类越权,其中一次正是"替换等待"这条侧门。 - 能力 · 卡详情抽屉密度重塑(T-74):属性 / 动作 / 指派三块重排 —— 属性 11 行 → 4 行语义行(人 / 值 / 时 / 标签;字段形如
标签 值,行内自动折行);动作 9 行 → 一行胶囊 + 一行折叠的「暂不可用」(原因一条不删);指派 → 一行当前:X(更换)+ 可展开 combobox;详情页签顺序改为 属性 → 描述 → 动作 → 指派。 真机实测(抽屉宽 560):三块合计 1010.67 → 233.92px(−776.75,−76.9%);描述区 top 1182 → 237。顺带修掉一个真 bug:可复制字段的title是死的(新增propTitle()让复制与悬浮共存)。 - 能力 · 看板顶部横幅 → 卡片凸显(T-76):删掉 156.52px 的大横幅,改为 卡片左侧竖条 + ⏳「等你决定」徽章(徽章
title= 问题原文)+ 该列内排最前,顶部只留一行 28px 汇总条(无等待卡时不渲染,点「跳过去」滚到该卡并闪现 1.2s)。 真机实测:顶部 156.52 → 28px、看板内容上移 135px、等待卡位次 6/7 → 0/7。 颜色纪律:「等你决定」= 主色蓝 + 实心矩形;「已超时」= 琥珀 + 胶囊描边 ⇒ 颜色与形状双重区分(浅/深色实测)。回复人类等待的入口从横幅搬进抽屉的等待框(能力不丢,代价是多一次点击)。 - 修正 · CHANGELOG 更正(不改写历史):v0.8.0 的发行说明里有一句宣称 —— 「every path now runs the same ownership check」 —— 被 T-69 否证(当时
block替换等待没有过门)。按"已发布的文本不改写"的口径,该句保持原样,在此更正。 - 文档 · 两条设计系统事实:
ACCENT是宿主的反相单色(不是强调蓝 ⇒ 要蓝色强调请用LINK);模板串里的 CSS 不受tsc检查(把注释写在*/之后会让解析器吃掉整条规则 ⇒ 必须有骨架合法性判据,本版已加)。
验证:npm test 333 项 / 16 套件全绿 + typecheck 干净 + action-parity 全绿(7 动作 × 5 状态 ⇒ 未移除任何动作入口);
负面对照:320px 关掉 flex-wrap ⇒ 横向溢出真红;注入隐藏徽章/竖条 ⇒ 可见性 True→False(不是"DOM 里有没有节点");源码变异 ⇒ 对应断言真红;
加载面用页面级 sha256(不是 rev+200 —— 那个方法已被证明无效)。
v0.8.0 —— 权限从「约定」变成「机制」+ reviewer 的主动通道
这一版全是行为变更(不是文案),装完刷新页面即可看到客户端部分;host 面(权限判定、通知)需重启 dsh web 才生效。
unblock/close的 actor 校验(T-61):起因为一天内 4 次同类越权 —— 有 agent 自行解挂"挂人类"的卡并替人类裁决、还自行npm publish两个版本。unblock:挂人类的卡 ⇒ agent 一律不得解挂(只有人类);挂具名 agent ⇒ 只有本人或其别名等价名、或人类;who为空 /kind:'external'⇒ 有意放宽为任何在场 agent 或人类(板外对方永远不会来敲这块板子,按字面锁死只会把卡烂在板上)。close/cancel/reopen:只有卡主 / 持卡人 / 裁决人 / 人类;被拒走conflict,拒信写清"谁被拒 / 在等谁 / 由谁挂起 / 该找谁"。- 别名折叠:名册优先 + 配置兜底(内置
dsh ≡ dsh-agent、TASKBOARD_ACTOR_ALIASES/WATCH_NAMES/SIBLING_NAMES,组间传递合并)—— 否则自己会被自己拒 ✗。 - 向后兼容:缺
created_by的老卡退回改前行为(不锁死老卡);顺序是状态先于归属(没人在等 / 非法转移仍是invalid-transition)。 - 定位写明:这是防误操作与越权,不是安全边界 ——
--by不是凭据、可被伪造。
submit归属门 + 等待释放归属 + 新机制锁死修复(T-62):submit此前任何人都能提交别人 in_progress 的卡 ⇒ 现在只允许持卡人 / 卡主 / 人类;submit/approve/reject/done/close/reopen此前会静默清waiting_on(可以让一张"等人类"的卡被done直接变"已完")⇒ 现在每条路径都过同一道归属判断,并显式补写unblocked事件; 同时修掉新机制引入的锁死:"谁被等"由排他许可改成追加许可(等 agent/external/未指名 ⇒ 卡主/持卡人/裁决人始终可解;等人类仍然只有人类能解)。- reviewer 的主动通道(T-56):
submit成功后主动通知指定 reviewer —— 打印一条可原样复制的msg9 send命令;环境里有msg9且板里名册有他的地址时才真发;地址未知 / 无msg9/ 发送失败 / 内部异常 ⇒ 一律降级为只打印,submit照常成功(提交是核心动作,通知是附属); 幂等锚点是卡上最后一条submitted事件的下标(不是时间戳);stale新增「🕵 欠审核」按 reviewer 分组(一行一个人 + 他该敲的命令 +--json.review_owed)。起因:9 张卡挂在
review列、而那位 reviewer 反馈「没有他待办的」—— 因为他 cwd 不在本工作区、看的是"指派给我"而不是"欠我审核";"被人指定为 reviewer"这件事此前没有任何东西会主动告诉他。 - 等待区间读侧兜底(T-60):区间配对此前只认
unblocked⇒ 被隐式结束的等待段闭不上、一路延伸到此刻(走势图末点说"还在等"、而当期大数字说"没在等",同屏自相矛盾);现在把submit/approve/reject/done/closed/reopened也作为区间终点。口径变更留痕:统计页「被卡住」会因历史等待被计入而变化,属于主人已批准的方向。
验证:npm test 312 项全绿(14 套件)+ typecheck 干净;新增 tests/authority.test.mjs(18 项)与 tests/authority-wait.test.mjs;
变异:摘掉"挂人类的卡 agent 不可解挂" ⇒ 红 · 摘掉别名折叠 ⇒ 红 · 摘掉 submit 归属门 ⇒ 红;
向后兼容用真实板当夹具:有 created_by 的卡挡住陌生人,抹掉后逐字回到改前。
v0.7.5 —— 把「复核里说过、但没人跟」的待办变成清单
全是客户端与 CLI 改动,刷新页面即可(CLI 侧无需重启)。
- 新增
taskboard tails(复核尾巴扫描器):扫已 closed / done 卡的裁决 note 与复核留言,抽出"看起来是待办"的句子; 每条带 卡号 + 位置(log:6/comment:3)+ 句偏移 + 原文片段 + 命中判据。处置:--file <尾巴> --card <卡号>落卡、--waive <尾巴> --reason "…"显式作废(必须给理由)、--reset撤回;落进板文件的可选新字段Board.tails?(旧板可读)。 同时接进既有路径:stale加「🔻 复核尾巴」一节、inbox给责任人提示(带上限与"还有 N 条")。 判据是被真实数据三轮收窄出来的(第一版 240 条=没人看的墙纸 → 94 条),六条收窄规则各自有单测; 另有not-done类只在小节标题层面放行("明确没做"标题下的条目也会被列出,而噪声控制由断言咬住 —— 变异二路证明)。 为什么做这个:38 张已闭卡里至少 10 条复核遗留掉了地(含"注释声称有测试、其实没有"这种假绿)—— 根因是复核结论写在 note 里、而 note 不改变列 ⇒ 没有载体、没有提醒、没有清单 ⇒ 天生静默。 - 清理 kimi 复核遗留 9 条:含补上那个"注释声称有、其实不存在"的对照测试(
tests/action-parity.test.mjs驱动 host: 7 动作 × 5 状态逐格跑updateTask,与抽屉真正渲染的结果比 —— 不比字面量比行为);统计页整页去掉brand-primary数据色 (done改用宿主 success 绿,刻意不用 link 系以免与 in-progress 撞色,两主题实测对比度 2.28/6.89 与 4.23/6.75);MILESTONE_TAG大小写口径与semverParts对齐;?指南新增卡面记号图例(由真常量派生,不手抄); 卡面 ref 补「任务编号」;T-93-hotfix/T-93.5补齐编号边界;两处半角逗号 + 一条"源码里不存在『汉字,汉字』"的可咬规则。 - 卡面渲染层补护栏:无名等待(
waiting_on.who为空)不得渲染成"可认领" —— 此前把渲染分支顺序改回旧顺序, 76 项测试仍全绿(派生层有护栏、最该护的卡面层没有);现在变异只让它一条红。 另补「裁决人 {reviewer} 仍可裁决」提示(一处holderTitle()三处生效),reviewer为空时不加也不回落到创建者(与属性表那行一致)。 - 两条语义小项:
start后若等待仍在,结果列显式提示「等待仍在({who})」(host 确实不清waiting_on); 抽屉不加stop/block(都不改列,加了会出现"按下去状态没变"的按钮)⇒ 文档给出可直敲的 CLI 路径。 - 统计页「被卡住」口径变更(主人拍板):
blockedAt改为按log里blocked→unblocked配对重建等待区间 (旧板没有该事件时回退waiting_on.since;连续两次blocked会产生重叠段、some判定不受影响) ⇒ 这个数字会上升,而这正是要的:从此能查"这张卡被卡过多久",历史等待不再隐身。
验证:npm test 256 项全绿 + typecheck 干净;新判据与清理项各带变异红/绿(T-44 13/13 · T-42 10/10 · T-50 二路 · T-40 单点);
tails 在本工作区真实板上把 94 条未收口处置到 0(落卡 47 / 作废 47,逐条带理由)。
v0.7.4 —— 键盘能用了 · 走位不迷路 · 导航区不再"不稳定"
全是客户端改动,刷新页面即可。 三件:
- 键盘导航
j/k:在当前视图里按视觉顺序(从上到下、从左到右)移动选中,抽屉开着内容也跟着换;Enter打开第一张。顺序直接由渲染用的同一份泳道派生(不是另算一套),所以"键盘顺序 === 屏幕顺序"是结构性的;收起的「已结清」竖条不进顺序(没渲染就不该落到看不见的卡上)。焦点在输入框 / 评论框 / contenteditable 里时一律走原生行为(⌘/Ctrl/Alt、输入法组字、指南/关于/成员选择器/新建表单开着时也不抢);Esc仍只有一套分层,没有第二套语义。 - 走位自动滚入视口:长板 / 窄窗口下
j/k走到视口外时把选中的卡带回可见区;用block:'nearest'且已经可见时根本不滚(否则每按一次都跳,比不滚更烦),也不会带着抽屉一起滚。 - 导航区重构(三段式 + 永远在场):这一版是回应"排列和显示很不稳定"——
- 同一行现在有三段:视图(
按进度|按负责人|统计)· 筛选(含已关闭)· 操作(信息?ⓘ· 动作↻· 主按钮)。组内 4px、组间 16px; 含已关闭永远在场(原先只在「按负责人」视角出现 ⇒ 切视角时这一行凭空多/少一个控件,这才是"不稳定"的真正来源);- 三个工具图标换成内联 SVG(原先
?是 ASCII、↻/ⓘ是符号字形,CSS 完全相同也对不齐); - "刷新"不再夹在"使用指南"和"关于"中间;
- 所有可点控件统一 28px 高 / 同圆角 / 同 hover / 键盘 focus 描边;
- 按宽度跳档而不是挤压:
≥720全标签 →520–720按路径 → 计数 → 标题顺序舍、筛选只留方框 →<520把筛选与三个工具收进⋯(视图组与主按钮永远在位;320px 时视图组也进菜单)。⋯接进既有的 Esc 分层,一次按键只关一层。 - 顺带修:删样式表时误删了一个键,英文界面 @720 下视图组会掉出 flex 被挤到第二行。
- 同一行现在有三段:视图(
验证:npm test 230 项全绿 + typecheck 干净;每处改动都有变异验证(键盘守卫 / 顺序 / 走位闸门 / 图标形态 / 组间间距 / 菜单分层 …),并用真浏览器逐条量过:三个图标 16×16、按钮 28×28、所有可点控件垂直中心差 0.00px、各档单行且不溢出、⋯ 开合与 Esc、focus 可见、超长路径截断、空板与英文最长文案。
v0.7.3 —— 「关于」浮层 · 持球行铺到每个面 · 两处清理
全是客户端改动,刷新页面即可,不用重启 dsh web。 这一版里唯一会离开你机器的,是你自己点的那几个 GitHub 链接。
- 「关于」浮层(工具栏的
ⓘ;0.7.4 起它在信息组里与?相邻,↻已归到动作侧):名称 + 版本(版本由构建注入——scripts/build.mjs用 esbuilddefine从package.json取;构建若漏了注入会回落成dev,不会印出空版本)、一句话简介,然后是真正值得读的一块:本地透明度——看板文件的绝对路径 · 卡片数 · 花名册人数 · 数据格式版本(board.version)· 插件 id,以及一句话说清「数据全在这一个 JSON 里,没有服务器、没有账号、没有云」。下面四个 GitHub 入口:仓库 · 问题反馈 · 协作规范(docs/COLLABORATION.md)· 变更记录(就是这段),每个都是target="_blank" rel="noreferrer",末尾是 MIT 与作者。Esc 只关它(它是最上层,按键不会穿透到底下的抽屉或指南)。 - 持球行成为「球在谁手上」的唯一答案,每个面都一样。 卡面、抽屉、状态栏 mini 抽屉全部用同一个派生(
currentHolder)渲染记号 + 名字,严格一行:➤待提交 ·○待认领(池子)·◷待回复 / 待回执(等人类 / 等 Agent)·⚑待裁决(待审核)·⌂待收口(已完成)。closed没有持球人。它由状态机派生而不是读assignee:review阶段负责人被冻结(球在裁决人手上)、done阶段裁决人已清空(球在卡主手上),所以「当前处理人」的字面读法在这两处都会指向错的人。mini 抽屉原先堆了三枚逐字段徽章、同一件事说两遍,现在只剩这一行。 - 标题前缀只在显示层剥离。 与卡面重复的
【owner】 T-93 ·前缀不上卡面,原文留在 tooltip 里;板文件里的字符串一个字符都不改。 - 长名字改为截断,不再溢出。
.tb-badge-outline(以及所有同类 10px 药丸)补上max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap:长 actor 名以前会溢出虚线边框、再被泳道从中间切掉,看起来像排版坏了而不是「名字太长」。 - 两处清理:0.7.2 之前就死掉的三个样式键(
cardSep/reviewerBadge/cardWaitAge,各定义 1 处、引用 0 处)删掉。
验证:npm test 214 项全绿 + typecheck 干净;版本回落、持球记号、徽章 ellipsis 规则、Esc 分层各自都有变异对照(改坏 ⇒ 对应测试必须红)。
v0.7.2 —— 卡面与统计的「一眼看懂」改造(十项)
主人 2026-10-01 连着给了四轮反馈,这一版把它们一起收了(全是客户端改动,刷新页面即可,不用重启 dsh web):
- 卡面三行版式:第 1 行=固定属性(优先级点 · 编号 · 价值点 · 状态 · 负责人/创建者 …… 列龄靠右,10px);第 2 行=标题(≤2 行,冗余前缀在显示层剥离);第 3 行=会变的(处理人 · 等待 · 审核 · 标签,严格一行)。卡高由 90–123px 收敛到统一 88px。
- 标题去噪:
【owner】 T-93 ·这类与本卡重复的前缀不再上卡面(【owner】仅当它就是本卡的负责人/创建者/裁决人;编号仅当就是本卡自己的 id;原文留在 tooltip)。编号匹配带完整 token 边界 ——T-930/T-93X/T-93-2不会被误吃(这是复核打回的真 bug)。 - 持球人派生视图:卡面只回答一句「球在谁手上(待什么动作)」——
➤ 待提交/◷ 待回复·待回执/⚑ 待裁决/⌂ 待收口/○ 待认领(closed无持球人)。它由状态机派生(review阶段负责人被冻结、done阶段裁决人已清空,所以"当前处理人"的字面读法在这三处都会指向错的人)。 - 卡面图标化:标签词换成记号(
@负责人 ·✎创建者 ·◆价值点 ·◷等待 ·⚑裁决 ·⌂收口 ·➤持球),只用文本字形不用 emoji,含义在 tooltip 里;卡面文字 −10.6%。 - 统计页 v2:时间窗 7/14/30 天可切;KPI 分「要你动手的」与「背景数字」两组并带与上一等长窗口的环比(样本不足 <3 时显式标注"样本不足",不装趋势);新增「现在该动什么」(持球人排行 + 异常清单,每条可点开对应卡)与「价值度视角」(积压 vs 交付、吞吐、每卡价值/周期);流量图补 y 轴刻度/网格/数值标签、状态改环形图;「里程碑进度」由 tag 派生(如
v1.42.0)。 - 详情抽屉 v2:粘性头部(标题 + 持球行 + 主操作常驻);属性表(值可复制);动作永远全部列出并带结果列(
关闭 → 进入「已结清」· 可 reopen 恢复),不可用的置灰并说明原因;指派改可搜索、默认只列在场者(久未活动者要显式展开);描述可折叠;标签可点。 - 两条潜在死路随之修掉:池子里的卡点「开始」会造出无主的「进行中」;被等待的卡在抽屉里只有「认领」这个必然失败的出口(现在有「解除等待」)。
- 无名等待(
waiting_on.who为空)不再渲染成"可认领"(卡面与统计页两处)——store 会拒绝认领等待中的卡,之前那是个点了必然失败的入口。
验证:npm test 207 项全绿 + typecheck 干净;每一处都带变异验证(改错即红)。
0.7.1 = 把「看不清的小字」修好 + 卡片更紧凑。 卡面上那一档「淡字」原来用的是 --dsw-alias-label-dimmed —— shell 里故意做到近乎不可见的一档(浅色 #e1e5ee、深色 #43454a,对比度 ≈1.1:1 / 1.3:1),于是引用号 #18、价值度 ◆2、列龄这些字两套主题都看不清;现在改用 --dsw-alias-label-tertiary(#81858c / #adb2b8)。同时按「结构重塑」把卡片从 5 行压到 3 行(#N 与 ◆value 移到 meta 行右侧,负责人与等待/审核胶囊合一行),泳道 260 → 240px,间距/行高同步收紧:同一块板上卡高平均 125px → 97px(−22%),一屏单泳道约 7 张 → 9 张。等待胶囊允许内部换行,保住「等了多久」这半条信息。
0.7.0 = markdown 保真度 + 两处安全加固。 详情与评论的渲染补齐了四件事:图片(,只放行 http/https)、嵌套列表(按缩进两级,子列表开在父 <li> 内)、只读任务复选框(- [ ] / - [x])、评论容器改块级 div(表格/列表不再塞进 span)。安全面同步收紧两处:cwd 白名单按方向分档 —— 写(POST)在无板目录里必须由宿主明确回答「这个工作区我服务」,读保持既有契约(面板要能读「会话不在线 / 板还没建」的工作区);信任门开始比 scheme(Origin: https://… 不再被当成本机 http 源放行)。另外收了审计剩下的三条 minor(CLI 打错 --cwd 不再与空板难分、锁等待预算覆盖 stale 窗口、上文那条 scheme)。
0.6.2 修的是「样式表归属」。 状态栏那枚「看板 · N ◷M」胶囊偶尔会退化成浏览器
默认按钮(灰底灰边、图标被挤到上一行)。根因不在布局:dsh 的客户端模块加载器会把文档里
所有还没有主的 <style> 收编给下一个加载的插件模块,再在它卸载 / 热重载时删掉;
我们的样式表原本是 React 渲染的(天然无主),于是被别人的一次重载顺手删掉,而 React 还以为
它还在(fiber 既不会补回,界面上也没有任何报错)。现在 TB_CSS 只由
ensureTaskboardStyles() 注入 <head>,出生就带 data-plugin="dsh-taskboard-kit",
加载器再也抢不走,丢了下次挂载还会自愈。建议所有 0.6.x 用户升级。
(0.6.1 是纯文案版本:「叫人类」改说成你自己的通知通道(msg9 / 桌面通知 / webhook /
邮件),不再把 msg9 写成唯一方式;msg9HookExample 改名为 notifyHookExample,
旧名保留为 deprecated 别名。零行为变化。)
升级同理:dsh plugin --profile web add dsh-taskboard-kit@<版本号>(0.x 锁小版本,
dsh plugin update 不会带你跨版本)。
给没有插件的 Agent 用(Kimi Code、Claude Code、任何 shell)
板就是一个文件,但绝不要手改它——锁和原子认领都在 store 里。kit 自带一个零依赖 CLI,封装的正是同一个 store,让每个 Agent 用同一个安全入口操作:
taskboard inbox --by kimi # ★ 会话开始第一步:现在该你处理的事(带该敲的命令)
taskboard list --waiting human # 谁在等人类(--waiting agent|external|any 同理)
taskboard stale # 协作健康:在等人类 / 审核没人认领 / 交接断了 / 列陈旧
taskboard roster # 名册:谁还在场(派活前查)
taskboard claim T-3 --by kimi # 原子认领,log 记 "kimi"(等谁的卡会被拒)
taskboard update T-3 --action submit --reviewer claude --by kimi # 提交并指定审核人(**持卡人的动作**:持卡人 / 卡主 / 人类,T-62)
taskboard update T-3 --action approve --by claude # 审核通过(非 reviewer/卡主/人类会被拒)
taskboard update T-3 --action block --on human --who iceskysl \
--question "现在就发,还是等 T-8 修完?" --by kimi # 挂到人类身上(触发外发通知)
taskboard update T-3 --action unblock --by iceskysl # 答复到了 —— 等人类的卡**只有人类**能解挂(agent 会被拒,T-61)
taskboard update T-3 --action close --note "已部署" --by kimi # 收口 —— 只有卡主 / 持卡人 / 裁决人 / 人类能敲(T-61)
taskboard comment T-3 --text "交接:…" --by kimi # 不改任务状态
taskboard create --title "…" --priority high --value 3 --by claude--by 指定 log 里的操作者(默认 $TASKBOARD_ACTOR 或 cli-agent);--cwd 指向别的 workspace;--json 输出机器可读结果;认领失败退出码 3 并给出可读的冲突原因。包还没上 npm 之前,直接从仓库调用:node /path/to/dsh-taskboard-kit/bin/taskboard.mjs list。dsh web 跑不跑都能用;web 在跑时,本机进程也可以直接调回环 bridge /dsh-taskboard/*。
环境开关
| 变量 | 作用 |
|---|---|
| TASKBOARD_ACTOR | Agent 工具写进 log 的默认操作者名(默认 dsh-agent) |
| TASKBOARD_HUMANS | 逗号分隔:哪些名字算人类(默认 human) |
| TASKBOARD_ACTOR_ALIASES | 规范名:别名1\|别名2,…——把同一个 Agent 的多个名字并成一个主人(默认 dsh:dsh-agent) |
| TASKBOARD_WATCH_NAMES | 逗号分隔的「自己人」名字列表(第一位为规范名,其余自动成为别名);watcher 按它判断哪些变化与我有关、哪些是我的自回声(默认 dsh,dsh-agent) |
| TASKBOARD_SIBLING_NAMES | 同一实例的其他会话名:它们的卡算我的,但它们的动作会通知我(默认空)。默认配置下两个 dsh 会话会互相看不见,这一项就是解药 |
| TASKBOARD_ALLOW_SELF_REVIEW | 1 = 允许自审(只给「一个 Agent 独占一个 workspace」的场景) |
| TASKBOARD_NOTIFY_CMD | 卡片开始等人类 / 等超 SLA 时执行的外发通知命令;卡片 JSON 从 stdin 进,TASKBOARD_TASK_ID / TASKBOARD_QUESTION / TASKBOARD_NOTIFY_REASON 等从环境变量进。失败只记日志,绝不让写板失败 |
| TASKBOARDKIT_LOCALE | en 强制英文工具输出(默认中文) |
| TASKBOARD_WATCH | 0 整体关闭变化推送与时钟自检 |
浏览器桥
面板通过 /dsh-taskboard/* 与宿主通信。只接受回环 / 同源调用;所有 POST 必须带 x-taskboard: mutate 头。浏览器侧的操作在任务 log 里记为 human。
开发
npm install
npm run build # esbuild → lib/index.js + lib/client.js
npm test # node 原生 runner:store、host smoke、client
npm run typecheck # tsc --noEmit零运行时依赖。浏览器 bundle 只 require shell 内置的 react / react/jsx-runtime。
License
MIT
