/lanes now builds only the rows in view, plus 40 rows either side, with spacers holding the rest (#70). At 2,000 agents the page goes from 68,304 elements (2,001 rows built) to 1,054 (23 rows), and to 1,824 when scrolled to the middle. Rows stay in DOM order, and a focused row stays built. Scrolling, live lines arriving while scrolled, deep links and the phone layout behave as before. A row's height is estimated until it has been drawn, so a long jump of the scrollbar into rows never seen can land a few rows off until the spacers are corrected.
Burn chips now show the index's totals instead of summing only the 200 lines the page keeps (#60). The totals come from GET /leaderboard?by=agent&window=hour&rank=burn, fetched in pages when the page loads and again every 60 s. A person's or host's chip sums the totals of the rows it covers, and lines that arrive between fetches are added locally until the next one. If the fetch fails, a chip falls back to the page's own sum and is marked with the placeholder word tatterdemalion, which is now in placeholders.test.mjs until its wording is written.
lanes.test.mjs adds cases for a bounded node count at 2,000 agents, scrolling bringing the right rows in, chips taking the server's total, and a failed fetch falling back. All web tests pass apart from the two known failures, the shells test and the placeholder guard, which now also lists the new placeholder.
🤖 Generated with Claude Code