From b9f1ac8c10224988bbacdec20715d52e426f1da8 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 7 May 2026 05:43:05 -0700 Subject: [PATCH] fix(kanban): make dashboard board pin authoritative over server current file (#21230) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the user created a new board via the dashboard with "switch" checked, the server-side `current` file was flipped to the new board. Clicking the original board's tab then showed no cards even though the count badge read correctly — the REST fetch dropped `?board=` when the selection was "default" and the backend fell through to `current` (= the new board), returning a different board's data than the tab the user clicked. Fix: - `withBoard()` always appends `?board=` when a board is selected, including "default". The dashboard's tab selection becomes authoritative instead of silently deferring to the server's `current` file. - `writeSelectedBoard()` persists every selection (including "default") to localStorage. Previously "default" was stripped, which meant the next page load had nothing to pin to and fell through to `current`. - Same change applied to the WebSocket query builder in `openWs()`. Contract verified live: current_board = "proj2" GET /board → proj2's tasks (bug shape: falls through to current) GET /board?board=default → default's tasks (fix: explicit pin wins) GET /board?board=proj2 → proj2's tasks Closes #20879. --- plugins/kanban/dashboard/dist/index.js | 33 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/plugins/kanban/dashboard/dist/index.js b/plugins/kanban/dashboard/dist/index.js index b4d85432d8..cc8e3a2225 100644 --- a/plugins/kanban/dashboard/dist/index.js +++ b/plugins/kanban/dashboard/dist/index.js @@ -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= 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= 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)}`; } @@ -447,9 +460,11 @@ token: token, }; // 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;