zhu1090093659/dsh-web

packages/dsh-task-board

侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。

UI 強化cordisdeepseek-harnessdsh

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); Done, Failed and the archive group cards into Today / Last 7 days / Earlier and fold the oldest group once a column 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 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 [REDACTED] with a marker, slash-prefixed command lines reject the whole snapshot, and each field is capped at 8 KiB.
  • 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.
  • 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: 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.
  • 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 , 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 an