DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

@linxin666/dsh-client-ui-task-board

Client Ui Task Board

面向 DSH Web GUI 的主机权威任务面板,支持实际会话执行、主机 cron 调度以及可选的跨平台空闲睡眠保护;以挂载方式提供,无需修改 DSH 源代码。

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

npx -y @deepseek-ai/dsh plugin --profile web add @linxin666/dsh-client-ui-task-board@0.4.14
README兼容性版本

兼容性与来源证明

Client Ui Task Board 以 @linxin666/dsh-client-ui-task-board 发布,当前版本为 0.4.14。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

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

版本

0.4.14stable
2026/11/2
0.4.13stable
2026/10/31
0.4.12stable
2026/10/29
查看其余 62 个版本收起版本
0.4.11stable
2026/10/27
0.4.10stable
2026/10/26
0.4.9stable
2026/10/26
0.4.8stable
2026/10/20
0.4.7stable
2026/10/17
0.4.6stable
2026/10/10
0.4.5stable
2026/10/5
0.4.4stable
2026/9/29
0.4.3stable
2026/9/26
0.4.2stable
2026/9/24
0.4.1stable
2026/9/23
0.4.0stable
2026/9/23
0.3.24stable
2026/9/20
0.3.23stable
2026/9/16
0.3.22stable
2026/9/13
0.3.21stable
2026/9/12
0.3.20stable
2026/9/10
0.3.19stable
2026/9/9
最新版
0.4.14
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
4.6 MB
文件数
213
Surface
web
许可证
Apache-2.0
发布源
npm
GitHub
★ 10.1k
周下载
35,372
安全扫描
✓ v0.4.14 扫描通过
最近提交
2026/11/4
查看源码 ↗
README Badge

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

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

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

认领这个 Plugin →
报告问题
0.3.18stable
2026/9/8
0.3.17stable
2026/9/7
0.3.16stable
2026/9/5
0.3.15stable
2026/9/5
0.3.14stable
2026/9/3
0.3.13stable
2026/9/3
0.3.12stable
2026/9/2
0.3.11stable
2026/9/1
0.3.10stable
2026/8/31
0.3.9stable
2026/8/30
0.3.6stable
2026/8/27
0.3.5stable
2026/8/26
0.3.4stable
2026/8/25
0.3.3stable
2026/8/24
0.3.2stable
2026/8/23
0.3.1stable
2026/8/23
0.3.0stable
2026/8/23
0.2.9stable
2026/8/22
0.2.8stable
2026/8/22
0.2.7stable
2026/8/21
0.2.6stable
2026/8/21
0.2.5stable
2026/8/20
0.2.4stable
2026/8/19
0.2.3stable
2026/8/19
0.2.2stable
2026/8/19
0.2.1stable
2026/8/18
0.2.0stable
2026/8/18
0.1.20stable
2026/8/17
0.1.19stable
2026/8/16
0.1.18stable
2026/8/16
0.1.17stable
2026/8/16
0.1.16stable
2026/8/15
0.1.15stable
2026/8/15
0.1.14stable
2026/8/15
0.1.13stable
2026/8/15
0.1.12stable
2026/8/14
0.1.11stable
2026/8/14
0.1.10stable
2026/8/14
0.1.9stable
2026/8/14
0.1.8stable
2026/8/14
0.1.7stable
2026/8/14
0.1.6stable
2026/8/14
0.1.3stable
2026/8/13
0.1.2stable
2026/8/13

README

dsh-task-board · Autonomous Task Board & Cron Scheduling for DeepSeek Harness (DSH)

English | 中文

   

Autonomous Kanban Task Board & Cron Scheduling for DeepSeek Harness (DSH)
Host-Authoritative Ledger · Parent-Card Subtask Dispatch · Double-Blind Verification · Sleep Inhibition · GitHub Issue-to-PR

A hot-pluggable DeepSeek Harness (DSH) Web GUI and official desktop client task automation plugin with a Host-authoritative task ledger, real DSH agent session execution, cron background scheduling, cross-platform idle-sleep protection, parent-card subtask dispatch, and automated verification quality gates. It executes unattended background workflows including automated code reviews, routine health checks, and scheduled pipelines without requiring an open browser window. It mounts cleanly via cordis.patch.yml and profiles without modifying DSH core code.

  • Host-authoritative architecture and offline background execution: The browser is merely an asynchronous view; closing tabs or dropping connections will not interrupt Host scheduling, session execution, or atomic ledger settlement. Built-in cross-platform sleep inhibition (macOS, Windows, Linux) allows screens to power down while preventing system idle sleep for reliable unattended automation.
  • Parent-card dispatch over subtask trees: a task can carry subtasks, one to three levels deep as the deployment allows. Running a card opens one session, that card's own: the Host opens nothing for its subtasks, and that session reads them with task_board_get, dispatches them with subagent in dependency order, and writes their conclusions back with task_board_manage before it finishes. A card whose subtasks are still unfinished runs under the liangshen dispatcher preset.
  • Double-blind adversarial judge quality gates (No False Completion): Intercepts false agent completion claims; requires verification by an independent Judge Model using strict coding criteria, a 0.65 threshold, two-round A/B position swapping, and validation against real workspace filesystem diffs, returning actionable diagnostics for self-repair.
  • Automated GitHub loop, freeze handover, and agent tool surface: Real-time bidirectional GitHub Issues sync automates agent bug fixing and PR creation; supports <<<FREEZE session context snapshots with handover packages; exposes 8 native model tools for autonomous planning and external execution registration.

Features

