Skip to content

feat(desktop): add Kanban board UI over the existing kanban plugin API - #58137

Closed
rod-nxtlevel wants to merge 1 commit into
NousResearch:mainfrom
rod-nxtlevel:feat/desktop-kanban-board-ui
Closed

feat(desktop): add Kanban board UI over the existing kanban plugin API#58137
rod-nxtlevel wants to merge 1 commit into
NousResearch:mainfrom
rod-nxtlevel:feat/desktop-kanban-board-ui

Conversation

@rod-nxtlevel

Copy link
Copy Markdown
Contributor

What

Desktop currently has no surface for the kanban subsystem (the only reference is the /kanban slash command). This adds a native board UI as an overlay view (same pattern as CronView), built entirely on the existing /api/plugins/kanban/ REST surface — zero backend changes.

  • Board view: all 8 columns in BOARD_COLUMNS order, dnd-kit drag between allowed targets, card tiles with assignee/priority/progress/diagnostics/comment-count, board switcher, dispatcher-nudge button, new-card dialog.
  • Card drawer: full body, status/assignee/priority editors, comments (+composer), events feed, run history with worker-log preview, attachments, DAG parent/child links.
  • Server-authoritative by design: optimistic moves reconcile from the PATCH response; when board rules land a card somewhere other than the drop target (e.g. auto-review flows), the UI renders the server's placement with an info toast. 409s revert with the backend's detail. running is never a drag target (backend 400s a direct PATCH; ready→running is dispatcher-claim only).
  • HOME-scoped: kanban wrappers deliberately do NOT carry ?profile= — boards are shared across profiles per kanban_db.py's design; the board switcher is client-side only and leaves the CLI/gateway current-board pointer untouched.
  • Polling, not WS, in v1: 4s visibility-gated poll of GET /board using latest_event_id as a change cursor (atom identity preserved when unchanged). The view mounts inside its own ErrorBoundary.
  • parseKanbanApiError unwraps the Electron IPC error prefix so HTTP status/detail survive the hermes:api bridge (tests assert the exact wrapped shape seen in production).

Not in v1 (deliberate)

WS /events, attachment down/upload, bulk actions, tenant filter, board CRUD, specify/decompose/reclaim surfaces, diagnostics recovery-action buttons.

Tests

22 vitest cases: drag-target rules, move/revert/rule-bounce reconciliation, poll-cursor identity, 409/404 handling with IPC-wrapped errors, board-scoping, persistence, component smoke tests over a mocked hermes:api. Typecheck + lint clean. i18n: en + zh (+ types); ja/zh-hant fall back.

🤖 Generated with Claude Code

Board overlay (CronView pattern) over the existing /api/plugins/kanban
backend: 8 dnd-kit columns in BOARD_COLUMNS order, card drawer with
comments/events/runs/worker-log/attachments/DAG links, new-card dialog,
board switcher, dispatcher nudge.

- HOME-scoped by design: no profileScoped()/?profile= on kanban routes.
- Server-authoritative mutations: optimistic moves reconcile from the
  server response (rule bounces like done→review render where the server
  put them, with an info toast); 409s revert with the backend's detail.
- 'running' is never a drag target (backend 400s direct PATCH; ready→
  running only via dispatcher claim) — Nudge dispatcher instead.
- 4s visibility-gated polling with latest_event_id cursor (atom identity
  preserved when unchanged); overlay wrapped in its own ErrorBoundary.
- parseKanbanApiError unwraps the Electron IPC error prefix so HTTP
  status/detail survive the bridge.
- i18n en+zh (+types); ja/zh-hant fall back to English.
- 22 vitest cases (store rules, move/revert/bounce, cursor identity,
  component smoke via mocked hermes:api).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/feature New feature or request comp/desktop Electron desktop app (apps/desktop/*) comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have labels Jul 4, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment (large surface area)

Feature PR (+2632/-6, 20 files) adding a Kanban board UI over the existing kanban plugin API. Substantial addition but appears well-scoped to the kanban feature area.

Looks Good

  • Desktop UI layer for existing kanban API
  • Appears to use existing plugin interfaces properly

Reviewed by Hermes Agent

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Kanban board UI over the existing kanban plugin API. 20 files, clean new feature. TODOs in test files are intentional placeholders. No security concerns.


Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for building this against the existing Kanban plugin API. The native Desktop Kanban premise still holds on current main: apps/desktop/src/app/routes.ts:1-69 has no Kanban route/view, while the plugin API supplies the required board endpoints at plugins/kanban/dashboard/plugin_api.py:378-510.

Problems

  • apps/desktop/src/store/kanban.ts:104-106 applies an awaited board response without checking whether the selected board changed. setKanbanActiveBoard() clears board state at lines 47-60, so a late response for the prior board can repopulate the atom after a selection switch.
  • apps/desktop/src/store/kanban.ts:200,215 reads the active board after an optimistic move begins and unconditionally restores the old snapshot on failure. Switching boards during that await can send the PATCH to the new board and/or render the old board snapshot there.

Suggested changes

  • Capture board identity plus a request generation before GET/PATCH, and ignore stale completions and rollbacks.
  • Cover board switching during pending GET and pending successful/failed PATCH calls with deferred-promise tests.

This is an automated hermes-sweeper review.

$kanbanRefreshing.set(true)

try {
const next = await getKanbanBoard($kanbanActiveBoard.get())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capture the requested board slug and a selection/request generation before this await, then apply the response only if it is still current. setKanbanActiveBoard() clears the atom, but a late response from the old board can otherwise repopulate it after the user selects another board.

$kanbanBoard.set(applyKanbanCardMove(board, taskId, targetStatus))

try {
const response = await updateKanbanTask(taskId, { status: targetStatus }, $kanbanActiveBoard.get())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must use the board identity captured when the optimistic move began, not the current selection after an await. If the user switches boards here, the PATCH targets the new board and the catch path restores the old snapshot into that board. Guard both reconciliation and rollback with the same generation.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
@OutThisLife

Copy link
Copy Markdown
Collaborator

Superseded by #61173 — the desktop Kanban board landed as a bundled plugin on the desktop SDK rather than a hardcoded app route, so it ships off by default, tears itself down cleanly on disable, and localizes without touching core en.ts.

Your board shape fed into it — column order, the task drawer, the board switcher, and the /kanban route + sidebar nav row are the design this and the sibling PRs converged on independently. You're credited in the PR body.

Thanks for the work here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants