Skip to content

(remote): light a pip while a remote session writes - #242

Merged
devsuitup merged 2 commits into
mainfrom
feat/remote-activity-pip
Sep 10, 2026
Merged

(remote): light a pip while a remote session writes#242
devsuitup merged 2 commits into
mainfrom
feat/remote-activity-pip

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

The watch channel from #241 already parses every transcript event into
{ kind: 'project', rel } and threw rel away. That signal arrives in real
time — measured against the live host, one remote session answering a single
question produced 19 of them over nine seconds — and fed nothing visible.

This puts it on screen: a pip that lights while a remote session is writing its
transcript, and goes out on its own when it stops.

Two callbacks, deliberately unequal

start(alias, onEvent, onActivity).

  • onEvent stays coalesced at 15 s. It opens an ssh cycle, so it must be.
  • onActivity fires on every project event, uncoalesced. It costs no
    connection and no disk — only a map write and, at most once a second per
    session, one small IPC message.

Routing activity through the coalesced path would have pinned the pip to the
15 s refresh rhythm, which is exactly what makes it useless as an activity
indicator. kind: 'session' never reaches it: a descriptor rewrite is a status
change, not writing.

Nothing rebuilds the sidebar

The renderer gets { alias, sessionId, at } and updates that one row. No
projects rebuild, no morphdom pass — the whole point is that an event costs
nothing. The pip clears 20 s after the last event for that session, one timer
per session, reset on each event, and dropped when the row goes away.

A rebuild for other reasons still paints the pip correctly: remoteActiveAt
rides along on the session object, so a fresh launch or an unrelated refresh
does not blank an active session.

The decision itself lives in remote-activity.js — a pure module the main
process wires thin, testable directly rather than through a replica.

One defect fixed on top of the original commit

The IPC throttle map kept one entry per session it had ever seen, for the life
of the process, while the sightings map was pruned. Both now age out of the
same window, pinned by a test that fills 50 sessions and asserts both maps
shrink to one. Removing the prune turns it red.

Verification

Mutations, each breaking only its own test: the onActivity call dropped; the
kind === 'project' guard removed; the session-id check relaxed; the IPC
throttle removed; the decay prune disabled; the renderer's decay delay changed;
the initial-paint comparison inverted; the throttle-map prune removed.

One real bug surfaced by the tests and fixed before it shipped: both the
throttle check and activeAt used map.get(k) || <default>, which collapses a
legitimate 0 timestamp to "absent". Invisible with a real clock, immediate
once the injected clock started at zero.

task check: 1202 pass, 0 fail, 8 pre-existing skips, lint 0 errors.

Not verified: the pip's colour was chosen by inspection against the existing
dot palette, not looked at in a running window.

remote-watch.js already parses every transcript-write inotify event but
threw the rel path away. Wire a second, uncoalesced callback (onActivity)
through it so a per-session last-write timestamp reaches the sidebar in
near real time, without paying for an ssh refresh per event the way the
existing coalesced onEvent does.

main.js tracks last-write times per alias+session (remote-activity.js,
throttled to 1 IPC message/s/session) and seeds session.remoteActiveAt on
every rebuild so a fresh launch paints correctly. The renderer
(remote-activity-ui.js, mirroring the session-activity.js split so it can
be evaluated standalone in jsdom) lights a dedicated dot next to the
existing status dot and clears it 20s after the last sighting.
The per-session IPC throttle kept one entry per session it had ever seen, for
the life of the process, while the sightings map was pruned. Both now age out
of the same decay window.
@devsuitup
devsuitup merged commit d28b753 into main Sep 10, 2026
10 checks passed
@devsuitup
devsuitup deleted the feat/remote-activity-pip branch September 10, 2026 00:38
devsuitup added a commit that referenced this pull request Sep 10, 2026
…ed decay

Two bugs in e1e2187: remote-activity-ui.js wrote sessionBusyState directly
instead of going through session-activity.js's setActivity dispatcher, so a
decayed remote session never picked up the response-ready transition a
local one gets; and buildSessionItem seeded busy=true from remoteActiveAt
with no decay timer, so a row rendered within the window and never touched
again by a live event stayed busy forever. Move the seed into
remote-activity-ui.js's seedRemoteActivity(), called once per render before
items are built, arming a timer for the remaining window instead of a fresh
one. Also repoints every dangling .ai/contexts/session-cache.md comment at
the new Remote hosts - busy spinner (issue #242) subsection.
devsuitup added a commit that referenced this pull request Sep 10, 2026
* fix(sidebar): make the remote activity indicator the local busy spinner

A remote session writing its transcript lit a separate violet dot
(#242) while a local session busy generating used the braille spinner
on .cli-busy. Same signal, two vocabularies. Route the remote-activity
event through sessionBusyState and applyActivityClasses — the single
declared writer of .cli-busy in session-activity.js — instead of
toggling a dedicated element, and drop that element and its CSS.

* fix(sidebar): route remote activity through setActivity, fix stuck-seed decay

Two bugs in e1e2187: remote-activity-ui.js wrote sessionBusyState directly
instead of going through session-activity.js's setActivity dispatcher, so a
decayed remote session never picked up the response-ready transition a
local one gets; and buildSessionItem seeded busy=true from remoteActiveAt
with no decay timer, so a row rendered within the window and never touched
again by a live event stayed busy forever. Move the seed into
remote-activity-ui.js's seedRemoteActivity(), called once per render before
items are built, arming a timer for the remaining window instead of a fresh
one. Also repoints every dangling .ai/contexts/session-cache.md comment at
the new Remote hosts - busy spinner (issue #242) subsection.

* docs: point the remaining activity comments at the busy-spinner section
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant