DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Taskboard Kit — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

dsh-taskboard-kit

Taskboard Kit

dsh (DeepSeek Harness) 任务看板套件:每个工作区一个本地任务看板。代理使用模型工具创建、认领和推进任务;人类在看板选项卡中查看并操作同一个看板——工作区中的单个 JSON 文件是唯一事实来源,无需服务

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add dsh-taskboard-kit@0.9.2
README兼容性版本

兼容性与来源证明

Taskboard Kit 以 dsh-taskboard-kit 发布,当前版本为 0.9.2。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/10/16

版本

0.9.2stable
2026/10/11
0.9.1stable
2026/10/10
0.9.0stable
2026/10/10
查看其余 10 个版本收起版本
0.8.0stable
2026/10/9
0.7.5stable
2026/10/7
0.7.4stable
2026/10/6
0.7.3stable
2026/10/3
0.7.2stable
2026/10/3
0.7.1stable
2026/9/30
0.7.0stable
2026/9/30
0.6.2stable
2026/9/30
0.6.1stable
2026/9/30
0.6.0stable
2026/9/29

相关插件

正在加载相关插件…

最新版
0.9.2
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
1.9 MB
文件数
16
Surface
web
许可证
MIT
发布源
npm
周下载
1,054
安全扫描
✓ v0.9.2 扫描通过
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 productivity-workflow 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Experimental Schedule Bundle@deepseek-ai/dsh-experimental-schedule-bundle为 Web 组合添加 Schedule、其任务页面和时间上下文的可选捆绑包Ab Ocrdsh-ab-ocr--- description: "外部 ocr 工具:逐页识别扫描的 PDF 或图像,仅在处理完最后一页后按顺序合并页面,并使用模型确定的标题级别保存为一个 Markdown 文档。" kind: "package-reference" ---Client Ui Task Board@linxin666/dsh-client-ui-task-board面向 DSH Web GUI 的主机权威任务面板,支持实际会话执行、主机 cron 调度以及可选的跨平台空闲睡眠保护;以挂载方式提供,无需修改 DSH 源代码。

README

dsh-taskboard-kit

One local task board per dsh workspace. Agents create, claim and progress tasks with model tools; humans watch and drive the same board in a kanban tab. The board's only source of truth is a single JSON file inside the workspace — no server, no account system, nothing to deploy.

中文文档

Compatibility

  • dsh ≥ 0.1.7 — fully supported since v0.5.3: the session cwd rides the sessionId slot prop (0.1.7 removed current from the session list state), the composer entry rides the rebuilt compact dock layout, and injected board notices use the v4 producer-owned source kind (plugin:taskboard-kit) that 0.1.7's persistence requires. Older dsh versions keep working through legacy fallbacks.

What you get

  • Eight model tools the agent can call in any session of the workspace:
    • taskboard_inbox — the first call of every session: what is on YOU right now, most urgent first, each item with the command that moves it
    • taskboard_list — list tasks (filter by status / column / assignee / who they wait on)
    • taskboard_create — add a task, optionally delegating it to someone
    • taskboard_claim — atomically claim a task from the pool (exactly one winner under concurrency; a card waiting on someone is not claimable)
    • taskboard_update — start / stop / submit (naming a reviewer) / approve / reject / done / close / reopen / block / unblock, reassign, edit fields (incl. value points), append notes
    • taskboard_comment — add an information comment (findings / handoffs / test feedback) without changing state
    • taskboard_get — full task detail with the event timeline, the comment thread, column age / SLA, reviewer and wait
    • taskboard_roster — who is actually here (last time each actor acted, plus its aliases)
  • 「开启看板 / Turn the board on」 — a fresh workspace shows a one-click wizard instead of an empty board: it creates .dsh/taskboard.json AND seeds .dsh/BOARD-PROTOCOL.md (the workspace's own rules), then tells you agents can start. Both files are idempotent — an existing board, or a protocol doc the project has edited, is never overwritten. An on but empty board shows a different prompt ("create the first card"), because those two states are not the same thing: the first needs enabling before any agent can work, the second just needs work.
  • A「看板 / Board」conversation view in dsh web with three switchable views over the same board:
    • 按进度 / By status — six swimlanes (pool · assigned · in progress · review · to settle · settled), cards with priority / value / assignee / current-column age / stale dot / reviewer / who it waits on, a ◷ N cards are waiting on you strip and a ✔ N cards done, awaiting settle strip at the top (answerable / closeable inline), a detail drawer with the event log, comments and the collaboration facts, and one-click claim / start / submit / approve / reject / close / reassign.
    • 按负责人 / By owner — one lane per owner (alias-folded), so "what is on each person's plate" is answerable at a glance; settled work hides behind a toggle.
    • 统计 / Stats — the macro read: KPI tiles (open / to settle / WIP / blocked / median cycle time / reject rate / total value), a daily created-vs-settled chart with a running backlog line (the one chart that says whether work arrives faster than it closes), status and priority distributions, a per-owner load/throughput/cycle table, and a where-time-piles-up chart of cumulative dwell per column. Every number is derived from the board file's own log — no extra state, no extra requests.
  • Markdown in the drawer (v0.7.0): task details and comments render as markdown — headings, lists (nested by indentation, two levels), GFM tables, fenced code, quotes, links, images (![alt](url) over http/https only) and read-only task checkboxes (- [ ] / - [x]). The pipeline escapes first and only ever emits tags it built itself, so a card written by any agent stays data — see docs/images/markdown-fidelity.png for the rendered result.
  • A multi-agent collaboration protocol: the full spec is docs/COLLABORATION.md; its short form goes into the system prompt at session start, and the CLI --help / panel guide snippets are generated from the same rules.
  • Session-start awareness: the agent receives its own actionable slice (reviews it owes, who is blocked on it, what was rejected back to it, which delegation went quiet) — not a bare count.
  • Board-change push: an fs.watch watcher on every live session's board notifies the agent (context-only, never a wakeup) when a task is assigned to it, a verdict lands on its task, a review is handed to it, someone starts waiting on it, a card starts waiting on the human, a pool task appears, or someone comments on its work — no polling, no msg9 needed. Changes inside a 5s storm window merge into one notice.
  • A clock-driven self-audit (v0.5.4): a file watcher can never see "nobody touched this card for three days" — so a periodic pass re-reads the board and nudges only about what is pressing on you (a review you owe, someone waiting on you, a rejection you never answered, your own stale cards, a delegation that went quiet, a human wait past its SLA), with the command to run. One nudge per workspace per 30 minutes.
  • Waiting on a human really reaches the human (v0.5.4): block --on human lands in the panel's「waiting on you」list; set TASKBOARD_NOTIFY_CMD and it is pushed out-of-band too (msg9, a desktop notification, a webhook — your wiring); past 24h it escalates automatically.

The board file

Each workspace gets <workspace>/.dsh/taskboard.json:

{
  "version": 1,
  "workspace": "/abs/path",
  "next_seq": 2,
  "tasks": {
    "T-1": {
      "id": "T-1",
      "title": "Write the release notes",
      "detail": "…markdown…",
      "status": "open",            // open | in_progress | review | done | closed
      "assignee": null,             // null = waiting in the claimable pool
      "reviewer": null,             // v0.5.4+: who owes the verdict while status is review
      "waiting_on": null,           // v0.5.4+: { kind, who, question, since } — parked on a human/agent
      "priority": "high",           // high | medium | low
      "value": 3,                   // value points ½|1|2|3|5|8 · null = unestimated
      "tags": ["docs"],
      "created_by": "dsh-agent",
      "created_at": "…", "updated_at": "…",
      "log": [{ "at": "…", "by": "dsh-agent", "event": "created" }],
      "comments": [{ "at": "…", "by": "kimi", "text": "handoff: …" }]  // v0.2+; older files load with []
    }
  },
  "actors": {                       // v0.5.4+: the roster — who acted, and when we last saw them
    "dsh": { "kind": "agent", "aliases": ["dsh-agent"], "first_seen_at": "…", "last_seen_at": "…" }
  }
}

Because the file lives in the workspace, every harness and every human working in the same directory sees the same board. Commit it or gitignore it — your call. All mutations go through a lock file with stale-lock recovery and atomic tmp+rename writes, so concurrent agents (or the panel) can never tear the file. claim is adjudicated inside the lock: N concurrent claims → exactly one succeeds, the rest get a conflict.

Upgrading to 0.6.0 (read this if you used an older board)

done is no longer terminal — closed is. Before 0.6.0 the flow ended at done and closed meant "abandoned". That made "finished but nobody closed it out" invisible: cards sat in done forever and no rule said anyone owed a step.

Now done means finished and approved, still owes a settle: the card stays on the board and still counts as open work until someone closes it. Findings that follow from that change:

  • a「待收口」strip lists done-but-unsettled cards with a one-click settle;
  • a done card past 72h is reported as stale (it used to never be);
  • taskboard_inbox tells an owner "your card is done but unsettled", with the command;
  • the stats view reports median cycle as created → done (the work) and settle lag as done → closed (the paperwork) — two numbers, because one combined number made a fast board look slow whenever closing lagged.

Abandoning work is still a close — say why in the note; there is no separate "abandoned" status. Old board files load unchanged (cancelled → closed, missing value / comments / actors are hydrated on the next write). If your existing board has a pile of done cards, they now show up as work awaiting a settle — that is the intended reading, not a bug.

Status model

columnrule
待认领 / poolopen, no assignee, not waiting on anyone — anyone may claim
已指派 / assignedopen with an assignee — delegated, not started
进行中 / in_progressclaimed or started
待审核 / reviewsubmitted, waiting for a reviewer to approve / reject
待收口 / doneapproved (or marked done directly) — NOT terminal, still owes a settle
已结清 / closedsettled — close (the panel hides them behind a toggle)

Flow: open → in_progress → review → done → closed. closed is the one terminal status; done means "finished and approved, not yet closed out", so a done card stays on the board and still counts as open work until someone settles it. That two-step exists because approval is not the same as the matter being closed out (deploys, upstream sign-off and docs may still follow). Abandoning work is also a close — say why in the note, as there is no separate "abandoned" status. A「待收口」strip at the top of the panel lists done-but-unsettled cards with a one-click settle, so finished work cannot rot in a lane nobody owns. In actions: 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; the legacy name cancel is its alias), done|closed → open (reopen). Status names mirror the msg9 task model on purpose, so a future server-backed board keeps the same semantics. Boards written by older versions load seamlessly: cancelled tasks/logs become closed, and missing value / comments fields are hydrated.

Two collaboration axes are orthogonal to the status (v0.5.4 — neither invents a status):

fieldmeaningwritten by
reviewerwho owes the verdict; only that reviewer, the task's creator or the human can approve/reject; you cannot review your own worksubmit (default resolution: creator → most recently active other agent → the human)
waiting_onwho the card waits on (human/agent/external + who + question + since); a waiting card cannot be claimedblock / unblock

The board also keeps an actors roster (who has acted, when they were last seen, aliases like dsh ≡ dsh-agent) — the fact that answers "was this delegated to an agent that is no longer around?".

Each task still carries value points (Fibonacci scale ½ / 1 / 2 / 3 / 5 / 8, null = unestimated) — what the card is worth. Set it at create or update time; on the CLI, --value 1/2 means ½ and --value none clears the estimate.

Install

dsh plugin --profile web add dsh-taskboard-kit@0.7.4
# restart dsh web, open any session, and the「看板」tab is there

Pin the version explicitly. pnpm 11 (which dsh plugin forwards to) ships a supply-chain minimumReleaseAge gate: a version published minutes ago is held back, and a bare add dsh-taskboard-kit then silently installs an older one — you would get a board whose done is still treated as terminal without any hint. Naming @0.7.4 opts that release out of the gate and installs what you asked for. Once a release is more than a day old, a bare add finds it too.

v0.9.2 — fix: rigid 1310px column row (clipped on narrow, blank on wide)

  • Fix (T-85): styles.column was width: 240 with flexShrink: 0, producing a rigid 1310px row: narrower panels clipped the right-hand columns (317px at 1280, 157px at 1440) while wider ones left dead space (83px at 1680, 963px at 2560). Columns now use flex: 1 1 240px; min-width: 168px, the width spec lives in a single TB_LANE constant, and overflowX: auto is kept only as a fallback — no overflow: hidden band-aid. This was a pre-existing defect, not a v0.9.x regression (identical measurements at v0.9.0 and v0.9.1).
  • Tooling: a new 18th suite (tests/layout-invariants.test.mjs) drives a real headless Chrome against the built artifact and asserts, at four widths: no page-level horizontal overflow, the lane row is not clipped, the last column's right edge is inside the viewport, the row must fill, and column width ≥ the minimum. Note: page-level scrollWidth === clientWidth was already true before the fix, so asserting only "the page doesn't overflow" would not have caught this. Pointing the suite at the v0.9.0/v0.9.1 client makes it genuinely red (clipping 310/150px, blank 90/970px).
  • Known consequence (accepted): the "by owner" view fills the panel when there is a single owner; do not revert to flexShrink: 0.

Versioning: under 0.x, fixes and internal changes are patches — hence 0.9.2.

v0.9.1 — 320px drawer header fix · remove the “to settle” strip · pre-commit guard

