Sidebar: fix scrollbar artifacts, keep sessions with pending work "running" - #322
Merged
Conversation
| # wakeup, a restart continuation, a star hook): they resume work the | ||
| # session was already doing, so they must not re-sort the sidebar. | ||
| await self._finalize_turn( | ||
| session_id, st, channel, bump_updated_at=not internal, |
Collaborator
There was a problem hiding this comment.
I think it would be better to add bump_updated_at as a param to run() instead of overloading internal here. A couple of things that won't be bumped, when I think they should:
- Starring a session, which is marked internal:
nerve/nerve/gateway/routes/sessions.py
Line 483 in a587b06
- Waking up a run-later task:
Line 1352 in a587b06
Both of those are user-requested actions that should bump updated_at.
Alternatively, we could just explicitly bump updated_at at both of those sites and leave the internal check as it is?
The '...' load-more row was sized `w-full` *plus* `mx-1`, i.e. 100% + 8px: the session list overflowed by exactly those 8px and grew a horizontal scrollbar. Where that scrollbar met the vertical one, WebKit painted ::-webkit-scrollbar-corner with its own near-white default — the bright dot in the panel's bottom-right corner. Size the row to `calc(100% - 0.5rem)`, paint the corner transparent like the track, and pin the list to overflow-x-hidden: it only ever scrolls vertically, and rows truncate, so nothing is reachable only by scrolling sideways.
A session that schedules a wakeup or leaves a background job in flight is not idle: it will pick itself back up. But the moment its turn ended the sidebar dropped it out of the Running group, and when the continuation landed the assistant row bumped updated_at and yanked the session to the top of the list — reordering the panel for work the user never triggered. Sessions now carry two live bits on every row and every session_running transition: pending_wakeup_at (the fire time of the one pending wakeup a session can have) and has_background_tasks (live run_in_background work in its CLI). A row with either is "parked": it stays in the Running group and shows a violet pulse instead of the emerald one, so "working right now" still reads apart from "will resume on its own". A tooltip names which. updated_at is now frozen for turns nobody asked for — internal runs (fired wakeup, restart continuation, star hook) and autonomous drains (a background task settling). The message row is still recorded and still counts; only the sidebar's sort key is left alone. A turn the user actually sent bumps it as before. Tests: updated_at freeze at the store level and through the feed ordering, the engine's pending-work lookup and broadcast payload, the two fields on every sidebar list route, and the client handler that applies them.
…ernal` Review catch: `internal=True` only means "don't persist the trigger message" — it does not mean "the user didn't ask for this". Deriving the freeze from it quietly stopped two user-requested turns from surfacing: * the star-project hook, fired because the user just starred the session; * a run-later deferral, delivering a message the user wrote and scheduled. `run()` / `_run_inner()` now take `bump_updated_at` (default True), so a freeze is something a call site asks for rather than a side effect of an unrelated flag. Only two callers ask: the wakeup sweep for model-scheduled ScheduleWakeup ticks (run-later deferrals keep bumping), and the autonomous drain when a background task settles. Everything else — the star hook, restart continuations, expired-question injections — bumps exactly as it did before this branch.
pufit
force-pushed
the
pufit/sidebar-parked-sessions
branch
from
August 20, 2026 15:51
af4b74c to
e2d3126
Compare
alex-clickhouse
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three sidebar fixes from one report: a stray white dot, a horizontal scrollbar, and sessions dropping out of "Running" the moment their turn ended.
1. The white dot in the panel corner
index.cssstyles the scrollbar, its track and its thumb — but never::-webkit-scrollbar-corner. Where a horizontal and a vertical scrollbar meet, WebKit painted that square with its own near-white default: a bright dot on a dark panel. Now transparent, like the track.2. The horizontal scrollbar
Not cosmetic — a sizing bug. The
...load-more row wasw-fullplusmx-1, i.e. 100% + 8px, so the list overflowed by exactly those 8px (measured live:clientWidth233,scrollWidth241). Sized tocalc(100% - 0.5rem), and the list is pinned tooverflow-x-hidden: it only ever scrolls vertically and rows truncate, so nothing was reachable only by scrolling sideways.3. Sessions with pending work stay "running" — and stay put
A session that scheduled a wakeup, or left a background job in flight, is not idle: it will pick itself back up. Two problems today:
updated_atand yanked the session to the top of the list — reordering the panel for work the user never triggered.Each row (and each
session_runningevent) now carries two live bits:pending_wakeup_atsession_wakeups,status='pending'has_background_tasksrun_in_backgroundBash/Agent work still live in its CLIA row with either is parked: it stays in the Running group and shows a violet pulse instead of the emerald one, so "working right now" still reads apart from "will resume on its own". Violet was the only free hue — blue is awaiting your input, orange review loop needs you, emerald running, red error, yellow stopped, accent unread. A tooltip names what it is waiting on (
Background job running/Scheduled wake-up at 03:24 PM). Ranked below the error dot on purpose: a failed turn must not be masked by the background work it left behind.Shipping both bits with the transition (not just on the list route) matters because the client deliberately skips the list refetch for the active session.
updated_atis now frozen for turns nobody asked for —internal=Trueruns (fired wakeup, restart continuation, star hook) and autonomous drains (a background task settling). The message row is still recorded andmessage_countstill advances; only the sidebar's sort key is left alone. A turn the user actually sent bumps it as before.Notes
GET /api/sessions/searchwas duplicating_decorateinline; it now calls it, so search rows get the same treatment (including review-loop state) for free.background_tasks_updateis broadcast on the session channel only, so a task that settles without triggering a continuation turn leaves the dot up until the next list refresh. Making that event global would close it — deliberately out of scope here.Testing
pytest tests/— 3351 passed (15 new intests/test_parked_sessions.py: theupdated_atfreeze at the store level and through feed ordering, the engine's pending-work lookup + broadcast payload, and the two fields on every sidebar list route).npm test— 114 passed (5 new for the client handler that applies the fields).npm run lint— unchanged (148 pre-existing problems before and after).