Cards keep multi-line title and description previews with visible metadata. Previews strip Markdown formatting; task details render description and prompt as safe Markdown without raw HTML or automatic remote images, and content taller than about eight rendered lines is clamped to a fixed height with a fade hint and a "Show more" toggle that expands and collapses it, while shorter content renders untouched with no toggle. Stored task text is unchanged. A bracketed title prefix such as [Bug]: renders as a type badge, and the description preview skips issue-form boilerplate (checkbox lists, section headings, empty-field placeholders), preferring a summary section when one exists. A left stripe shows the latest run (running, succeeded, failed, cancelled); a card whose column disagrees with that run is labelled as set by hand. Each column shows the time its cards differ by — created in Backlog/To Do, started while running, settled in Done/Failed — read in the Host time zone. The latest execution session opens straight from the card. Every column header toggles compact single-line cards (Done starts compact, the choice is stored in the browser). All five columns and the archive first group cards by provider source — full owner/repository labels for GitHub — with unclaimed cards in Local tasks; source groups sort by label and their fold choices persist per browser. Done source groups start collapsed while the other columns and the archive start expanded, and search always reveals matching groups. Done, Failed and the archive nest Today / Last 7 days / Earlier within each source group and fold the oldest group once that source holds more than 30 cards, unless a search is active.

  • Task board UI: the board is the shell's own center-column panel — like the desktop application's Plugins page, it contributes a row to the shell's official sidebar panel list and a page to the layout's center-column seat, so row geometry, the active highlight, the collapsed rail, the label, and language switching are the shell's to own; the board provides five kanban columns, search, task details, archive/restore, execution history, and links to execution transcripts. Every column is reachable by hand, by dragging a card or from the detail view's move menu: the move writes the column and no execution record, so Done or Failed declares work that finished (or failed) outside a tracked run and Running parks work that is under way without a session, while a card the runner is actually executing refuses every move until it settles. Archived tasks are read-only except for restore, delete, and transcript viewing, and cannot run manually or on schedule until restored.

  • One scheduling entry in the sidebar: while the board is enabled, the shell's official Automation tasks entry (the host ui-schedule page over the Host Schedule service) is hidden, so the sidebar offers one scheduling surface instead of two. The board's own master switch owns the state: switching the board off brings the official entry back, and the official page keeps working everywhere else it is still reachable — the entry is hidden, not uninstalled.

  • Detail layout and folds: the task detail is two columns on a wide viewport — the left holds the task itself (description, execution prompt, labels, subtasks, frozen snapshot, handover bundle) and the right holds how this run will run (execution settings, scheduled runs, execution history, status); a narrow viewport falls back to one column in the same order. Workspace, agent preset, permission, model, and reasoning effort stay visible, while the three card-level switches (continue in the same conversation, start with /goal, skip this task's acceptance) fold into one "Advanced" row that echoes the switches that are on while it is closed, so the card's shape is readable without expanding it. Scheduled runs and execution history each take one fold row: the schedule row says whether a rule is armed and when it next fires, the history row says how many runs there are and what the latest one did, and the list itself shows the five most recent runs until it is expanded; an acceptance report shows one verdict badge on its row and the verdict, judge route, per-criterion scores, and findings only once expanded. The permission gate and the current status never fold. The view uses three type sizes (title, body, meta), status and result badges share one filled slot scale, and every fold row and button carries a hit target of at least 44 pixels.

  • Continuation cards (data plane): a new task may paste a <<<FREEZE ... >>>FREEZE block from a session; it parses into a goal/progress/next snapshot persisted with the task (ledger v3). Cards carry a frozen badge, the detail view shows the full snapshot and freeze time, search covers snapshot text, and archive/restore matches plain tasks. The snapshot reuses the freeze security gate at the protocol layer: sensitive patterns become with a marker, slash-prefixed command lines reject the whole snapshot, and each field is capped at 8 KiB.

Architecture and protocol

The board view renders on first open and keeps its local view state when closed and reopened. Host synchronization, scheduling and execution remain active independently of the view.

  • src/index.ts mounts the Host service through the official @deepseek-ai/dsh-api-gateway, @deepseek-ai/dsh-workspace, and @deepseek-ai/dsh-host-webserver SDKs.
  • src/host-ledger.ts serializes actions and persists { schemaVersion: 6, revision, tasks, scheduler, recentRequests } through a temporary file plus atomic rename.
  • src/host-service.ts owns schedule firing (a single timer armed at the ledger's nearest target through the host's timer service, falling back to process timers when the host serves none), missed-trigger skipping, runner launch, restart reconciliation, and power reasons; the ledger itself owns the run budget, so a one-shot or an exhausted capped rule can never be opened a second time.
  • src/client/native-panel.tsx registers the board's sidebar row and center-column page (the official sidebar.panellist list seat and the keyed main seat) and maintains the center-column exclusivity protocol with the ssh panel.
  • src/client/host-api.ts imports legacy browser data once, submits idempotent actions, and treats Host snapshots as the only confirmed UI state.
  • Same-origin endpoints are GET /api/task-board/state, GET /api/task-board/events, and POST /api/task-board/action.
  • Every endpoint requires browser authentication. Under the board's own fence that means a browser same-origin marker: sec-fetch-site: same-origin, an Origin header, or the Host's dsh-auth-* browser-auth cookie. The last one is how the DSH Desktop shell reaches the board: it serves the Web GUI from dsh-app://app/ and forwards that page's requests itself, dropping Origin and sec-fetch-site and attaching the authority-bound cookie it redeemed from the Host's launch URL at startup. Direct access is restricted to the DSH loopback origin. A deployment DSH itself serves through a trusted reverse proxy or ingress is instead admitted by the Host's connection service, which applies its own Host/Origin fence and verifies the signed browser session before the board sees the request; an authenticated same-host proxy that DSH does not front must use an explicit Host allowlist and a server-injected token, and a deployment whose own tunnel dials the loopback upstream can instead turn on trustForwardedProxy (see Configuration), which admits that shape on the same-origin marker alone. POST requests additionally require JSON. Ordinary actions are limited to 64 KiB and import to 2 MiB. The action union has no command, executable path, shell text, or arbitrary argument field.

Agent tools

The board is operable from a conversation, not only from the GUI. Each tool drives the same Host ledger, so a card created in the GUI is immediately visible to agents and vice versa, and every gate the UI honors still holds. The surface follows the enabled switch: a disabled board answers no tool call, and a deployment whose runtime serves no tool registry still mounts the board with the GUI intact.

  • task_board_list lists cards with filters (column, parent, roots only, label, free-text query, archived) plus the board summary: revision, time zone, subtask depth limit, session-default permission, and column counts.
  • task_board_get reads one card in full: prompt, labels, parent link, direct subtasks, execution targets, schedule, permission-gate state, and the last ten execution attempts with their session ids, initiator, and outcome.
  • task_board_create creates a task or, with parentId, a subtask; execution targets left unset inherit the parent, and the deployment subtask-depth limit applies.
  • task_board_update edits content, labels, and execution targets; an empty string clears a target and an empty label array clears the labels.
  • task_board_set_parent links an existing card under a parent, or detaches it with an empty parent id.
  • task_board_run runs a card now (optionally re-running a settled one) and nothing else: the card gets its own session, the Host opens none for its subtasks, and a card with unfinished subtasks is told which of them its session must dispatch; it consumes real API quota and refuses an unconfirmed above-default permission with confirmation-required.
  • task_board_manage moves a card to any column — the move writes the column and never an execution record, so backlog/todo plan, done/failed declare work that finished outside a tracked run and running parks work that is under way without a session, while a card with an open execution refuses the move until it settles — archives or restores it, deletes it, or settles a running card the board can no longer observe (recording a cancelled verdict with the caller as the reason and returning the card to todo), with the Host's running-task and subtask guards.
  • task_board_schedule arms, changes, or disarms a card's schedule — a recurring cron rule (optionally capped by maxRuns; 0 clears the cap back to unlimited) or a one-shot at an epoch-millisecond at — including its IANA time zone (an empty timeZone clears it back to the Host zone).

There is deliberately no tool that confirms a permission binding: that gate exists so a human lifts an above-default permission, and an agent able to stamp it would make the gate decorative. An agent that hits confirmation-required asks the user to confirm the card in the board UI. Tool calls are attributed: a run records the calling session as its initiator, and a create/update stamps it into a continuation card's snapshot.

Install

Install the aggregate package or this package alone, then restart dsh web:

dsh plugin --profile web add @linxin666/dsh-client-ui-task-board@latest

For local development:

git clone https://github.com/zhu1090093659/dsh-web.git
cd dsh-web
pnpm install
pnpm build
dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board

Configuration

This plugin's settings card groups its options into collapsible sections — the board and its runtime behavior, task acceptance, new-card defaults, and the section a provider extension contributes (the GitHub Issues integration). Every section starts collapsed. A change takes effect as it is made: a pick is written at once, and a typed value once typing pauses, on Enter, or when the field is left; Escape puts the stored value back.

KeyDefaultBehavior
enabledtrueEnables the Host service and browser board.
announceToAgentfalseOpt-in: when true, adds the task-board guidance section to agent system prompts.
preventIdleSleepfalseHolds one system idle-sleep assertion while any DSH session runs, any schedule is enabled, or session state is unknown.
trustedProxyHosts[]Canonical host[:port] authorities accepted only through the authenticated loopback reverse-proxy path.
proxyTokenEnvDSH_TASK_BOARD_PROXY_TOKENEnvironment variable containing the reverse-proxy token; the token itself is never stored in plugin config.
trustForwardedProxyfalseAdmits a forwarded request that landed on this machine's loopback upstream — the shape your own reverse proxy or tunnel produces when it dials 127.0.0.1:<port> — on the browser same-origin marker alone. Off keeps every forwarded request remote (issue #1906); on still refuses an explicit sec-fetch-site: cross-site marker, and the token-allowlisted proxy path above is unchanged. Volatile: read at each request, so an edit binds the next one without a restart.
sessionDefaultPermissionfollow the HostExplicit baseline for the permission confirmation gate. Unset, the board follows the Host's own default permission preset (what new DSH sessions start at) and falls back to read-only when the deployment serves no permission catalog. A card whose effective permission (handover bundle or pin) is above the baseline requires a human confirmation before it may run; cron refuses unconfirmed cards.
maxSubtaskDepth1Subtask depth limit, 1 to 3. At 1 a task may carry one level of subtasks and a subtask cannot be given subtasks of its own. It decides only how deep the tree under one card can go: a run opens that card's own single session and dispatches its subtasks from there, so the depth never changes how many sessions one run opens.
sessionPollSeconds5How often, in seconds, the Host re-reads the DSH session roster while the board has something to reconcile: a running card or an open execution. An idle board with neither does not poll at all. Range 1 to 300; a shorter interval settles runs sooner and costs the Host more.

All four new-card defaults are blank by default and volatile: an edit takes effect without a restart, for the cards created afterwards and on a page that is already open (no reload is needed for the new-task dialog to offer the saved values), and a blank value pins nothing, so a deployment that configures none of them creates cards exactly as it does today. A new card takes each of its execution targets in one order — the explicit choice made in the new-task dialog wins over the values carried over from the card it was created from (a duplicate keeps its source's pins, a subtask keeps its parent's), which win over the board defaults — and the parent's permission is never carried over. None of the four lifts the permission gate: a default permission above the session baseline is only a prefill, and the card still waits for a human confirmation before it may run.

Direct browser access remains limited to the DSH loopback origin. A deployment that DSH already serves through its own trusted-proxy or ingress arrangement (for example dsh web --trusted-host dsh.example.internal behind TLS termination) needs no board-specific setting: the routes bridge the Host's connection service, so a request whose Host/Origin fence and signed dsh-auth-* browser session that service accepts is admitted on its verdict, while a request it declines still faces the board's own fence. This path grants nothing a browser without the Host-issued session could use, and it is independent of the settings below.

For a same-host authenticated reverse proxy, bind DSH Web to loopback, set trustedProxyHosts, place a high-entropy token in the environment variable selected by proxyTokenEnv, and configure the proxy to replace (not forward from the client) X-Dsh-Task-Board-Proxy-Token after it authenticates the request. The proxy Host must be allowlisted, and the browser Origin must have that same authority. Restart the Host after changing these composition-level proxy settings.

The alternative shape is a request an operator's own tunnel or reverse proxy forwarded to this machine. With trustForwardedProxy on, a request that carries a forwarding header and arrived on the loopback upstream (loopback socket and loopback Host) is admitted on the browser same-origin marker alone; an explicit sec-fetch-site: cross-site marker is still refused, and a request that came through a proxy that is not dialing this machine stays remote. The switch is volatile and read per request, so an edit binds the next request; leave it off unless you operate the hop yourself, because a proxy you do not control can forge those markers on your behalf.

On macOS the backend starts /usr/bin/caffeinate -i -w <host-pid> and never requests -d. On Windows it starts the absolute Windows PowerShell under SystemRoot with a fixed helper that requests only ES_CONTINUOUS | ES_SYSTEM_REQUIRED; it never requests ES_DISPLAY_REQUIRED, changes a power plan, or requires administrator privileges. On Linux it starts a systemd-logind idle block inhibitor only from /usr/bin/systemd-inhibit or /bin/systemd-inhibit; it does not request sleep, handle-lid-switch, or a display/screensaver inhibitor. A Linux host without systemd-logind reports unsupported or a visible error and does not start a desktop-specific fallback. Other platforms report unsupported.

Data storage and migration

  • The authoritative ledger file is $DSH_HOME/task-board/ledger-v2.json (the file name is historical); the current document schema is v7, and an older document (v2, v3, v4, v5 or v6) is migrated losslessly in place on the next Host start. New POSIX files use mode 0600; Windows inherits the user directory ACL.
  • v5 adds the per-execution acceptance block (verification). The migration deliberately does NOT stamp one, so an execution opened before v5 keeps settling on its historical verdict and no in-flight run is retroactively judged by a gate that was never armed for it. A malformed block is dropped (that execution is then treated as unverified — fail closed), and an imported execution record never carries one, because a fabricated pass record must not let imported work look accepted.
  • v6 adds the schedule plan type and the run budget, and v7 adds the card attachment metadata (attachments). Both are additive: a card written before the field existed keeps running exactly as it did, and a malformed attachment list is repaired entry by entry (unusable entries dropped, count and byte caps applied) instead of dropping the task row. The legacy browser import does NOT accept attachments: a v1 export never carried one, so a document claiming one is refused rather than let past the storage check.
  • A v2 to v3 migration failure (structurally invalid task rows) fails closed with an explicit error and keeps the original file untouched; it never restarts from an empty ledger silently. A corrupt or unsupported-schema file is moved to a collision-resistant ledger-v2.json.corrupt-* name and the Host starts with an empty ledger plus a visible scheduler error. The corrupt bytes are not overwritten.
  • On the first upgraded page load for an origin, dsh.taskBoard.v1 is imported by stable source and request ids. Tasks merge by id, strictly newer browser top-level fields win, equal timestamps keep Host fields, and execution records merge by execution id.
  • The most recent 256 request ids and SHA-256 action fingerprints are stored with the ledger, so a retried mutation remains idempotent after a Host restart without duplicating full action payloads.
  • An external-outcome execution record adds one optional boolean (external). A ledger written before it existed carries none, and the loader neither invents one nor treats an absent field as external, so old documents read back unchanged; a non-boolean value is dropped rather than trusted as provenance. The ledger stays at v5: the field is absent from every existing row, which is exactly the additive change the v5 migration already describes.
  • Task labels are an optional tags field on the task row ({ name, promptPrefix? }[]) and needed no schema bump: a v3 document without it loads unchanged, and a malformed list is repaired entry by entry (blanks, repeats, and over-long names dropped, count capped) rather than dropping the task row.

Security model

  • The plugin stays inside the existing DSH Web deployment and network boundary and emits no permissive CORS headers. State, action, and SSE routes share the same access fence; bare local command-line requests are not accepted as browser requests. When the deployment serves the Host's connection service, the fence admits exactly the requests that service already authenticated — its Host/Origin fence plus the signed dsh-auth-* browser session — and keeps its own loopback and token checks as the fallback for every other request. The fence otherwise refuses every forwarded request unless trustForwardedProxy is on, and even then only the forwarded shape that landed on this machine's loopback upstream and carries no cross-site marker.
  • All mutation payloads use a strict, versioned discriminated union; schedule-owned timestamps and execution outcomes cannot be written by the browser.
  • Workspace, preset, permission, cron, task status, and imported records are validated again on the Host.
  • A card's effective permission above the configured session default enters a pending-confirmation state: the Host refuses manual runs and cron triggers until a human confirms the exact binding, and changing the pinned permission or the handover bundle clears the confirmation (no confirm-then-swap escalation).
  • A task prompt is data sent to a DSH agent session. The protocol does not accept shell commands, PowerShell bodies, executable paths, or configurable helper arguments.
  • Task labels are client-asserted like the prompt itself and ride the same gated action channel: the protocol gate rejects a blank name, an unknown key, more than eight labels, and an over-long name or hint. A label's injected hint is delimiter-escaped (it cannot forge the continuation-card provenance markers) and is placed outside that provenance wrap.
  • Attachment metadata is client-asserted too, and the Host does not believe it: every attachment an action carries is read back from the official attachment storage before the ledger commits it (an image is compared field by field, by digest and by its stored display name, a file is streamed and its digest recomputed), and a reference the storage cannot prove refuses the action. The upload route measures the ACTUAL request stream and refuses a body past the limit — the refused upload never reaches the ledger, and the storage SDK does not guarantee that a partially written backend object is cleaned up, so the board promises no such cleanup. The image read route serves an object only when a card of this board references it, so an unreferenced identifier is not readable.
  • The subtask lineage is validated on the Host, not in the browser: the parent must exist and be on board, attaching a task under one of its own descendants is refused, and the resulting depth must stay within maxSubtaskDepth. The permission gate judges the binding the card being run resolves on its own: a subtask's own unconfirmed elevated pin no longer blocks its parent, because the parent opens no session for it, while a subtask that runs on its own resolves an inherited binding down the ancestor chain, and the inherited binding carries that ancestor's human confirmation. The binding is never copied onto the card, so detaching a subtask cannot leave a confirmed elevated task behind.

Build and test

Node 20 or newer and the official NPM SDK packages are required; no DSH source checkout is used.

pnpm --filter @linxin666/dsh-client-ui-task-board typecheck
pnpm --filter @linxin666/dsh-client-ui-task-board test
pnpm --filter @linxin666/dsh-client-ui-task-board build

Set DSH_POWER_SMOKE=1 to opt into the native helper smoke test on Windows, macOS, or Linux. It starts the fixed helper, waits for readiness, releases it in cleanup, and confirms process exit without changing the system power plan. Linux first probes systemd-logind with a bounded timeout; without a usable system bus the native portion is skipped while pure logic tests remain available.

Manual verification

  1. Mount the package, restart dsh web, open the task board, and confirm the Host time zone and power status are visible.
  2. Create and edit a task; refresh or open a second same-origin tab and confirm both show the same Host revision.
  3. Run a task with pinned workspace, preset, and permission; confirm a new session appears and the task settles from its turn/end history.
  4. Run a task with the /goal option checked: confirm the session starts a goal and the card stays in running across continuation rounds until the goal completes, then settles as succeeded. Uncheck the option and confirm the next run is a single turn that settles from one turn/end.
  5. Enable a near-future cron, close all browser pages, and confirm the Host still creates and settles exactly one execution. Then arm a one-shot at a near-future instant and confirm exactly one execution is created and the rule reads as ended; set a recurring rule's run limit to 2 and confirm the third occurrence creates nothing.
  6. Stop the Host past a cron occurrence, restart it, and confirm the missed occurrence is skipped and nextRunAt rolls forward from current Host time.
  7. Enable preventIdleSleep, run a long session, and let the display turn off; after restoring the display, confirm the session continued and the execution settled.
  8. Disable the setting and all schedules, stop DSH, and confirm the helper exits; on macOS, pmset -g assertions should show no display-sleep assertion from this plugin.
  9. On Linux, use systemd-inhibit --list to confirm that only an idle/block entry exists; the display should still follow desktop settings, while manual sleep and lid close remain under system policy.

Known limitations

  • Missed occurrences during Host downtime, system sleep, or a long pause are skipped and never queued for catch-up.

  • A task that is already running skips its due occurrence and rolls to the next cron match; task runs never overlap or queue. A one-shot has no next match: an instant missed because the card was running or the Host was down stops the rule as skipped, and is never replayed.

  • A capped rule counts the executions the scheduler opened, and a manual run never spends the scheduled budget. A launch that fails before any session exists is refunded, so the rule keeps its next occurrence; a run that did reach a session counts even if it then failed.

  • DST follows the Host local wall clock: a nonexistent spring-forward minute is skipped, and a repeated fall-back minute is not replayed a second time.

  • Power protection prevents only idle system sleep. It deliberately allows display sleep and lock.

  • Lid close, manual sleep, hibernation, shutdown, low-battery forced sleep, and enterprise power policy are outside the guarantee.

  • The plugin does not schedule wake timers and cannot wake a computer that is already asleep.

  • Linux requires systemd-logind and policy permission for the current user to acquire an idle block lock. Containers, WSL, hosts without a system bus, and non-systemd systems may report unsupported or error. Whether a desktop also associates a logind idle lock with display idleness is desktop policy; the plugin does not request a screensaver or display inhibitor.

  • Keeping enabled schedules armed may increase battery consumption because protection starts before their future trigger time.

  • Host execution consumes the same API quota as an ordinary DSH agent session.

  • Running a card opens one session, its own, and that session dispatches the unfinished subtasks with subagents inside it: a card with many subtasks consumes the quota of every subagent it dispatches, while the board itself opens nothing extra. A subtask whose conclusion is never written back inherits the parent run's verdict, which records the parent's judgement rather than a separate execution of that card.

  • Agent tool calls are model-driven: a run or a scheduled execution started from a conversation consumes the same API quota, and an agent can arm a schedule that keeps firing until it is disarmed or the board is switched off.

  • A goal run keeps working for as many rounds as the objective needs: every round consumes API quota, and the session stays busy until the goal completes or dsh blocks it (round limit, a rejected round, or a failed queue), which the board reports as a failed execution.

Telemetry

The browser half sends one anonymous install heartbeat per UTC day to dsh-market.com: a random localStorage id plus this package's name, nothing else. The server stores only a salted hash of that id, never IP addresses, and exposes aggregate counts only. See docs/telemetry.md for the full contract.

[REDACTED]
  • Handover bundles and the permission confirmation gate: a continuation card may attach a handover bundle — the pinned execution triplet (workspace / agent preset / permission) plus doc/script references. The bundle's triplet overrides the plain pin fields at execution, and the references ride the prompt as a handover preamble. A binding whose effective permission is above the deployment's permission baseline — the Host's own default permission preset unless the profile pins sessionDefaultPermission — is unconfirmed: manual run refuses, cron skips the card and rolls to the next occurrence, and the confirm button in the task detail resolves the binding; any later permission or bundle change re-arms the gate.

  • Claim provenance wrap and source audit: executing a continuation card (a card with a frozen snapshot) mandatorily wraps the task instruction in a source-declaration template — freeze instant, source session, and an unreviewed-content warning — composed after the handover preamble so the picking-up agent stays wary of stored prompt injection in card text. The session issuing a create/update action is stamped into the snapshot (frozenBy, re-stamped when the snapshot is replaced), and the session issuing a run/rerun lands on the execution record (initiatedBy) together with a captured copy of the freeze provenance; both are visible in the task detail. The initiator is client-asserted audit metadata, not a trust boundary.

  • Task tags (issue #1521): a task may carry up to eight labels. A label renders as a colour-toned badge on the card — the tone is hashed from the name, so one label always paints the same way and no colour is stored — the board header gains a multi-select tag filter built from every label in use (including archived tasks), and search also matches label names. A label with an "execution hint" is injected ahead of the execution prompt on every run as a 标签提示 block; a label without one is display and filter only, so an untagged task's prompt is byte-for-byte what it was before the feature. Labels stay editable after the first run: they classify the task and shape the next run, they are not the record of what already ran. A Manage labels action on the filter row opens the label manager: every label in use with the number of tasks carrying it, a rename that rewrites the label on every card (merging into an existing label of that name and keeping that label's execution hint), and a delete that removes it from every task including archived ones. Both are ledger-wide Host transactions, so several windows looking at one board stay consistent.

  • Label cleanup: a Cleanup row in the label manager takes a threshold (5 by default, 1 to 99) and removes the labels the board has stopped using. A label is a candidate when fewer than that many tasks carry it and every one of those tasks is already done or failed; archived cards count on the same terms, so an archived card that settled lets its label go while a card that is archived and still unfinished keeps it. Cleanup works out its candidates before it writes anything: with none it only says so and leaves the ledger alone, and with some it opens the confirm dialog listing each label with its task count, after which each of them goes through the same ledger-wide delete the manager's own delete button uses. The label leaves every card including archived ones, its execution hint goes with it, and no task is otherwise touched; the removal cannot be undone.

  • Subtasks and parent-card dispatch: a task can be given subtasks, either by creating one from its detail view (the form starts from the parent's workspace, preset, permission, and model, and each stays overridable) or by linking an existing task that has no parent yet; a subtask can be detached again once it is not running. The lineage gate lives in the Host: the parent must exist and be on board, a task can never be attached under one of its own descendants, and a tree may not exceed the configured maxSubtaskDepth (1 by default, 3 maximum). Running a card opens one session for that card and none for its subtasks, and running a subtask directly is the same operation: that card is the root of the run even when it carries a parent and subtasks of its own, and nothing under it is expanded. A subtask already in the done column is outside the run's dispatch set, so finishing subtasks one by one and then running the parent costs one session instead of a repeat of the tree; move it back to the todo column to have it dispatched again. The board marks parent and subtask cards, the detail view lists the direct subtasks and says what one run will do, and the header offers a one-click hide-subtasks filter.

  • Parent-card dispatch and subtask accounting: clicking a card that carries unfinished subtasks opens that card's own session and nothing else. The run is forced under the liangshen dispatcher preset shipped by packages/dsh-liangshen, the card's stored agent preset is never rewritten, and session reuse is skipped so the run mints a fresh session; a deployment that registers no liangshen preset fails the run closed, with that missing preset as the reason on the card. The prompt names every unfinished subtask with its title, task id, and status, states that no session is opened for them, and asks the session to read each card with task_board_get, dispatch it with subagent in dependency order (independent ones may run together), and write each conclusion back with task_board_manage({action:'record-external-outcome'}) before it finishes. A conclusion the session never wrote back is supplied by the Host when the card's own run ends: every still-unrecorded unfinished subtask inherits that verdict as an external record whose initiator reads parent-run:<card id> and whose reason says the card ran no session of its own and inherited the parent run's conclusion. The backfill is idempotent, and a cancelled parent writes nothing. A card with no unfinished subtask is unaffected.

  • Project partition (issue #1536): the board header offers a project row built from the deployment's DSH workspaces — "all projects" plus one entry per registered project. Selecting a project narrows the columns to the tasks pinned to it, and tasks with no pinned workspace stay visible under "all projects". Opening the new-task form while a project is open preselects that project as the task's workspace, and "new project…" registers a host directory through the same runtime call the GUI's own add-project uses. A root task created without a project choice — from the form or through the board's agent tool — inherits the workspace of the session that created it, and a card that still pins nothing runs in the deployment's most recently used workspace instead of the Host's own working directory.

  • AI parse of pasted text (issue #1540): the new-task form takes text copied from anywhere and has a model turn it into the title, description, and run prompt. The model is one of the deployment's configured models (the same list the task's model pin uses, first entry preselected), and the call runs on the Host behind the board's usual loopback and same-origin fence, with a 45 s budget and a cancel affordance. The draft only fills the form: nothing is created until the task is submitted, and a failed parse leaves what you already typed untouched.

  • Collapsible task form: the create/duplicate/subtask dialog groups its configuration into collapsible regions — task content open by default, then labels, execution settings, run mode, handover, and scheduled runs — and every collapsed region keeps a one-line summary of the values it holds, so the dialog fits without a scrollbar (its body keeps an overflow fallback for very short windows). The agent-preset field is named "Agent preset" and lists the deployment's built-in presets under localized names plus any user preset, with an inherit default that names the preset a run without a pin actually uses.

  • Card attachments (issue #1979): a card may carry up to eight files and images. The create and edit forms accept them three ways — drag and drop, paste, and the file picker — and a picked file stays a LOCAL preview until the card is saved: the upload happens on save, so a card the user abandons leaves no stored object behind. The drop area is focusable and keyboard-operable, and the removal controls are disabled while a save is in flight. An image is stored as a raster image and travels with the execution as an image part of the prompt, placed after the text part; any other file is stored verbatim and reaches the execution session as a readable host path in a read-only attachment section of its own, separate from the handover references. The card keeps METADATA only — the kind, the stored identifier, the name, the size, the media type, the digest of the exact stored bytes, and the pixel size of an image — never the bytes, never base64, never a data URL. The display name is always one canonical line without any path part, so it can neither break the prompt nor point at a directory. Before the ledger commits an attachment, the Host proves the claim against the official attachment storage: an image is read back and compared, a file is streamed and its digest recomputed, and a reference the storage cannot prove refuses the whole action, so a spoofed client claim never becomes a stored fact. Every execution resolves the attachments afresh, and a route that declares it cannot read images refuses the run before the prompt is queued. The board's limits are eight attachments per card, 10 MiB per attachment and 32 MiB per card; a deployment whose own attachment storage is stricter — including its per-message image budget — lowers the published limit, and the Host enforces that budget both when it writes the card and when the execution resolves it. One stored object is claimed once: the same content never appears twice on a card.

  • Host-authoritative ledger: tasks, schedules, and execution records live in $DSH_HOME/task-board/ledger-v2.json; browser actions become confirmed Host transactions.

  • Bounded execution history: each task keeps the most recent 20 execution records; the oldest runs are trimmed when a new run starts, so ledger size and write cost stay bounded regardless of how often a task has run.

  • Real execution: manual and scheduled runs use the same Host runner, which by default creates a fresh session, renames it, applies the agent preset and /permission <id>, then queues the task prompt.

  • Goal-driven runs (opt-in per card, on top of the global switch): the GLOBAL native /goal switch (below, off by default) is the master control; with it on, and only when a card opts in through its detail-view checkbox (the new-task dialog starts unchecked), the runner queues the task prompt and then arms dsh's built-in /goal with that same composed prompt as the objective. dsh's goal-round driver keeps starting continuation rounds in the session until the agent marks the goal complete, and the board keeps the execution in running until the goal leaves its active phase: a completed goal settles the run as succeeded, a blocked one fails it with the goal's own reason. The option is per task (goalRun, absent means off, so a card that never touched it runs one plain turn) and is available to task_board_create/task_board_update too. The checkbox is always operable: the master switch decides whether the preference takes effect, never whether it can be stated, and it never rewrites the stored value.

  • Goal acceptance (on by default): a task executed in goal form must pass ONE acceptance before update_goal(action: complete) may take effect, because the board gates that call on the official tool pre-execute lifecycle instead of trusting the agent's own report. Acceptance runs the default final acceptance of the installed dsh-llm-verifier (MIT): the three coding criteria (Specification Adherence, Output Match, Error Signal Detection), a 0.65 threshold applied to the total AND to every criterion, two judge rounds per criterion with the A/B slots swapped and averaged, the task's real trajectory (tool calls, their output, assistant prose) judged against the empty-work baseline, the work must beat that baseline, and — when the deployment records workspace changes — the HOST's own record of what the run changed on disk (file list with added/deleted counts and bounded per-file comparisons) is attached as the prompt's reference context so a described-but-unapplied patch cannot pass. A veto is booked as a quality verdict only when it can be LOCATED: every criterion that makes the run fail must arrive with a structured finding naming the requirement it imposes, the observation that falls short, the difference, and a verbatim citation of the task, the work's own trajectory or the host's workspace record. A rejecting answer with no finding, a generic or empty finding, a citation that does not occur in the reviewed evidence, a citation of the empty-work baseline, and a criterion failed without its own evidence are recorded as an INVALID acceptance instead: they spend no quality budget, do not fail the card, and are bounded separately (two), after which the board stops calling the judge and holds the card for a human. A run whose only records are invalid ends for lack of a matching pass, but with its own terminal reason that states the acceptance was unusable and explicitly denies being an evidence-backed quality veto. A judge that could not see the output because the trace was truncated is treated the same way, because insufficient evidence is not a failure of the work. One execution may accept twice: the first failure returns the total, the per-criterion scores and the located findings to the fixing agent, the second failure ends that execution as failed. Once an execution PASSES and settles, the acceptance-only detail (its findings and structured citations) is removed automatically and only a lightweight audit credential remains — verdict, scores, time, judge route, evidence hash and a one-line problem summary. Nothing outside that execution's own acceptance record is touched, a failed or invalid execution keeps everything a repair or a review needs, the removal is idempotent, and a cleanup that fails is recorded and retried on the next Host start without ever revoking the pass. The budget lives on the execution record, so a repeated completion call, a new goal round, a plugin reload and a Host restart all reuse the same cycle, while a rerun or a scheduled occurrence is a new execution with its own budget. An anomaly (timeout, authentication failure, unparseable judge answer, unresolvable judge route) is recorded separately from a quality verdict and never consumes the quality budget. Exhausting the anomaly budget does NOT fail the card: a judge route that cannot answer is the environment failing, not the work, so the cycle is held open with no further judge call until a user clears the recorded anomalies through task_board_manage(action: reset-verification). An attempt that the acceptance time budget ends before any verdict is recorded as its own stage, spends no budget at all and also fails nothing. Both ceilings are settings (goalVerificationCallTimeoutSeconds, goalVerificationBudgetSeconds), read live, and a judge call is never opened when the time left cannot hold it. The running column shows executing / verifying / fixing, and each execution's row carries its verdict, judge model and level, count, scores with thresholds, findings, evidence scope and token usage. Forced acceptance also keeps the old fallback paths honest: a paused goal, an unreadable projection, a completed turn and a manual settle can no longer settle a goal execution as succeeded without a matching pass record. A failure carrying acceptance records means the judge answered and the work did not pass, while a failure with no acceptance record at all is reported as its own reason: the gate never opened because the session never called update_goal(action: complete) — often a session that narrated completion in prose — so nothing was ever judged and the delivery was never rejected. A single card can opt out of the gate: the new-task form and the task detail both carry a Skip acceptance checkbox (off by default, a card-level option like Continue in the same conversation and Start with /goal), and a checked card no longer intercepts its completion claim in goal form. The execution records applicability: skipped and the report says the CARD skipped acceptance, which is a different fact from the board-wide switch being off and is reported as such. It applies only to executions started afterwards, so flipping it mid-run never rewrites the rule judging a run already in flight.

  • Task reasoning effort: the task model picker exposes a reasoning-effort selector when the selected model declares supported levels in the Host model catalog. Leaving it blank inherits the model/Host default; only that model's declared levels are offered. The task stores its choice and applies it when selecting the execution model, including a reused session. This setting is separate from the acceptance judge's reasoning effort.

  • Optional session reuse: a task can opt into continuing in its previous execution's session (issue #1419). Reuse happens only when that session is idle and still present in the runtime roster — the Host then re-applies the pinned permission and model on it and queues the prompt, keeping the conversation title and history; otherwise the run mints a fresh session as before, so an unknown roster or a busy session never blocks a scheduled run. The roster read is taken for the launch itself, so a board that has been idle reuses a session that has been idle all along instead of refusing it.

  • Idle-aware session polling (issue #1821): the Host reads the DSH session roster only while the board has something that roster can decide — an execution to inspect, or a card parked in the running column whose verdict may arrive from a settle this process never saw. An empty board with no running card, no open execution and no enabled schedule polls nothing at all, so an idle Host no longer rebuilds every persisted session row (string conversion, object allocation, one stat per record) on a fixed heartbeat. The cadence that governs a busy board is the sessionPollSeconds setting, and a roster read that fails or stays unknown backs off on a doubling delay up to one minute, so a session tree that is down is not hammered at the poll interval.

  • Fail-closed pins: a missing workspace, missing or broken preset, or rejected permission command fails before the task prompt is sent.

  • Host scheduler: 5-field cron supports *, */n, ranges, comma lists, and Sunday 0/7. Each rule carries its own IANA time zone (the Host's zone by default), and day-of-month/day-of-week follow Vixie semantics: both fields restricted means OR, any other combination means AND. Wall-clock gaps at a daylight-saving transition are skipped, and an ambiguous repeated time fires once, at the earlier instant. A rule is either a recurring cron rule — optionally capped by a run limit (unlimited, 1, 2, or any positive whole number) — or a one-shot pinned to a single future instant; the Host counts the executions it actually opens, and a rule that reaches its cap or fires its one-shot stops itself in the ledger rather than waiting for the executed work to disarm it.

  • Deterministic recovery: a running execution with a recorded session is observed after restart; an interrupted start without a session id is cancelled and is not resent.

  • Live synchronization: mutations return a full revisioned snapshot; SSE announces revision, scheduler, and power changes. The mirror has five re-read entry points: change frames (revision advancing), connection establishment or re-establishment (EventSource.onopen), page visibility recovery, stream errors (EventSource.onerror, throttled by STREAM_ERROR_NOTIFY_MS), and a bounded verification read while the mirror still has unsettled executions (src/client/pending-verify.ts, PENDING_VERIFICATION_MS 15 seconds, stopped as soon as the last execution settles). A connection can stay open yet stop delivering, in which case there is neither an onerror nor a frame, so frames cannot be verified from the browser side. An idle board with no unsettled executions performs zero reads.

  • Optional idle-sleep protection: off by default; when enabled it covers every running DSH session, enabled non-archived task-board schedules, and unknown session state.

  • System-prompt injection: the Host registers a plugin:task-board section (order 200) through SystemPrompt.section, and the task-board settings can disable the announcement without disabling the board. The guidance also reminds agents to close any visible todo_write plan before the final answer.

  • Agent tools: every session gets eight model-facing tools (task_board_list, task_board_get, task_board_create, task_board_update, task_board_set_parent, task_board_run, task_board_manage, task_board_schedule) that drive the same Host ledger the browser drives, so an agent can list the board, create subtasks, link or detach them, start a run, move a card to any column (declaring it done, failed, or in progress without a run), archive/restore/delete it, settle a card the board can no longer observe, and arm its cron schedule from the conversation.

  • External outcomes (issue #1826): work that is not executed by this Host — an agent outside DSH such as Codex or Claude Code, running its own model — can be recorded as a real run rather than a bare column edit. task_board_manage(action: 'record-external-outcome', result: 'succeeded'|'failed', initiatedBy: <who>) appends an execution record carrying the verdict, the caller and an optional summary, and the terminal column is derived from that same record by the same rule a Host settlement uses (core/tasks.ts settledStatus). The record is marked external, never carries a session, and is refused while the card still has an open execution (while this Host is running the card it stays the only authority) and on an archived card. running and cancelled cannot be recorded this way: those stay Host-only. A manual move-done/move-failed remains available and still writes only the column — the difference is that this one leaves a record behind, so the column and the run history can never disagree.

  • External provider extensions: the GitHub Issues extension (@linxin666/dsh-client-ui-task-board-github, enabled by default) synchronizes GitHub Issues into board cards. It is a provider of THIS board, so its configuration renders inside this plugin's own settings card — the section only appears while the extension is installed, and its repository/credential form only while the extension is on; see its README.

  • runLaunchWidth3How many run launches may be in flight at once, 1 to 16. It only matters when several cards want to run at the same moment — a manual run, a re-run and a due occurrence alike: that many executions are admitted to create their session and queue their prompt, and the rest wait their turn, round-robin across runs and FIFO inside one. Running a single card opens one session, so this value never changes the shape of one run. The default follows this repository's measured guidance for heavy implementation subagents (at most three). Read at each admission, so a live edit bounds the launches that have not been admitted yet.
    spendBudgetTokens0Daily token ceiling for the work this board dispatches. 0 means no gate, and the board then reads no ledger and counts nothing at all. The unit is tokens because the usage ledger prices only DeepSeek's own routes and reports 0 for every other route. Measured against the family usage ledger's local-date bucket: a manual run or re-run whose day already spent the cap is refused, and the caller sees that message as its board-refused reason; a due schedule records the refusal on the scheduler error and skips the occurrence without spending a planned run, so raising the cap or the local day rolling over admits the next trigger. Read at each gate, so an edit binds the next run without a restart.
    goalRunEnabledfalseGLOBAL native /goal switch. While off, every card runs one plain turn and the board never arms dsh's built-in /goal; a run uses native /goal only when this is on AND the card's own Start with /goal option is checked (that option is off by default). The value is frozen when an execution starts, so a live edit only affects executions that begin afterwards, and a card's stored goalRun preference is never rewritten. Such a run is recorded as goal-disabled: it is neither reported as accepted nor failed for lacking a completion call. Independent of goalVerification.
    goalVerificationtrueGoal acceptance for executions this board starts. When on, update_goal(action: complete) inside a task execution is refused until an acceptance pass is recorded for that execution. Only goal-form executions are affected: plain chat and a card that did not opt into goalRun are untouched. A single card can opt out of the gate on its own with the Skip acceptance checkbox (below).
    goalVerificationModel'' (inherit)Judge model route as provider/model. Blank inherits the HOST model catalog default — never the card's pinned execution model.
    goalVerificationReasoningEffort'' (inherit)Judge reasoning level. Blank inherits the host default level; a level the resolved model does not declare is never sent, and the fallback to that model's own default is recorded and shown.
    goalVerificationCallTimeoutSeconds150Ceiling of ONE judge request of an acceptance, in seconds (range 30 to 600). One acceptance issues six judge requests, so a ceiling too small for the judge model turns a slow but healthy route into a wall of timeouts.
    goalVerificationBudgetSeconds1200Total ceiling of ONE acceptance attempt, in seconds (range 120 to 1800), covering evidence rendering and every judge call including retries. Keep it at or above the per-call ceiling times six or the acceptance can never finish; an attempt that runs out is recorded as a budget stop that judges nothing and spends no budget.
    cardDefaultMode'' (no default)Agent preset a newly created card starts from. Blank pins nothing, so the card follows the deployment default as before.
    cardDefaultPermission'' (no default)Permission a newly created card starts from. It may sit above the session permission baseline: it is a prefill, so an above-baseline binding still needs a human confirmation before the card runs.
    cardDefaultModel'' (no default)Execution model (provider/model) a newly created card starts from. Blank pins nothing and makes cardDefaultReasoningEffort inert.
    cardDefaultReasoningEffort'' (no default)Reasoning level a newly created card starts from, applied only while the card's final model is the configured cardDefaultModel; a caller that picks a model of its own borrows no default level.
  • The goal opt-in is an optional goalRun field on the task row and needed no schema bump either: absent means off, an explicit true is the opt-in, and a stray false is repaired back to absent on load.
  • The import marker dsh.taskBoard.v2.hostImported stores the confirmed Host ledger generation only after import succeeds. A new or recovered ledger generation is offered the retained v1 data again. The v1 localStorage value remains untouched as a read-only rollback copy.
  • One Host process owns a task-board ledger directory at a time through $DSH_HOME/task-board/ledger-v2.lock; a second Host using the same DSH home fails closed instead of concurrently writing the ledger.
  • The agent tool surface reaches the same Host ledger through the in-process service rather than the HTTP fence, and it carries no confirmation capability: refusals, fail-closed pins, the depth gate and the running-task locks all still apply, and no tool call can lift an above-default permission binding.
  • Power helpers use fixed executable paths, fixed arguments, shell: false, and bounded retry delays of 1, 2, 5, 10, then 30 seconds. The Linux helper follows the Host stdin lifetime so the systemd inhibitor is released automatically after an abnormal Host exit.
  • Goal mode needs the runtime's /goal command and a command dispatcher. A deployment that serves neither still runs every card as a single turn; the host log reports that the goal was unavailable instead of failing the run.

  • Acceptance spends model requests: one acceptance is three criteria times two rounds (six judge requests), and one execution may accept twice, so a goal cycle that needs its one repair costs up to twelve extra requests on top of the work itself.

  • No cost estimate is shown for an acceptance: this deployment exposes no reliable per-token price for the resolved judge route, so the report lists tokens only.

  • The host-observed workspace-change block appears only when the deployment records changes and the read succeeds; an unavailable service or a failed comparison degrades the evidence to the trajectory alone rather than failing the acceptance.

  • Acceptance is enforced only where it can be: a run that never became a goal run (/goal refused or unavailable, or the global switch off) is not gated, and its record says so explicitly instead of implying it was verified; a teammate record left by a historical team run carries no acceptance either. A global-switch run is marked goal-disabled — its own reason, distinct from a refused command — so it is never read as a passed acceptance either. A deployment that serves no model catalog cannot resolve a judge route, so a completion claim is refused and recorded as an acceptance anomaly; once the anomaly budget is spent the card is held open rather than failed, and recovering needs a user to clear the anomalies.

  • The first version always judges with the coding criteria: there is no automatic rubric selection, so a non-coding task is judged by engineering criteria (the settings copy states this).

  • If a third-party verifier (for example the installed dsh-llm-verifier) also enables its own automatic acceptance, both judges score the same session independently: this board's acceptance gates completion while the other only steers, and no public SDK interface lets them share one verdict. Turning that plugin's automatic mode off is the only reliable way to avoid paying for two judgments.

  • The official attachment storage has no deletion API. Removing an attachment from a card, or deleting the card itself, removes the METADATA only: the stored object stays in the deployment's attachment storage. The board neither promises a physical deletion nor works around one.

  • A non-image attachment reaches the execution session as a readable host path, so the session can only read the file when the deployment's attachment storage is host-file-backed. A deployment whose storage hands out no readable path fails the run with that reason instead of silently running the card without its attachment.

  • An image attachment needs a model route that accepts image input. The board asks the official model service about the route the run really uses (the card's pin, or the current route of a reused session); a route that declares its modalities and excludes images refuses the run before the prompt is queued. A route whose modalities cannot be read is treated as unknown and allowed, and a route the board cannot resolve at all skips this preflight — in both cases the official Host check remains the fallback.