Versioning (effective this release): under 0.x, fixes and internal changes are patches; only new capabilities or user-visible behaviour changes warrant a minor — a minor should be a capability step, not a pile of fixes.

  • Fix (T-75): at a 320px viewport the drawer header title collapsed into a vertical column and the header grew to 953.95px; now 117.98px (title 175 → 15px). 560/900 are byte-for-byte unchanged (60.89 / 40.59px) — zero regression. Chosen by measurement: flex-wrap: wrap plus flex-basis: 160px (both are required; flex-basis: 100% was rejected because it pushed 560/900 to 102.39px).
  • Change (T-77): the top “to settle” strip is removed (147.59 / 100.80 / 75.80px with one done card; 332 / 235 / 210px with four) — board content moves up 154 / 107 / 82px. The explanatory sentence moves into the done column header's tooltip, and both settle paths remain (the drawer's “Settle (close)” is the same close action) — the only cost is one extra click.
  • Tooling (T-79 / T-82): a pre-commit guard (npm run check:commit / npm run commit:guarded) enforcing source↔artifact consistency, refusing to commit while another writer is active, and giving a clear message when the index is empty. An optional repo-local git hook (npm run install-hooks) never touches the global config; git commit --no-verify remains available.
  • Docs: two design-system facts (ACCENT is the host's inverting monochrome — use LINK; CSS inside template strings is not type-checked, so a comment after */ silently eats a rule).

Verified: npm test 351 checks / 17 suites green, clean typecheck, green action-parity; negative controls genuinely red; the loaded face was proven by page-level sha256.

v0.9.0 — closing the permission gate's side door, and reshaping drawer / board density

Every entry is labelled by kind (capability / fix / docs / tooling) — otherwise the version count gets read as a delivery volume.

  • Fix · the gate's side door (T-69): parking block onto a card that is already waiting replaces that wait, so it now goes through the same ownership gate as unblock and explicitly records an unblocked event first; a first-time block is unconstrained. Motivated by four same-family overreaches in one day — one of them exactly this side door.
  • Capability · drawer density (T-74): attributes 11 rows → 4 semantic rows (people / values / time / tags, wrapping in-row); actions 9 rows → one chip row + one collapsible 「not available」 line (no reason is deleted); assignment → one line current: X (change) + an expandable combobox; detail tab order is now attributes → description → actions → assignment. Measured: three blocks 1010.67 → 233.92px (−76.9%); description top 1182 → 237. Also fixed a real bug: copyable fields had a dead title.
  • Capability · board banner → card emphasis (T-76): the 156.52px banner is gone, replaced by a left rail + ⏳ badge on the card (badge title carries the full question text), the card is pinned first in its column, and a single 28px summary line remains (not rendered when nothing waits). Measured: top block 156.52 → 28px, board content up 135px, waiting card 6/7 → 0/7. Colour rule: 「waiting on you」= accent blue + solid rectangle; 「overdue」= amber + pill outline (verified in both themes).
  • Fix · changelog correction (history is not rewritten): v0.8.0's note claimed "every path now runs the same ownership check"; T-69 disproved it (block replacement was ungated then). The published text stays as-is; corrected here.
  • Docs · two design-system facts: ACCENT is the host's inverting monochrome (use LINK for an accent blue); CSS inside template strings is not type-checked (tsc), so a comment placed after */ silently eats a whole rule — hence the new skeleton-validity check.

Verified: npm test 333 checks / 16 suites green + clean typecheck + green action-parity; negative controls genuinely red; the loaded face was proven by page-level sha256 (not rev+200, which was shown to be invalid).

v0.8.0 — permissions move from convention to mechanism, plus a proactive channel for reviewers

Everything here is a behaviour change; the client side shows up on refresh, while the host side (permission checks, notifications) needs a dsh web restart.

  1. Actor checks for unblock / close (T-61). Motivated by four same-family overreaches in one day: an agent unblocked a human-parked card and ruled in the human's place, and twice published versions on its own.
    • unblock: a card parked on the human may only be released by the human — never by an agent; a card parked on a named agent only by that agent (or an alias-equivalent name) or the human; who empty / kind:'external' are deliberately relaxed (the other side never knocks on this board; locking it literally only rots the card).
    • close / cancel / reopen: only the creator / current owner / reviewer / the human; refusals are conflict and the message spells out who was refused, what the card waits on, who parked it, and who to ask.
    • Alias folding: roster first, config as fallback (built-in dsh ≡ dsh-agent, TASKBOARD_ACTOR_ALIASES / WATCH_NAMES / SIBLING_NAMES, transitively merged) — otherwise you lock yourself out.
    • Backward compatible: cards without created_by fall back to the pre-change behaviour; state is checked before ownership, so nothing-waiting / illegal moves keep their original error code.
    • The scope is stated where it matters: this prevents mistakes and overreach; it is not a security boundary — --by is a record, not a credential.
  2. A submit ownership gate, ownership on wait release, and a lock-out fix (T-62): previously anyone could submit someone else's in-progress card; now only the owner/creator/human. submit/approve/reject/done/close/reopen used to clear waiting_on silently (which let a human-parked card be turned into "done" by someone else) — every path now runs the same ownership check and explicitly writes an unblocked event. The lock-out the new mechanism introduced is fixed too: who the card waits on is now an additional permission, not an exclusive one (waiting on an agent/external/unnamed ⇒ creator/owner/reviewer can always release; waiting on the human stays human-only).
  3. (T-56): after a successful , the named reviewer is notified — a ready-to-copy command is printed, and the message is actually sent only when exists the board roster knows his address. Unknown address / no / send failure / internal error (submitting is the core action, notifying is secondary). Idempotence is anchored on the index of the last event (not a timestamp). gained a 「review owed」 section grouped by reviewer, plus .

Verified: npm test 312 checks green (14 suites) + clean typecheck; new suites tests/authority.test.mjs (18 checks) and tests/authority-wait.test.mjs; mutations turn exactly the expected assertions red; backward compatibility is pinned against the real board.

v0.7.5 — turning "said in review, nobody followed up" into a list

Client and CLI changes only — refresh the page (no restart needed for the CLI side).

  1. New taskboard tails (review-tail scanner): scans the decision notes and review comments of closed / done cards and pulls out sentences that look like outstanding work; each hit carries **card + location (log:6 / comment:3) + sentence offset
    • the original fragment + the rule that fired**. Disposition: --file <tail> --card <id> files it, --waive <tail> --reason "…" explicitly voids it (a reason is required), --reset undoes it; stored in an optional Board.tails? field (old boards still read). It also feeds the existing surfaces: a 「review tails」 section in stale, and a bounded hint in inbox for whoever owes them. The rules were narrowed against real data (the first version produced 240 hits — wallpaper nobody reads — now 94), each narrowing rule has its own unit test, and a not-done rule fires only on section headings (so the items under "explicitly not done" surface, with noise control pinned by an assertion — proven by two mutations). Why: at least 10 review leftovers across 38 closed cards had been dropped on the floor (including falsely-green claims like "a test pins this" when no such test existed). The root cause is structural: a review verdict lives in a note, and a note does not move a card — so it has no carrier, no reminder, no list, and is therefore silently forgettable.
  2. Nine review leftovers cleaned up: including the test that was claimed but never existed (tests/action-parity.test.mjs drives the host: 7 actions × 5 states through the real updateTask, compared against what the drawer actually renders — behaviour, not string literals); the stats page no longer uses brand-primary as a data colour (done uses the host success green, deliberately not the link colour so it cannot collide with in-progress; measured contrast 2.28/6.89 and 4.23/6.75 in the two themes); MILESTONE_TAG case handling aligned with semverParts; the ? guide gained a card-mark legend derived from the real constants; the card ref gained a proper title; T-93-hotfix / T-93.5 number boundaries fixed; two half-width commas and a source-level rule to keep them from coming back.
  3. A guard on the card render layer: an unnamed wait ( empty) . Previously, reverting the render branch order left — the derived layer was guarded, the layer that actually paints the card was not. A mutation now turns exactly one assertion red. Also added: 「the reviewer can still decide」 (one , three surfaces), and when the reviewer is empty nothing is added (no fallback to the creator — consistent with the attribute row beside it).

Verified: npm test 256 checks green + clean typecheck; every new rule and cleanup carries mutation evidence (T-44 13/13 · T-42 10/10 · T-50 two-way · T-40 single-point); the scanner took this workspace's real board from 94 open tails to 0 (47 filed / 47 waived, each with a reason).

v0.7.4 — the keyboard works, walking never loses sight, and the toolbar stops being "unstable"

Client-side only: refresh the page. Three things:

  1. Keyboard navigation j / k — move the selection in visual order (top to bottom, left to right) within the current view; the drawer follows along; Enter opens the first card. The order is derived from the same lanes the board renders, so "keyboard order === screen order" is structural, and a collapsed "settled" lane is not in the order at all (nothing rendered should be reachable). While focus is in an input, the comment box, or a contenteditable, every key goes to native behaviour (⌘/Ctrl/Alt, IME composition, and the guide/about/picker/new-task overlays are left alone too); Esc still has exactly one layering — no second meaning was added.
  2. Walking brings the card back into view — on a long board or a narrow window, j/k scroll the selected card into sight. It uses block:'nearest' and does not scroll at all when the card is already visible (otherwise every keystroke jumps, which is worse than not scrolling), and it never drags the drawer along.
  3. Toolbar restructure (three groups + always present) — this release answers "the layout and display are unstable":
    • the row now has three groups: views (By status|By owner|Stats) · filters (Include closed) · actions (info ? ⓘ · action ↻ · primary button), with 4px inside a group and 16px between groups;
    • Include closed is always present (it used to render only in the owner view ⇒ switching views made a control appear or vanish, which is the instability);
    • the three tool icons are inline SVG now (? was ASCII while ↻/ⓘ were symbol glyphs — identical CSS could never align them);
    • "Refresh" is no longer wedged between "Guide" and "About";
    • every clickable control is 28px tall with the same radius, hover and keyboard focus ring;
    • it steps between layouts instead of squeezing: ≥720 full labels → 520–720 drops path → count → title in that order and the filter keeps only its box → <520 moves the filter and the three tools into ⋯ (the views and the primary button never leave; at 320px the views join the menu too). is wired into the existing Esc layering — one keypress closes one layer.

Verified: npm test 230 checks green + clean typecheck; every change carries a mutation check (keyboard guard / ordering / reveal gate / icon form / group gap / menu layering …), and a real browser measured the geometry: 16×16 icons, 28×28 buttons, 0.00px spread between the vertical centres of all clickable controls, one non-overflowing row at every breakpoint, ⋯ open/close and Esc, visible focus, a truncated long path, an empty board and the longest English labels.

v0.7.3 — the ⓘ About card, one holder line everywhere, two cleanups

Client-side only: refresh the page — no dsh web restart needed. The「关于」links are the only thing here that ever leaves your machine, and only when you click one.

  1. The「关于」card (ⓘ; since 0.7.4 it sits next to ? in the info group, while ↻ moved to the action side). Name + version (the version is injected at build time from package.json via esbuild's define; a build that forgets it falls back to dev rather than printing nothing), one sentence on what this is, and then the block that is actually worth reading — local transparency: the board file's absolute path, how many cards and how many roster entries it holds, the data-format version (board.version), the plugin id, and the plain statement that all of it lives in that one JSON file: no server, no accounts, no cloud. Below that, four GitHub entries — repository · report an issue · the collaboration spec (docs/COLLABORATION.md) · these release notes — every one target="_blank" rel="noreferrer", plus MIT and the author. Esc closes it (it is the topmost layer: the key never falls through to a drawer or the guide underneath).
  2. The holder line is now the panel's one answer to「球在谁手上」— on every surface. The card, the drawer and the status-bar mini drawer all render mark + name from the same derivation (currentHolder), strictly one line: ➤ to submit · ○ to claim (pool) · ◷ to answer / to reply (parked on a human / an agent) · ⚑ to decide (review) · ⌂ to settle (done). closed has no holder. It is derived from the state machine, not from assignee: in review the assignee is frozen (the reviewer holds the ball) and in done the reviewer is cleared (the creator owes the settle), so reading「当前处理人」literally points at the wrong person in exactly those stages. The mini drawer used to stack three per-field badges that said the same thing twice; it now shows this single line.
  3. Title prefixes are stripped for DISPLAY only. A leading 【owner】 T-93 · that merely repeats what the card already shows is dropped from the rendered title — the untouched original stays in the tooltip. Nothing is rewritten in the board file.
  4. Long names truncate instead of spilling. .tb-badge-outline (and every sibling 10px pill) now carries max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap: a long actor name used to overflow its dashed border and get clipped mid-glyph by the lane, which read as broken layout rather than「the name is long」.
  5. Two cleanups: three dead style keys from before 0.7.2 ( / / , each defined once and referenced nowhere) are gone.

Verified: npm test 214 checks green + clean typecheck; the version fallback, the holder marks, the badge ellipsis rule and the Esc layering each carry a mutation check (break it ⇒ that test goes red).

v0.7.2 — making the cards and the stats readable at a glance

Four rounds of owner feedback (2026-10-01), shipped together. Client-side only: refresh the page — no dsh web restart needed.

  1. Three-row cards: row 1 = the fixed attributes (priority dot · id · value · status · owner/creator … column age pinned right, 10px); row 2 = the title (≤2 lines, redundant prefixes stripped for display only); row 3 = what moves (holder · waiting · review · tags, strictly one line). Card height went from 90–123px to a uniform 88px.
  2. Title de-noising: a 【owner】 T-93 · prefix that merely repeats what the card already shows is dropped (the 【owner】 only when it is this card's owner/creator/reviewer; the number only when it is this card's own id; the original string stays in the tooltip). The id match requires a whole token — T-930 / T-93X / T-93-2 are no longer eaten alive (a real bug caught in review).
  3. The holder view: a card answers exactly one question — whose ball is it, and what do they owe. ➤ to submit / ◷ to answer·to reply / ⚑ to decide / ⌂ to settle / ○ to claim (closed has no holder). It is derived from the state machine: in review the assignee is frozen and in done the reviewer is cleared, so reading "current handler" literally points at the wrong person in exactly those stages.
  4. Marks instead of labels: @ owner · ✎ creator · ◆ value · ◷ waiting · ⚑ decide · ⌂ settle · ➤ holder. Text glyphs only, never emoji (emoji render as colour bitmaps and fight the theme); the meaning lives in each tooltip. Card text is 10.6% shorter.
  5. Stats v2: 7/14/30-day windows; KPIs split into "what needs you" and "background", each with a delta against the previous equal-length window (fewer than 3 samples ⇒ an explicit "not enough samples" badge rather than a fake trend); new "what needs doing now" (holder ranking + anomalies, every row opens the card) and a value view (backlog vs delivered, throughput, value/cycle per card); the flow chart gained a y-axis, gridlines and value labels, status became a ring chart, and milestone progress is derived from tags (e.g. v1.42.0).
  6. Drawer v2: sticky header (title + holder row + primary actions); a property table (values copyable); with its () and the unavailable ones are ; the assignee picker is searchable and (quiet ones need an explicit expand); the description collapses; tags are clickable.

Verified: npm test 207 checks green + clean typecheck; every change carries a mutation check (break it and the test goes red).

0.7.1 makes the quiet text readable and the cards tighter. The card's quiet tier used --dsw-alias-label-dimmed — a shell token that is near-invisible by design (#e1e5ee on light, #43454a on dark; ≈1.1:1 / 1.3:1), so task refs (#18), value points (◆2) and column ages were unreadable in BOTH themes. They now use --dsw-alias-label-tertiary (#81858c / #adb2b8). Cards were reshaped from five rows to three (#N and ◆value moved onto the meta row, owner + waiting/reviewer badges share one row), lanes went 260 → 240px, and spacing/line-height tightened: a card is ~22% shorter on the same board (125px → 97px average), so one lane shows ~9 cards per screen instead of ~7. The waiting chip may wrap internally so the "how long has this been waiting" half never gets ellipsized away.

0.7.0 = markdown fidelity + two security hardenings. The drawer's rendering gained four things: images (![alt](url), http/https only), nested lists (two levels by indentation, the child list opening inside the parent <li>), read-only task checkboxes (- [ ] / - [x]), and a block-level comment container (tables/lists no longer sit inside a span). Two guardrails were tightened at the same time: the cwd whitelist is now direction-aware — a mutation (POST) may only create a board in a boardless directory when the host positively says it serves that workspace, while reads keep the older contract (the panel must be able to read a workspace whose session is not live and whose board does not exist yet); and the trust gate now compares the scheme (an https://… Origin is no longer treated as this plain-http origin). Three remaining audit minors came along too: a mistyped --cwd is no longer indistinguishable from an empty board, the lock wait budget now outlasts the stale window, and the scheme check above.

0.6.2 fixes stylesheet ownership. The status-bar「Board · N ◷M」pill could occasionally collapse into a browser-default <button> (grey fill and border, the icon pushed onto its own line). The cause was not layout: dsh's client module loader books every unowned <style> in the document to the next plugin module that materializes, and deletes it when that plugin unloads or hot-reloads. Our sheet was React-rendered (hence unowned), so a stranger's reload deleted it behind React's back — no error, and React never put it back. TB_CSS is now injected into <head> by ensureTaskboardStyles() and is born tagged data-plugin="dsh-taskboard-kit", so the loader can never claim it, and a lost tag heals on the next mount. 0.6.x users should upgrade.

(0.6.1 was copy-only: reaching a human is described as your notify channel (msg9 / desktop notification / webhook / mail) instead of naming msg9 as the way; msg9HookExample became notifyHookExample, old name kept as a deprecated alias. No behaviour changed.)

Upgrades: use dsh plugin --profile web add dsh-taskboard-kit@<version> with the exact version (0.x locks the minor, so dsh plugin update will not move you).

For agents without the plugin (Kimi Code, Claude Code, any shell)

The board is just a file, but never hand-edit it — the lock and the atomic claim live in the store. The kit ships a zero-dependency CLI over the very same store, so every agent works the board the same safe way:

taskboard inbox --by kimi                   # ★ the first call of every session: what is on you, with its command
taskboard list --waiting human               # who is waiting on the human (--waiting agent|external|any too)
taskboard stale                              # health: waiting on the human / review nobody owns / broken handoffs / stale columns
taskboard roster                             # who is actually around (check before delegating)
taskboard claim T-3 --by kimi                # atomic claim, stamped "kimi" (a waiting card is refused)
taskboard update T-3 --action submit --reviewer claude --by kimi   # hand to review — the HOLDER's act (holder / creator / the human only, T-62)
taskboard update T-3 --action approve --by claude                  # only the reviewer / creator / human may decide
taskboard update T-3 --action block --on human --who iceskysl \
  --question "ship now, or wait for the T-8 fixes?" --by kimi       # park it on the human (fires the notify hook)
taskboard update T-3 --action unblock --by iceskysl                 # the answer landed — a human wait is released by the HUMAN (an agent's unblock is refused, T-61)
taskboard update T-3 --action close --note "deployed" --by kimi     # settle — creator / owner / reviewer / the human only (T-61)
taskboard comment T-3 --text "handoff: …" --by kimi     # state untouched
taskboard create --title "…" --priority high --value 3 --by claude

--by names the actor in the task log (default $TASKBOARD_ACTOR or cli-agent); --cwd points at another workspace; --json gives machine-readable output; a lost claim exits 3 with a readable conflict. Until the package is on npm, invoke it straight from the repo: node /path/to/dsh-taskboard-kit/bin/taskboard.mjs list. Works with or without dsh web running — and while dsh web IS up, any local process can also call the loopback bridge /dsh-taskboard/* directly.

Environment switches

variableeffect
TASKBOARD_ACTORdefault actor name the agent's tools write into the log (default dsh-agent)
TASKBOARD_HUMANScomma-separated names that count as humans (default human)
TASKBOARD_ACTOR_ALIASEScanonical:alias1|alias2,… — fold one agent's many names into a single owner (default dsh:dsh-agent)
TASKBOARD_WATCH_NAMEScomma-separated names the watcher treats as "me" (the first is canonical, the rest become aliases); it decides both what is mine and what is my own echo (default dsh,dsh-agent)
TASKBOARD_SIBLING_NAMESthe OTHER sessions of this same instance: their cards are mine, but their actions still reach me (default empty). Without it, two dsh sessions in one workspace go blind to each other
TASKBOARD_ALLOW_SELF_REVIEW1 allows self-review — only for a workspace owned by a single agent
TASKBOARD_NOTIFY_CMDshell command run when a card starts waiting on a human, or blows its wait SLA. The card arrives as JSON on stdin plus TASKBOARD_TASK_ID / TASKBOARD_QUESTION / TASKBOARD_NOTIFY_REASON in the env. A failure is logged, never fatal
TASKBOARDKIT_LOCALEen forces English tool output (default Chinese)
TASKBOARD_WATCH0 disables both the change watcher and the self-audit

Browser bridge

The panel talks to the host over /dsh-taskboard/*. Requests are accepted only from loopback / same-origin callers, and every POST must carry x-taskboard: mutate. Browser mutations are attributed to human in the task log.

Development

npm install
npm run build       # esbuild → lib/index.js + lib/client.js
npm test            # node native runner: smoke, store, client, cli, watch, protocol
npm run typecheck   # tsc --noEmit

Zero runtime dependencies. The browser bundle requires only the shell-seeded react / react/jsx-runtime.

License

MIT

A proactive channel for reviewers
submit
msg9 send
msg9
and
msg9
all degrade to printing only; the submit still succeeds
submitted
stale
--json.review_owed

Why: nine cards sat in review while the reviewer reported "nothing pending for me" — his cwd was a different workspace and he was looking at "assigned to me" rather than "I owe a review". Nothing ever told him he had been named as reviewer.

  • Read-side fallback for wait intervals (T-60): interval pairing only recognised unblocked, so waits ended implicitly stayed open forever — the series said "still waiting" while the current value said "not waiting", on the same screen. The other terminal events (submit/approve/reject/done/closed/reopened) are now recognised. Metric note: the stats page's "blocked" number moves as historical waits are included — the direction the owner approved.
  • waiting_on.who
    must not render as claimable
    all 76 client tests green
    holderTitle()
  • Two semantic touches: after start, if a wait is still on, the outcome column says 「the wait is still on ({who})」 (the host genuinely does not clear waiting_on); the drawer deliberately does not gain stop/block (neither moves a column, so the button would appear to do nothing) — the docs give the exact CLI instead.
  • The stats page's "blocked" metric changed meaning (owner's call): blockedAt now reconstructs wait intervals by pairing blocked → unblocked in the log (falling back to waiting_on.since on older boards; two consecutive blocked events simply produce overlapping intervals, which some handles) ⇒ the number goes up, which is the point: "how long was this card stuck" is finally answerable, instead of historical waits being invisible.
  • ⋯
  • fixed along the way: deleting the styles table also deleted a key, which at 720px in English dropped the view group out of flex onto a second row.
  • cardSep
    reviewerBadge
    cardWaitAge
    every action is always listed
    outcome
    Close → moves to 「settled」 · reopenable
    greyed out with the reason
    lists only present actors by default
  • Two dead ends closed along the way: pressing "Start" on a pool card used to create an ownerless "In progress"; a waiting card's only exit in the drawer was "Claim", which always fails (now "Release wait").
  • An unnamed wait (waiting_on.who empty) is no longer rendered as claimable (both on the card and in the stats) — the store refuses to claim a waiting card, so that used to be an entry point that always failed.