maybeAddSessionFile computed formatDateTime(new Date()) at the top, before the status==='done' guard that early-returns for every other status. formatDateTime runs two Intl locale formats (~83us measured), so it executed on every progress event — onUploadProgressBatch loops the M-item batch into handleProgress -> maybeAddSessionFile, i.e. 10xM times per second — and threw the result away for all non-done events (the vast majority while uploading).
That made each progress batch a synchronous main-thread block scaling with the active-upload count: ~2.4ms at 25 concurrent, ~5ms at 61, every 100ms, on top of render and sort — enough to blow the 16ms frame budget and stutter scrolling. It is per-event, not per-render, so it janks regardless of scroll position, matching the user's report that the lag appears above ~50 connections and when the uploading rows are scrolled out of view.
Move the formatDateTime call inside the dedup block so it runs once per genuinely-new completed upload. A faithful Blink benchmark at the user's regime (500 rows virtualized, dynamic progress sort, scrolling) shows the per-batch cost drop from 1.7/3.2/4.1ms at 25/50/61 concurrent to a flat 0ms, and frame P95 at 61 concurrent from 7.3ms to 4.2ms.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>