From 66462528a928c1ab624f45ab3e00b0c2b8de33e4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Date: Fri, 11 Sep 2026 08:28:21 +0200 Subject: [PATCH] (activity): remote decay clears busy without claiming response-ready; PTY purge skips remote rows setActivity gains opts.armReady (default true); remote-activity-ui's decay passes armReady:false since silence isn't proof a turn actually finished. updateRunningIndicators now skips dataset.remoteAlias rows and routes its local purge through session-activity's new purgeActivityFor. --- .ai/contexts/session-cache.md | 13 +++ eslint.config.js | 1 + public/app.js | 15 ++-- public/remote-activity-ui.js | 8 +- public/session-activity.js | 28 +++++- public/sidebar.js | 3 + test/dom-sidebar-remote-session.test.js | 3 + test/remote-activity-ui.test.js | 12 ++- test/running-indicators.test.js | 86 +++++++++++++++++++ test/session-activity.test.js | 108 ++++++++++++++++++++++-- 10 files changed, 258 insertions(+), 19 deletions(-) diff --git a/.ai/contexts/session-cache.md b/.ai/contexts/session-cache.md index 3db7eaf8..fc9ba72e 100644 --- a/.ai/contexts/session-cache.md +++ b/.ai/contexts/session-cache.md @@ -224,6 +224,19 @@ and arms a 20 s decay timer (one per session, reset on each event) that calls busy without waiting for the next event; the visual is the shared `.cli-busy` braille spinner, not a separate indicator. +The decay call passes `setActivity(sessionId, false, 'remote-decay', { armReady: false })`, +not the bare two-argument form local PTY callers use. 20 s of transcript silence means +"stopped writing", not "the response is ready" — a remote adapter has no PTY to ask +whether a turn actually ended, so a long tool call or a parent delegating to subagents +(its own transcript silent while children write theirs) would otherwise light every +unviewed remote row as `.response-ready` on a plain inference. `armReady: false` clears +`.cli-busy` and `sessionBusyState` through the normal path but skips adding the session to +`responseReadySessions`, so a remote row falls idle without ever claiming "Claude finished, +you haven't looked." Separately, `app.js`'s `updateRunningIndicators` PTY-set purge skips +rows carrying `dataset.remoteAlias` (F7) — a remote row's busy state is owned by this decay +timer, not by local PTY presence, so it must not be cleared just because some unrelated +local PTY started or stopped. + ### Remote hosts file-level rescan (issue #216, first half) **The unit of rescan used to be the folder, not the file.** `syncMirror` diff --git a/eslint.config.js b/eslint.config.js index f3a43016..eb3b3891 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -147,6 +147,7 @@ const rendererCrossFileGlobals = { reconcileBusyState: 'readonly', currentActivitySeq: 'readonly', forgetActivitySeq: 'readonly', + purgeActivityFor: 'readonly', pruneRemoteActivityTimers: 'readonly', // Third-party renderer libs loaded as