# NousResearch/hermes-agent · Kanban dashboard board selection and card rendering bugs

## Open now

- Nothing open on GitHub.

These issues and fixes concern the Kanban dashboard rendering the wrong board's cards or no cards at all when the selected board diverges from the server's current file. The changes attempt to make the pinned or default board authoritative for what the dashboard displays.

## TL;DR

- 5 member(s): 0 open issue(s), 2 closed issue(s), 0 open PR(s), 2 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #21230. Verify it addresses your case.
- **Recommended:** read PR #21230 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-21230,pr-22940`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.34`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #20879: \`\[Bug\]: Kanban dashboard shows correct task count but renders no cards when localStorage board and CLI current-file board diverge\`
- #21819: Kanban dashboard Default board can render current non-default board cards

## Prior & competing PRs

- **#21230: fix(kanban): make dashboard board pin authoritative over server current file (#20879)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `plugins/kanban/dashboard/dist/index.js`
  - fix hunks:
  - `plugins/kanban/dashboard/dist/index.js`

    ```diff
    @@ -112,17 +112,30 @@
     
       function writeSelectedBoard(slug) {
         try {
    -      if (slug && slug !== "default") window.localStorage.setItem(LS_BOARD_KEY, slug);
    +      // Persist the user's dashboard-side board pin even for "default".
    +      // Previously this stripped "default" to keep localStorage empty,
    +      // but the fetch layer read that absence as "no opinion" and fell
    +      // through to the server-side ``current`` file — which the board
    +      // switcher also writes. Result: selecting the default tab after
    +      // creating a new board with "switch" checked showed the new
    +      // board's (wrong) data because the URL omitted ``?board=`` and
    +      // the backend happily returned whichever board was "current".
    +      // Persisting every selection keeps the dashboard's board opinion
    +      // independent of the CLI's active board, which was the original
    +      // design intent. Regression: #20879.
    +      if (slug) window.localStorage.setItem(LS_BOARD_KEY, slug);
           else window.localStorage.removeItem(LS_BOARD_KEY);
         } catch (_e) { /* ignore quota / private mode */ }
       }
     
       function withBoard(url, board) {
    -    // Append ?board=<slug> when a non-default board is active. Omitted
    -    // for default so the URL stays clean and the backend falls through
    -    // to its own resolution chain (env var → ``current`` file →
    -    // default) which is already correct.
    -    if (!board || board === "default") return url;
    +    // Always append ?board=<slug> when we have one picked — including
    +    // "default". Omitting the param would fall through to the backend's
    +    // resolution chain (env var → ``current`` file → default), which
    +    // means the dashboard's tab selection gets silently overridden by
    +    // whatever board the CLI or "switch" checkbox last activated.
    +    // Regression: #20879.
    +    if (!board) return url;
         const sep = url.indexOf("?") >= 0 ? "&" : "?";
         return `${url}${sep}board=${encodeURIComponent(board)}`;
       }
    ```

    ```diff
    @@ -447,9 +460,11 @@
               token: [redacted:secret-assignment]
             };
             // Pin the WS stream to the currently-selected board so events
    -        // from other boards don't bleed in. Only set for non-default so
    -        // single-board installs keep the cleaner URL.
    -        if (board && board !== "default") qsParams.board = board;
    +        // from other boards don't bleed in. Includes "default" so the
    +        // dashboard's own board pin always wins over the server-side
    +        // ``current`` file — same rationale as ``withBoard()`` above.
    +        // Regression: #20879.
    +        if (board) qsParams.board = board;
             const qs = new URLSearchParams(qsParams);
             const url = `${proto}//${window.location.host}${API}/events?${qs}`;
             let ws;
    ```
- **#22940: fix(kanban): request default board explicitly (salvage #22106)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `plugins/kanban/dashboard/dist/index.js`, `tests/plugins/test_kanban_dashboard_plugin.py`
- **#22106: fix(kanban): request default board explicitly (#21819)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#22940`

## Fix status

- Merged to main in PR #21230 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-10`.
