{"session": "d934c0db-2307-441f-978e-6fba4b5950f9", "env": "main", "command": "journal todo update 4267 --brief \"Notice from transportklok-workspace: the viewer asked GET /api/main/message for 50 rows, more than a page, seen once.\n\nFOUND, on 2026-10-11, and the row now needs a decision rather than a search. src/web/src/sync/rows.js keeps PAGE at 25, and load(type) asks for store.paging.size[type]. That size is set to store.rows[type].length in three places - caughtUp, the missing-row fill, and earlier() - so once the user has paged back once, the held window is 50 rows and EVERY later load asks for 50 in one request. trimmed() puts it back to a page only when the held rows exceed PAGE and the extra ones are completed.\n\nSo paging.size conflates two things: how many rows the viewer is HOLDING and how many it should ASK FOR. The fault is that conflation, in those three assignments, not in any caller.\n\nThe decision, which is why I have not simply changed it: asking for 50 may be deliberate - it restores the window the user had scrolled to after a reload. If that is wanted, the fix is to restore the window the way earlier() builds it, one page at a time by keyset, so no single request ever exceeds a page, and the dev-fault check stays honest. If it is not wanted, load() simply asks for PAGE and the rest is refreshed by caughtUp's since-query.\n\nI would take the first: it keeps the user's scrollback and keeps the rule. It is more work than the second and should not be done at the end of a long night.\n\nAlso fixed along the way, in 535ef99fd: the page size lived in four files and now lives in one, src/web/src/sync/paging.js, which holds numbers and no side effects.\" 2>&1 | tail -1; journal work park 2393 \"The cause is found and written down; the fix turns on whether a reload should restore the scrolled window, which is a decision worth making with a clear head\" 2>&1 | tail -1", "parts": ["journal todo update 4267 --brief \"Notice from transportklok-workspace: the viewer asked GET /api/main/message for 50 rows, more than a page, seen once.\n\nFOUND, on 2026-10-11, and the row now needs a decision rather than a search. src/web/src/sync/rows.js keeps PAGE at 25, and load(type) asks for store.paging.size[type]. That size is set to store.rows[type].length in three places - caughtUp, the missing-row fill, and earlier() - so once the user has paged back once, the held window is 50 rows and EVERY later load asks for 50 in one request. trimmed() puts it back to a page only when the held rows exceed PAGE and the extra ones are completed.\n\nSo paging.size conflates two things: how many rows the viewer is HOLDING and how many it should ASK FOR. The fault is that conflation, in those three assignments, not in any caller.\n\nThe decision, which is why I have not simply changed it: asking for 50 may be deliberate - it restores the window the user had scrolled to after a reload. If that is wanted, the fix is to restore the window the way earlier() builds it, one page at a time by keyset, so no single request ever exceeds a page, and the dev-fault check stays honest. If it is not wanted, load() simply asks for PAGE and the rest is refreshed by caughtUp's since-query.\n\nI would take the first: it keeps the user's scrollback and keeps the rule. It is more work than the second and should not be done at the end of a long night.\n\nAlso fixed along the way, in 535ef99fd: the page size lived in four files and now lives in one, src/web/src/sync/paging.js, which holds numbers and no side effects.\" 2>&1 | tail -1", "journal work park 2393 \"The cause is found and written down; the fix turns on whether a reload should restore the scrolled window, which is a decision worth making with a clear head\" 2>&1 | tail -1"]}