diff --git a/.ai/contexts/session-state.md b/.ai/contexts/session-state.md
index 87330d1c..c0755e7e 100644
--- a/.ai/contexts/session-state.md
+++ b/.ai/contexts/session-state.md
@@ -55,6 +55,40 @@ the terminal header's stop button, and the grid card's stop button all funnel
through it) — it now asks `resolveSessionStop` which IPC to call instead of
always calling `stopSession`.
+### Archive/delete are stop-then-archive/delete (issue #271)
+
+`public/sidebar.js`'s four archive/delete call sites (`.project-archive-btn`,
+`.slug-group-archive-btn`, `.session-delete-btn`, `.session-archive-btn`) used
+to call bare `stopSession` gated on `activePtyIds` — a detach for an attached
+remote row, and nothing at all for an unattached one, while the archive/delete
+proceeded regardless. They now share `stop-session-ui.js`'s `stopBeforeArchive(session)`,
+built on `resolveSessionStop` plus `isRemoteSessionAlive(session)` (the
+`remote-ssh` adapter's own `remoteSessionStates` snapshot when one exists —
+authoritative over a stale `session.remoteDescriptorSeen` right after this app
+itself just stopped it — falling back to `remoteDescriptorSeen` otherwise):
+
+| kind | alive / has PTY | call |
+|---|---|---|
+| remote | alive | `remoteStopSession(alias, sessionId)` |
+| remote | not alive | nothing — `{ok:true}` |
+| local | has PTY (`activePtyIds`) | `stopSession(sessionId)` |
+| local | no PTY | nothing — `{ok:true}` |
+
+A `{ok:false, error}` return skips that session's archive/delete and surfaces
+the failure on its own button — `sidebar.js`'s `surfaceStopFailure(btn, message)`,
+the same flash-and-title-with-restore convention `confirmAndStopSession` uses.
+For the two group archives (project header, slug group), one session's
+refusal only skips that session; the loop continues to the rest. The project
+header's confirmation names the host alias(es) it is about to stop, computed
+with the same `isRemoteSessionAlive` check.
+
+**Delete never calls `stopBeforeArchive` for a remote session.** `delete-session`
+is refused server-side for remote regardless (`REMOTE_READ_ONLY`, main.js) —
+stopping the process first would strand a killed remote session behind a
+delete that never happens, so the delete site checks
+`resolveSessionStop(session).remote` itself and skips the stop entirely for
+that kind, local sessions unaffected.
+
**The remote stop, main-side (`remote-stop.js`).** `createRemoteStopAdapter().stop(alias, descriptor)`
builds one non-interactive ssh command (same `buildRemoteCommandArgs` transport
as `remote-attach.js`'s probe/restore calls) that: (1) reuses
diff --git a/docs/session-browser.md b/docs/session-browser.md
index 6cc9737a..5c8a266c 100644
--- a/docs/session-browser.md
+++ b/docs/session-browser.md
@@ -42,7 +42,7 @@ Use **archive** instead if you only want the session out of the way — archivin
The confirmation dialog states what will be removed: the project, how many files are on disk, and how many subagent transcripts belong to the session. Subagent transcripts are removed with their parent, and their search/index entries with them.
-If the session is still running it is stopped first when Switchboard knows it is live; otherwise the deletion is refused with a reason rather than pulling a transcript out from under a running process. Anything that resolves outside `~/.claude/projects` — a symlinked transcript, for instance — is refused and logged. A session that never started has no transcript to remove, so deleting it just clears the leftover card.
+If a local session is still running it is stopped first; otherwise the deletion is refused with a reason rather than pulling a transcript out from under a running process. A session on a declared remote host is never stopped for a delete — Switchboard only observes remote sessions, so deleting one is refused outright regardless of whether it is still running; use the stop button first if you also want the process on the host ended. Anything that resolves outside `~/.claude/projects` — a symlinked transcript, for instance — is refused and logged. A session that never started has no transcript to remove, so deleting it just clears the leftover card.
## Stop a running session
@@ -53,7 +53,7 @@ A remote session you are not currently viewing keeps running on the host even th
## Star and archive
- **Star** — right-click a session and choose Star, or use the star icon in the session header. Starred sessions appear at the top of their project group.
-- **Archive** — right-click and choose Archive to hide a session from the default view. Archived sessions reappear when you enable the Archived filter.
+- **Archive** — right-click and choose Archive to hide a session from the default view. Archived sessions reappear when you enable the Archived filter. Archiving a running session stops it first — on its declared remote host, not just Switchboard's local view of it — the same as the stop button; the "archive all" buttons on a project or a same-slug group do this for every session they archive, and skip (and flag) any one that fails to stop rather than leaving it silently unarchived.
## Session count limits
diff --git a/eslint.config.js b/eslint.config.js
index 6a9e1912..72f90618 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -168,8 +168,10 @@ const rendererCrossFileGlobals = {
// public/remote-activity-ui.js (remote-ssh adapter, see .ai/contexts/session-state.md)
setRemoteAttached: 'readonly',
applyRemoteStopped: 'readonly',
- // public/stop-session-ui.js (pure stop-vs-detach decision, see .ai/contexts/session-state.md)
+ // public/stop-session-ui.js — see .ai/contexts/session-state.md (issue #271)
resolveSessionStop: 'readonly',
+ isRemoteSessionAlive: 'readonly',
+ stopBeforeArchive: 'readonly',
// read by sidebar.js's parentHasActiveSubagent() — see .ai/contexts/subagent-observability.md
remoteSessionStates: 'readonly',
// public/local-transcript-adapter.js (local-transcript adapter, see .ai/contexts/session-state.md)
diff --git a/public/index.html b/public/index.html
index 30d76676..85b3ce7f 100644
--- a/public/index.html
+++ b/public/index.html
@@ -138,14 +138,14 @@
+
+
-
-