Repository navigation
Releases: sueqet/dsh-todo-board
Release list
v0.13.1
修复
问题 2:开出的新会话自己那一页看不到自己的待办(计数 3、行数 0)
根因:面板按 ownerSessionId 分框,但宿主下发的 ownerSessionId 用的是 effectiveTarget(沿容器往上走到规划会话)——spawned 会话的 runIn 指向容器、容器的 runIn 不是 session kind,走到底还是规划者,所以开出的会话永远不是自己待办的 owner。
- 宿主修复(
plain()):ownerSessionId改按targetSessionId报告——跟行走:已派发的行报它实际运行的会话,spawned 容器报它开出的那个会话;只有 newSession 容器在开出会话之前回退到规划会话,保证规划者 spawn 前不丢这行。pendingOwned/dispatch语义不变。 - 客户端修复(
boxTree()):孤儿行提升。以前after子级的父级不在当前 scope(被验收掉、在别的页签、或容器归属另一会话)会被静默丢弃。现在提升为根——看不见的行是再也拖不出来的行。
问题 3 同根因的加固:面板样式偶发被别的插件清掉
宿主 dsh-client-modules 的 claimStyles 会把所有没打 data-plugin 标记的 <style> 认领给当前物化的插件,那个插件下次 HMR 替换时会删掉自己名下的样式。本插件注入的每个 style 标签(主样式 + 三套皮肤)现在都打上 data-plugin="dsh-todo-board",与官方包同一约定。
测试
smoke:新增「spawned 容器归属它开出的会话」「改派行归属实际运行的会话」断言client-smoke:新增spawned(站在开出的会话里看自己的三条)与orphan(父级已验收的子级仍渲染)两段- 变异测试:host-guard 16 → 18、row-guard 42 → 44(各新增两条,均被抓到)
- 全套
check/test/guard/test:browser通过
Full Changelog: v0.13.0...v0.13.1
0.13.0 — 界面重做 + 派发改按归属 + 版本门改用 peer
版本门终于会拦了 / The version gate latches
engines.dsh 一直是一扇哑门。npm 只校验它认识的引擎名(node / npm),自定义引擎名不参与检查——所以在 DSH 0.2.0-rc.2 和 0.1.6 上装都不吭一声,装错版本要等到运行时才炸,错误还只写进插件日志。
0.13.0 起包里多了一条真正会拦的 peer 依赖:
"peerDependencies": { "@deepseek-ai/dsh": ">=0.1.7-rc.2" }npm 的解析器执行它(includePrerelease: true,所以 0.2.0-rc.2 这类预发布版本能被正确匹配):
| 已装的 DSH | 结果 |
|---|---|
0.2.0-rc.2(当前桌面端) |
✅ compatible |
0.1.7 / 0.1.7-rc.2 |
✅ compatible |
0.1.6 |
⛔ BLOCKED,并告知需要 >=0.1.7-rc.2 |
engines.dsh 原样保留——它仍是给 DSH 自己读的范围声明,只是别再指望它拦人。
要留在 DSH 0.1.6 或更早,请显式装 dsh-todo-board@0.12.0 或更低:那几版没有这道约束,也没有这层保护。
行为变更:派发改按「归属」,面板按会话分框
这一版改变了派发语义,升级前请读完下面两条。
同目录的不同会话不再互相偷活。 以前回合结束时是「在本目录里取最上面一条还没派发的待办」,所以 D:\DSH\plugin 下会话 A 干完活会顺手接走会话 B 建的待办,在 A 的上下文里执行。现在每条待办带一个归属(runIn),只有归属自己的那条会话才会取它。归属在派发时解析而非预先写死,所以改一次模式能带走整棵子树;解析带访问集和深度上限,环在表达上就不可能。
remind 从「跳过」变成「链条断点」。 以前读到 remind 会跳过继续找,现在读到就停。这正是 remind 现在的含义——「先规划、暂不开始」;用户把它当容器摆好计划,之后改成 newSession 即可开跑。恢复不需要额外操作:改成 resume 或 newSession,dispatchedAt 会被清掉,链条自然续上。
容器与父子嵌套。 面板里一个待办可以成为容器:
- 拖到某一行上 = 成为它的子级
- 拖到两行之间的间隙 = 同级排序(只改
order,不改归属) - 拖到框标题上 = 交给那条会话执行
面板按执行会话分「框」而不是按目录;面板三段式重排为 [当前会话] [当前目录] [全部],默认「当前会话」——归属决定什么会接着跑,所以默认页应该是你正待着的那条会话。
界面重做:蓝白极简 + 新增「随主题」
- 默认界面整块重画:白卡片 + 一个蓝色强调色,状态从「一圈边框」改成「一块淡色」,分隔线一律改成细发丝线,标题栏计数变成可数的徽标。控件一个没动,改的只是它们长什么样。
- 新增第四套界面「随主题」
theme:跟 DSH 界面走。面板是浮在别人界面上的东西,深色界面里一块白卡片太扎眼,点一下把颜色交还给宿主。 - 配色拆成一层
--tb-*变量,底表里不再有任何一处直接写 GUI 的--dsw-alias-*令牌。
新增:交接新会话时自动写交接摘要
newSession 待办在 note 为空时,会在交接的那一刻自动生成一段摘要(目标 / 已完成 / 卡点 / 关键决策)写进 note,标记 【自动交接摘要 · 可直接编辑或删除】。
只在 note 为空时生成——你自己写的交接说明一个字都不会被覆盖。失败一律不影响交接:卡住的交接比没有上下文的交接更糟。
兼容
会话格式在这两端都是 v4。旧 board.json 自动前滚(新增 runIn,加载后行为与旧版完全一致),无需手动迁移。
安装
dsh plugin --profile web add dsh-todo-board安装后重启 Profile(重新执行 dsh web)才会装载。
npm 发布需手动执行——若从 npm 安装遇到 404,请用仓库地址安装,或等 npm 上的
0.13.0发布完成。
v0.12.0
The parked one-line panel can now be dragged, and this release is adapted to and verified against DSH 0.2.0-rc.2 (the desktop build).
Dragging while parked
Parking the panel with – used to strand it: the drag handle lives in the title bar, which the parked state does not render. The line now carries its own drag, sharing one localStorage entry with the expanded panel.
Two things made this more than a copy-paste of the title-bar handlers:
- The click still unfolds the panel. One press is both a drag candidate and the click that expands it. Travel under 3px (
PARK_SLOP) stays a click; past it, the gesture is a drag and the click that follows the release is swallowed so the panel does not pop open after you moved it. - A parked move rewrites only x/y. The line is auto-sized (width follows the next task's title, height is one row), so its bounds are measured from its own rect, not the card's
MIN_W=300 / MIN_H=260. A stored width/height may now benull— "the stylesheet decides" — so dragging the line never resizes the card to 220x40 on the next unfold.
DSH 0.2.0-rc.2 adaptation
Verified point by point, not assumed:
- Session format is still v4 (
currentVersion: 4), so 0.11.2's producer-owned source holds for both 0.1.7-rc.2 and 0.2.0-rc.2 — one adaptation covers both ends. shell.overlayis still a root-scoped list slot;useSessionsstill receives thesessions.liststore ({ byId, ... }), so 0.11.4'sretainedBy.mainViewderivation remains correct.- The Cordis bridge's
button[data-cordis-badge]selector andCordis Pluginlabel are unchanged in 0.2.0-rc.2.
How it was verified: npm's dsh is 0.1.7-rc.2 and cannot see this release, so the checks were booted with the desktop app's own Electron (ELECTRON_RUN_AS_NODE=1) against the 0.2.0-rc.2 runtime inside app.asar — check-live.mjs and check-session-format.mjs all green. No host restart needed.
engines.dsh stays >=0.1.7-rc.2 (it already covers 0.2.0-rc.2); storage format and tool interface are unchanged.
Tests
Two mutation variants were NOT caught on the first run — holes in the tests, not in the code: an early return null in dimension() made the mutated expression unreachable, and the parked re-clamp branch had no coverage. Both fixed; now 59 mutations caught (clamp 3 + row 40 + host 16), npm test green, and the real-Chrome suite pins that the parked boundary comes from the line's own ~207px, not the card's 300px minimum.
README
565 -> 361 lines (about -53%), stating the 0.2.0-rc.2 adaptation and how it was verified. The 「复制这段给模型」 template is untouched, character for character (check-template-parity.mjs still passes).
0.11.4 — scope tabs read a store key that does not exist
Both scope tabs were permanently empty, and rows created from the panel were filed under no directory and no origin session.
The cause: one dead read
The panel took the current session from
props.useSessions((s) => s.current)
but the session-list store's snapshot is { ids, byId, phase, projectionsBySession } — there is no current key, so that read was undefined on every render. currentId collapsed to undefined and cwd to '', which turned the two tab predicates into sourceSessionId === undefined and dir === ''. Nothing could match.
What made it look like a filter bug instead of a dead read: the row labels stayed correct, because they come from the host payload (dirLabel, computed server-side) and never went through this code path. Only the two filter keys collapsed.
The current session is now derived the way the harness derives it — the summary the main view retains (retainedBy.mainView > 0). That is not a criterion invented here: dsh-client-ui-layout, -workspace, -cordis, -settings-general and -session all scan for exactly that.
The other half, which is persisted
The same dead read made creating a todo send dir: '', sessionId: '' and sessionTitle: ''. A row written that way never matches 「当前目录」, never shows a session chip, and has no session to continue in.
A code fix cannot repair rows already on disk. If you created todos from the panel on 0.11.3 or earlier and their directory is missing, edit that row's directory once (or delete and re-create it). Rows created by the model tool, or by any release up to 0.11.2, set those fields host-side and are unaffected.
Why the tests did not catch it
tools/client-smoke.mjs fed useSessions the shape { current: 's1', byId: … } — a current key the real store never sends. The stub answered a question production never asks, so production broke while the suite stayed green. A fixture that invents a field is worse than no fixture: it converts a live bug into a passing test.
The fixture now mirrors the real snapshot and lists the current session second: with a single session, "take the first row" and "find the row mainView retains" are the same thing, and the test would have passed for a reason it never checked.
Added: a scope section asserting on rows per tab (a tab that is highlighted but lists nothing is exactly the failure mode), three assertions on the create payload (dir / sessionId / sessionTitle) pinning the boundary that gets persisted, and 4 new mutation-guard entries (35 total) — reverting to s.current, ignoring the retainedBy criterion, and dropping each tab's narrowing. All caught.
Install
dsh plugin --profile web add dsh-todo-board
Requires DSH >= 0.1.7-rc.2 (session format v4). On DSH <= 0.1.6 install dsh-todo-board@0.6.1 instead — the two sides are not interchangeable in either direction. The README's version boundary was corrected in this release: 0.11.2 is the first release carrying the v4 adaptation, and the only published releases without it are 0.4.1 / 0.5.0 / 0.6.1.
0.11.3 — ship assets/ (npm README images) and correct the engines claim
Published-artefact fixes. Both were found by inspecting what npm actually serves, not by reading the source.
1. The README's images were broken on npm
package.json's files whitelist had no assets/, while the README refers to assets/screenshot-*.png relatively — which resolves against the repository root on GitHub but against the tarball on a package page. So the repo rendered perfectly and only npm showed four broken images, which is precisely where people evaluate the package.
assets is in files now (four PNGs, ~3 MB). Verified by downloading the published tarball and checking that every screenshot screenshots.json declares is present and is a real PNG.
2. engines.dsh does not warn at install time — the 0.11.2 notes said it did
Measured, not assumed: installing against DSH 0.1.5 and 0.1.6, both --dry-run and a real install, raises no EBADENGINE. npm validates the engine names it knows — node, npm — and a custom name like dsh is not among them, so that field is a declaration and nothing more.
0.11.2's README and CHANGELOG described it as a guardrail. That turned "we declared it" into "it will stop you", which is the kind of documentation error that costs someone an afternoon.
The README now says what is true: a mismatched version has no install-time warning, and what it actually looks like is the whole turn failing at runtime, with the reason written to the plugin log.
How it was caught is worth recording: only by installing the package. The source, the docs, and
package.jsonall agreed with the wrong claim.
Install
| Your DSH | Install |
|---|---|
>= 0.1.7-rc.2 |
dsh plugin --profile web add dsh-todo-board |
<= 0.1.6 (including 0.1.5-rc.3) |
dsh plugin --profile web add dsh-todo-board@0.6.1 — last v3-era release |
See the README's DSH version requirement section and the 0.11.2 notes for why the two sides are not interchangeable in either direction.
Also: .gitignore takes .npm-cache/ (the workspace cache used when the system npm cache directory is not writable).
0.11.2 — session format v4 (requires DSH >= 0.1.7-rc.2)
Session format v4: this release needs DSH >= 0.1.7-rc.2
The plugin's injected notices still carried the retired v3 plugin wrapper:
source: { kind: 'plugin', plugin: 'dsh-todo-board', form: 'notice' }
DSH 0.1.7 writes session format v4, which refuses every NEWLY written message
whose source kind is not producer-owned — "format v4 message requires a
producer-owned source kind". This does not merely drop the notice: the refusal
fails the whole turn. The source is now
source: { kind: 'plugin:dsh-todo-board', form: 'notice' }
which is the producer's own kind, and it carries no plugin field.
Reading an old log is a different path from writing a new message: the v3 -> v4
migration edge rewrites the old wrapper on the way IN, so existing session
history still opens. A newly written message must be v4-shaped from the start,
and the migration edge never sees it.
Which version to install
| Your DSH | Install | Why |
|---|---|---|
| >= 0.1.7-rc.2 | dsh plugin --profile web add dsh-todo-board |
this release |
| <= 0.1.6 (including 0.1.5-rc.3) | dsh plugin --profile web add dsh-todo-board@0.6.1 |
last v3-era release, tagged v0.6.1 |
engines.dsh is now >=0.1.7-rc.2 (format v4 ships in 0.1.7-alpha.1; rc.2 is
the first released carrier), so a mismatched install warns at install time
instead of failing mid-turn. 0.11.1 and earlier do not work on DSH >= 0.1.7 —
upgrade the plugin together with the harness.
Also in this release
- Dropped
@deepseek-ai/dsh-client-runtimefromdsh.client.inject: that
package no longer exists in current DSH. The field is parsed without an
existence check and its edges are informational, so it was a harmless stale
declaration — this only makes the list honest. - Tests pin the producer-owned source kind AND assert the v3
pluginfield is
gone: pinning the new kind alone stays green if both are written. - The non-vacuous control in
tools/check-session-format.mjsnow uses the
current source shape, so it is rejected for its content — which is what it
exists to guard. With a retired wrapper it passed for the wrong reason. - README: new "DSH version requirement" section.
Verified
npm test (host, browser, retry-loop, http fence, live cordis, fence parity,
session format, template parity) and npm run guard (2 + 31 + 16 mutations
caught) both green against DSH 0.1.7-rc.2.
0.6.1 — last session-format-v3 release (DSH <= 0.1.6)
Last session-format-v3 release. Install this one on DSH <= 0.1.6; do not install 0.11.x there.
dsh plugin --profile web add dsh-todo-board@0.6.1
Why this tag exists
Releases 0.7.0 through 0.11.1 were developed locally but never published to npm and never pushed to this repository. That left the only installable version (0.6.1, still npm's latest) with no tag to pin it — and no way to point at "the last release that works on the older DSH". This tag sits on 6043936, the commit that was HEAD of main when 0.6.1 was published.
The boundary
DSH 0.1.7 moved to session format v4, which refuses any newly written message whose source.kind is not producer-owned. The plugin's injected notices used the v3 wrapper { kind: 'plugin', plugin: 'dsh-todo-board' }, and on v4 that refusal fails the whole turn — not just the notice.
The two sides are not interchangeable in either direction:
| Your DSH | Install |
|---|---|
<= 0.1.6 (including 0.1.5-rc.3) |
dsh-todo-board@0.6.1 — this release |
>= 0.1.7-rc.2 |
dsh-todo-board@0.11.2 — see that release |
Details: 0.11.2 release notes and the README's DSH version requirement section.