Conversation
…d memoize list rows The Tasks page paged every task on each visit and refetched the whole set on every tasks:changed and threads:changed signal, then mounted a DOM row for every task. On a large board that froze the page for seconds on open and on every change. - Open-first: the list fetches open and closed statuses concurrently and paints open work as soon as it arrives. - Per-task patching: signals are batched for 50 ms; a signal that names a task re-reads only that task (getTask), and the board re-reads only that card's attachments and threads. A signal without a taskId, or projects:changed, still triggers a full refetch. - The list renders only rows near the viewport, stepping the window every 200 px, and rows are memoized so a step mounts only the new ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
On a large board the Tasks page froze for seconds on open and again on every change. Three causes, all in the page's data layer.
listAllTaskspaged every task, done and cancelled included, before anything painted.useInvalidationrefetched the whole query on everytasks:changedandthreads:changedsignal, even though each signal names the task it is about, and agents emit these in bursts. The list then mounted a DOM row for every task. The board view had the same refetch, plus onelistAttachmentscall per card on every refetch.What changed
shell/data.ts.useTasksQuerytakes an optionalapplySignalspatcher. Signals are batched for 50 ms. When every signal in a batch names a task and no load is in flight, the patcher updates the current data in place. Otherwise, or when the patcher fails, it falls back to the existing full refetch. New helpers:listTasksOpenFirst, which fetches open and closed statuses concurrently and publishes open work as soon as it arrives, andpatchTasks, which re-reads only the named tasks viagetTask, drops deleted tasks and re-checks filter membership.views/list/data.ts,views/list/index.tsx. The list loads open work first and patches rows ontasks:changed. Active-thread metadata is fetched only for tasks with live threads (activeOnly, the same predicate asisActiveThread) and patched per task onthreads:changed.views/list/row-window.ts(new),views/list/row.tsx. Only rows near the viewport render, with 800 px of overscan, and groups under 120 rows render in full. The window recomputes every 200 px of scroll.TaskRowis memoized andonOpennow takes the task key, so a step mounts only the new rows.views/board/index.tsx. On a signal, the board re-reads only the named card:getTask, plus that card's attachments and live threads. It recomputes subtask progress locally and keeps the server's(position, id)order.projects:changedstill reloads the board.No contract, server or wire changes. No behaviour change for a signal without a
taskId, which still triggers a full refetch.How you verified
pnpm exec turbo run test typecheck lint --filter=bb-plugin-tasks: 402/402 tests (393 before, plus 9 new), typecheck clean, lint 0 errors.shell/incremental.test.tsx(new, 5 tests): a burst of 5 signals for one task makes 1getTaskand 0listTaskscalls; a deleted task drops without a refetch; a signal with notaskIdstill refetches; the board re-reads only the changed card; thread signals for tasks outside the board cost nothing. With the patch path disabled, 4 of the 5 fail. The fifth is the fallback test, which is correct.views/list/row-window.test.ts(new, 4 tests).shell.test.tsxmocks held only the latest deferredlistTaskscall. The page now makes two concurrent calls (open and closed), so the mocks release every pending call. Their assertions are unchanged.mainon the same machine and the same copy of a real board (≈4.7k top-level tasks, ≈5k with subtasks): apnpm start:worktreeinstance with its own data dir, driven from a headless browser. The same harness ran on both builds.mainlistTaskscalls¹ The one remaining
listTaskscall per edit is the sidebar'sactiveOnlycount, not the page.Not measured: the board view's load time, and a production React build (the dev instance runs React in development mode, so absolute times are pessimistic for both columns).
🤖 Generated with Claude Code