Skip to content

fix(cockpit): a chapter too big to draw opens folded, so a huge PR still reads - #47

Merged
jtomaszewski merged 2 commits into
mainfrom
jtomaszewski/review-page-cpu-spike
Aug 31, 2026
Merged

fix(cockpit): a chapter too big to draw opens folded, so a huge PR still reads#47
jtomaszewski merged 2 commits into
mainfrom
jtomaszewski/review-page-cpu-spike

Conversation

@jtomaszewski

@jtomaszewski jtomaszewski commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Opening a review of a very large PR made the cockpit unusable — the browser
pegged a core and stayed there.

🎯 The problem

A 368-file PR put 611,783 DOM nodes on the page at once: 47,806 diff rows,
a gutter button on every one of them, and 900,000 pixels of scroll. The browser
then spent its time on style and layout rather than on the review.

Measured in a real browser, on the page as it shipped:

before after
DOM nodes 611,783 67,539
page height 901,491 px 116,671 px
fps while scrolling 27 119
main thread blocked while scrolling 56% no long tasks at all

It was not the data — parsing the 2.1 MB artifact takes 4 ms and splitting the
diff takes 2 ms. It was purely the size of the tree the browser had to lay out.

The worst case is the catch-all chapter: every file the AI did not assign lands
in "other changes", so a review that is still running — or one of a PR far
larger than the chapters were meant to describe — puts the entire diff in a
single open chapter.

🔧 The fix

A chapter holding more than 2,000 diff lines now opens folded, and its
header says so and says why (· 34,961 diff lines, folded to keep the page quick). One click opens it — this is a default, not a refusal.

The threshold is measured rather than guessed. Across 314 chapters of real
reviews, p50 is 120 lines and p99 is 1,360; exactly two are past 2,000. A
normal walkthrough opens precisely as it did before.

The load-bearing detail is where the decision is made. My first attempt
seeded the folded set from a useEffect, and measuring caught it drawing all
612k nodes once and then folding them away — paying the entire cost the fold
exists to avoid. So the fold is derived during render from the chapter's weight,
and state records only the chapters the user has flipped the other way.

🔍 Three state leaks this turned up

All three predate this branch; the fold is what made them visible. They are one
bug in three places — state outliving the review it was about.

  • The detail view kept the previous review's artifact until the new fetch
    landed, so one PR's chapters rendered under another PR's URL. The Loading…
    state already existed — the effect just never cleared the artifact. With the
    fold in place this also meant briefly drawing a diff the page had no business
    drawing.
  • A chapter's open/closed state outlived its review. The cockpit walks from
    one review to the next without remounting, so folding other changes on one
    PR folded the unrelated other changes on the next.
  • A load error outlived its review — found by the reviewer on this PR. A
    review that failed to fetch left its banner standing over the next one that
    loaded fine.

🔁 From review

  • The fold note (· 34,961 diff lines, folded …) rendered whether or not the
    chapter was open, so an opened chapter still described itself as folded and
    offered to open itself. It now shows only while closed.
  • The load-error leak above.
  • One thread left open on purpose: the reviewer suggested counting only the
    rows diff2html renders rather than raw patch lines. Measured, the
    overestimate is 2.3% (predicted 47,802 rendered rows vs 47,806 actually
    measured in the browser), and across all 324 chapters on disk the two metrics
    disagree about folding zero times. Left for a human to settle rather than
    closed on my own say-so — see the thread for the numbers.

⚠️ Worth knowing

Expanding the folded catch-all on that 368-file PR still costs ~519k nodes.
That is the user asking for it, and it is now their click rather than the
page's default. If the expanded view of a PR that size ever needs to be usable
too, the next lever is the per-row gutter button — 47k <button> elements that
could be one hover-positioned button. It is ~8% of the nodes, so it would not
have fixed this on its own, and it is deliberately left out of scope.

✅ Verification

  • pnpm typecheck, pnpm test (527 pass, 2 new for diffLineCounts), pnpm build — all green
  • Driven in a real browser against the actual cockpit, not just reasoned about:
    • walking 449 → 392 → 401 → 449 peaks at 67,539 nodes with no transient blow-up
    • toggling a folded chapter open and a normal one closed works both ways and returns exactly to the defaults
    • a normal 6-chapter review renders identically to before (38,427 nodes, all chapters open)
    • the before/after numbers in the table above are measurements from that browser, not estimates
  • SPEC.md §17.5 documents the rule normatively; the old §17.5 is renumbered to §17.6 and its one inbound reference updated

🤖 Generated with Claude Code

…ill reads

Opening a 368-file PR put 611,783 DOM nodes on the page — 47,806 diff rows,
a gutter button on each, 900,000 pixels of scroll — and the browser then spent
its time on style and layout rather than on the review: 27fps and 56% of the
main thread blocked while scrolling, and about 8% burnt at idle re-committing
that tree every poll.

A chapter over 2,000 diff lines now opens folded, with the count and the
reason in its header. The line is measured, not guessed: across 314 chapters
of real reviews only two are past it, so a normal walkthrough opens exactly as
it did. The fold is a default, not a refusal — one click opens it.

Deciding it while rendering rather than seeding it from an effect is the whole
point: folding a chapter React has already drawn pays the cost the fold exists
to avoid. So the state records only where the user disagreed with the default.

Two things this turned up on the way:

- the detail view kept the previous review's artifact until the new fetch
  landed, drawing one PR's chapters under another's URL — and, with the fold,
  drawing them unfolded
- a chapter's open/closed state outlived the review it was set in, so folding
  "other changes" on one PR folded the unrelated "other changes" on the next

Measured after: 611,783 nodes → 67,539, no transient peak, 119fps, no long
tasks at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

This PR improves cockpit performance when opening reviews for very large PRs by default-folding chapters that would render an excessively large diff, while also fixing state-reset bugs that could briefly render the wrong review under a new URL.

Changes:

  • Add chapter “weight” calculation (diff-line counts) and default-fold chapters over a 2,000-line threshold, recording only user “flips” from the default.
  • Reset the detail view’s artifact when reviewKey changes to avoid rendering a previous review under a new URL.
  • Document the walkthrough folding rule in SPEC.md and add unit tests for the new diff line counting helper.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
web/src/Detail.tsx Computes per-chapter diff “weight”, defaults heavy chapters to folded, resets per-review UI state on navigation.
src/core/diff.ts Adds diffLineCounts() helper for per-file diff line counting.
src/core/diff.test.ts Adds tests covering diffLineCounts() behavior.
SPEC.md Documents the walkthrough folding rule and renumbers the prior section reference.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/core/diff.ts
Comment thread web/src/Detail.tsx Outdated
Comment thread web/src/Detail.tsx
… goes with its review

Two things the reviewer caught.

The "34,961 diff lines, folded to keep the page quick" note was rendered
whenever the chapter was heavy, open or not — so an opened chapter still
described itself as folded, and its tooltip still offered to open it. The note
is about the fold; once the fold is gone it has nothing to say.

The detail view cleared the artifact when the review key changed but not the
load error, so a review that failed to load left its banner standing over the
next one that loaded fine. Same class as the two state leaks this branch
already fixed — state outliving the review it was about.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jtomaszewski
jtomaszewski merged commit 25bd0fe into main Aug 31, 2026
2 checks passed
@jtomaszewski
jtomaszewski deleted the jtomaszewski/review-page-cpu-spike branch August 31, 2026 12:36
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.27.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants