← reports

#4508 · Hidden split pane timeline refetch

BugMedium priorityMedium effortthreads · perfGitHub issue2026-09-29 · base ea49535e06e83a0df0b11b56b8280cc2e1f5addd

Verdict: REPRODUCED · Root-cause confidence: high

1. TL;DR

A maximized split pane keeps sibling thread panes mounted. Their timeline observers continue to be enabled even though the sibling content is hidden. A focused React Query reproduction shows a hidden sibling fetch again when its query is invalidated. This proves avoidable query work in the split-pane path; production network frequency and CPU impact were not measured.

2. Claims vs findings

ClaimFindingEvidence
Maximizing hides, rather than unmounts, sibling panes.VerifiedThe split tree applies hidden presentation while retaining the pane component; the existing mounted-draft test also passes.
A hidden thread's timeline remains subscribed to query invalidation.Verified in focused UI testThe hidden sibling's query fetch count rises from 1 to 2 after invalidation in two clean runs.
Production CPU cost is material.UnverifiedNo live desktop profiling or network trace was taken.

3. Environment

4. Minimal reproduction

  1. On the trusted base commit, copy the linked test artifact over apps/app/src/views/thread-detail/SplitThreadArea.test.tsx in a disposable checkout.
  2. Run pnpm install --frozen-lockfile --prefer-offline, pnpm exec turbo run build, then pnpm exec turbo run test --filter=@bb/app -- src/views/thread-detail/SplitThreadArea.test.tsx -t 'pauses a hidden pane timeline'.
  3. The test mounts two panes, maximizes the first, invalidates the second pane's query, and expects no new fetch until restore.

Focused test from the complete runnable test file:

  it("pauses a hidden pane timeline and refreshes it when restored", async () => {
    timelineProbe.active = true;
    renderSplitArea({ path: threadPath("thr-a"), layout: twoPaneLayout("pane-1") });

    await waitFor(() => {
      expect(timelineProbe.calls.get("thr-a")).toBe(1);
      expect(timelineProbe.calls.get("thr-b")).toBe(1);
    });

    fireEvent.click(screen.getByTestId("maximize-thr-a"));
    await act(async () => {
      await queryClient.invalidateQueries({ queryKey: ["timeline-probe", "thr-b"] });
    });
    expect(timelineProbe.calls.get("thr-b")).toBe(1);

    fireEvent.click(screen.getByTestId("maximize-thr-a"));
    await waitFor(() => expect(timelineProbe.calls.get("thr-b")).toBe(2));
  });

Expected: hidden sibling fetch count remains 1 after invalidation; after restore it reaches 2.

Expected: 1
Received: 2
FAIL SplitThreadArea > pauses a hidden pane timeline and refreshes it when restored

5. Root cause

SplitThreadArea sets visual hiding on the sibling while preserving its mounted content. ThreadDetailView calls the timeline controller without a visibility input. The controller defaults enabled to true, and the timeline hook passes that state to React Query. The invalidation path sees an active timeline query and can refetch it. Document visibility is a separate condition and remains true during pane maximization.

6. Proposed fix

Pass split-pane visibility to the thread detail timeline controller. Disable only the hidden pane's observer, retaining the mounted pane and cached rows. Let the observer refetch on reveal. A separate visible observer for the same thread should continue fetching.

7. Related issues

No linked open pull request was found for this issue. Other timeline performance reports do not establish this pane-visibility behavior.

8. Verification

The same agent repeated the reproduction in a second clean worktree at ea49535e06e83a0df0b11b56b8280cc2e1f5addd, with a fresh frozen install and Turbo build. The same assertion failed there: expected hidden-pane count 1, received 2. No report claim was changed after the second run. The query probe uses the actual split-pane component and TanStack Query but replaces the large thread-detail view with a minimal observer; live desktop network and CPU behavior remain unmeasured.

9. Appendix

First and second commands: pnpm exec turbo run test --filter=@bb/app -- src/views/thread-detail/SplitThreadArea.test.tsx -t 'pauses a hidden pane timeline'. Both exited 1 for the same intended assertion. The issue body was treated as untrusted claims; no linked code or external artifact was executed.