You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
(sessions): send a running session to background and bring it back, so it survives a Switchboard restart #546
Restarting Switchboard, for an update for instance, kills every Claude session running in its terminals. That includes long autonomous sessions in the middle of a task, with their subagents and session crons. Wanted: a "Send to background" action before the restart, and "Bring to front" afterwards.
What exists (study 11/10, CLI 2.1.296; docs and code read, nothing measured live)
It starts a new process that resumes from the saved conversation.
A turn in progress continues there.
Background shells, background subagents, dynamic workflows and /loop tasks carry over, Windows included since 2.1.195/196.
Monitors, and any subagent that owns a monitor, are stopped. The CLI asks "Background this session?" when such work is running.
Unsent text cancels it ("Backgrounding cancelled — you have unsent text").
Bringing a session back already works: guardResume answers {attach} for a live kind:'bg' descriptor, so a click in the sidebar opens a claude attach <jobId> tab. Restore and reload skip sessions that are live elsewhere.
Missing
The trigger: write /bg into the PTY, wait for the kind:'bg' descriptor and its jobId (with a timeout), then turn the tab into an attach tab or close it.
parkedJobId in ~/.claude/sessions/<pid>.json: the binary reads it and the CLI excludes such descriptors from its own liveness checks. Switchboard ignores it, so it probably duplicates the session in live-elsewhere and in the Agents view (not verified).
trigger-watcher.js targets PTYs by sessionId, so a /compact injected into a session that went to the background would fail with target process not running. It needs routing to the attach client or the worker's messaging socket.
Disable the action for remote, sandboxed and preLaunch sessions: the daemon starts the worker outside the sandbox (bg-agents invariant 8).
Proposed UX
"Send to background" in the session tool bar and in the sidebar row's context menu.
On quit or update, when sessions are busy: "Move N sessions to background and quit".
"Bring to front" is the existing attach path.
Measure live before building (isolated instance, throwaway session)
After /bg, does the tab's claude exit or stay parked? Capture the descriptors before and after (same sessionId? parkedJobId? a new kind:'bg' with a jobId?) and the process tree.
Quit Switchboard, then kill it: do the worker and the supervisor survive the ConPTY closing on Windows?
After a restart: does the sidebar offer attach, and is the row free of an External + bg duplicate?
What carries over: /loop, a non-/loop cron, a Monitor, a running subagent, a background shell.
What a trigger-injected /compact does to a session in the background.
Live test, 11/10, CLI 2.1.296, Windows. An interactive session in a Switchboard tab ran /bg, then Switchboard was quit and restarted, and the session was reattached from the sidebar.
What
Result
The conversation continued
yes, with full context
sessionId
changed: 6577a487… → a9f4e948…. The new descriptor is kind:"bg", its jobId is the first 8 hex of the new sessionId, and its name gained a suffix (switchboard-dev (2))
Session cron (CronCreate, not /loop)
survived
In-process background subagent (Agent with run_in_background)
killed: "didn't finish before the previous session ended… in-process state was lost". The doc says background subagents carry over; here this one did not. It had partly edited a worktree, and the edits stayed on disk
Another gap observed live (11/10): for a session reattached after /bg, the sidebar's subagent indicators do not show running subagents. The data exists: ~/.claude/projects/<project>/<sessionId>/subagents/agent-*.jsonl keep being written as the two background subagents work. Probably the live subagent state is keyed on the Switchboard-owned PTY session, while an attach tab is keyed by its jobId. Not verified in the code.
Attach on a session that is already attached should go to its tab, not attach a second time.attachBgAgent (public/agents-view.js:386) calls openSession(session, { type: 'attach', … }), which should only show an already-open entry. Suspected cause, not verified: the attached tab is keyed under a different id than entry.sessionId. After /bg the session id changes, so the tab may sit under the old id. The roster already flags attachedHere (bg-agents-roster.js:211). Ask: when the job is attached here, Attach becomes "Go to tab" (or the same button focuses the tab). Test it with an attach entry whose tab key differs from the job's current session id.
Detach from the sidebar without stopping the agent.stop-session already detaches an attach session instead of killing it (test "stop-session detaches an attach session instead of killing it"). But the sidebar shows it as Stop, the same control that kills a regular session. Ask: on an attached tab, label the control Detach, with a tooltip saying the agent keeps running in the background, and keep a separate, confirmed Stop for actually stopping the job.
JB: the reference model is the remote SSH/tmux sessions (docs/remote-hosts.md). There, leaving the tab detaches and the session keeps running on the host, while stopping it is a separate, explicit action. An attached background agent should behave the same way: same controls, same wording, same 'still running elsewhere' state on the sidebar row.
"Detach (the session keeps running)" (public/app.js:1457)
What a detach shows. It sets stopRequested (main.js:1789-1791). The grid card footer then reads Stopped and the exit banner reads "session stopped" while the job is still running.
Other gaps:
Stop in the Agents view kills the job without a confirmation.
A remote attach (SSH/tmux) has no detach control at all.
Proposed convergence: two distinct controls on every surface, never one icon with three meanings:
Detach: reversible, never asks, the session keeps running (the ssh/tmux model already noted above).
Stop: kills, and confirms when the process is live.
An already-attached session gets Go to tab instead of a second attach.
Found while writing it: today a backgrounded session is reattached at the first restart only. At the second restart the saved entry is kept but never reattached ("Not reopened: … is live in pid …"). Half B fixes this and needs no live measurement.
Maintainer decision on Q2: when subagents are running in the tab, ask for confirmation, naming how many will be lost, with Send to background and Cancel.
Still open:
Q1, what the tab becomes after the handoff, is with the product review.
Half A also waits on two live measurements: what the descriptor holds after /bg, and whether the worker survives when the parked tab is killed.
Q1 decided by the product review (13/10): convert the tab in place into a claude attach <jobId> tab under the new id, which matches the remote-session model (docs/remote-hosts.md:205-208; docs/background-agents.md:126-133).
The tab keeps its position, focus and name.
The notice reads: " now runs in the background. Closing this tab detaches it; it keeps running, and Switchboard reattaches it at the next start."
The header control keeps the existing attach label, "Detach (the session keeps running)". Detaching asks for no confirmation.
The sidebar row for the new id shows the bg badge at once, without waiting for the Agents view.
Stopping the job stays in the Agents view.
Exceptions:
If killing the parked CLI kills the worker (measurement M2), fall back to leaving the parked window in place.
On slice 2's "move to background and quit" path, skip the conversion: close the parked CLI, write the bg mark and quit.
Live measurement M1/M2/M3, 13/10, CLI 2.1.296 on Windows 11. A real claude.exe ran in a node-pty ConPTY in a throwaway cwd; /bg, then 300 ms, then \r were written programmatically, the way Switchboard writes them. Raw logs: .work-files/spec-546-probe/live-m1m2/.
M3: yes. The programmatic /bg plus Enter runs /background (bg) through the completion menu.
M1: /bg does not park the tab.
At +65 ms the tab's descriptor goes waiting / dialog open.
At +252 ms that descriptor is deleted.
At +2011 ms the PTY exits by itself with code 0.
parkedJobId never appears, and the agents list is not shown.
The screen prints backgrounded · <jobId> and the attach, logs and stop hints, then the process exits.
Proof available (the spec's fallback branch):
About 0.2 s after the Enter, the old transcript gains {"type":"continued-in","continuedInSessionId":"<new>"}.
At +1.6 s a live descriptor appears with kind:"bg", jobId = the first 8 hex digits of the new sessionId, and the same cwd. It has no status for about 0.7 s, then idle.
The worker runs claude.exe --session-id <new> --fork-session --resume <old>.jsonl under --bg-pty-host … 120 40, so it inherits the tab size.
M2: the job survives the tab's process ending.claude agents --json --all shows it as idle/blocked, and the worker pid stays alive. Closing a parked CLI is moot, because nothing stays parked.
Consequence for slice 1, half A: Switchboard sees an ordinary PTY exit after /bg. It converts the tab using continued-in plus the live bg descriptor. It must tolerate the bg descriptor's first ~0.7 s with no status. There is no parked process to kill.
Slice 2 (with #562) — decided by Jean-Baptiste in person, 2026-10-15, after an independent refutation of the slice-2 spec:
Mixed population at quit / Restart / window close: one button "Move K sessions to background, stop M (listed by name), and quit/update/close". Plain terminals and panel shells are always on the stop side and never disable Background. Follows (ux): one confirmation rule for destructive vs reversible actions #565: a kill confirms by naming what will be lost.
Busy sessions: measure a /bg sent during a turn ((restore): a backgrounded session is restored from its pre-fork transcript #518 reports the CLI deferring it until the current tool finishes) on its own issue, and lift slice 1's busy refusal if it is safe. Until then, the dialog waits for busy sessions to become idle, within a bound the user can cancel.
Spec facts the revision must carry (verified 15/10 against 76edc5c and #646 head 0ff13e7):
On Windows, Electron emits neither before-quit nor quit on shutdown/restart/logoff (electron.d.ts:172-173,618-619), so (index): move every session index write to a writer worker (#616) #646's before-quit drain never runs there; a main-side flush must start from query-session-end / session-end.
Electron's close carries no origin: a WM_CLOSE from Task Manager, taskkill without /F or the NSIS per-machine installer looks like the X button (same limit as (window): ask before a window close ends the app #478) — document it.
Activity
devsuitup commentedon Oct 11, 2026
Live test, 11/10, CLI 2.1.296, Windows. An interactive session in a Switchboard tab ran
/bg, then Switchboard was quit and restarted, and the session was reattached from the sidebar.6577a487…→a9f4e948…. The new descriptor iskind:"bg", itsjobIdis the first 8 hex of the new sessionId, and its name gained a suffix (switchboard-dev (2))/loop)Agentwithrun_in_background)%TEMP%/claude/<project>/<sessionId>/scratchpad)Consequences for the design:
devsuitup commentedon Oct 11, 2026
Another gap observed live (11/10): for a session reattached after
/bg, the sidebar's subagent indicators do not show running subagents. The data exists:~/.claude/projects/<project>/<sessionId>/subagents/agent-*.jsonlkeep being written as the two background subagents work. Probably the live subagent state is keyed on the Switchboard-owned PTY session, while an attach tab is keyed by itsjobId. Not verified in the code.devsuitup commentedon Oct 11, 2026
Two more asks from JB (11/10, v0.0.94):
attachBgAgent(public/agents-view.js:386) callsopenSession(session, { type: 'attach', … }), which should only show an already-open entry. Suspected cause, not verified: the attached tab is keyed under a different id thanentry.sessionId. After/bgthe session id changes, so the tab may sit under the old id. The roster already flagsattachedHere(bg-agents-roster.js:211). Ask: when the job is attached here, Attach becomes "Go to tab" (or the same button focuses the tab). Test it with an attach entry whose tab key differs from the job's current session id.stop-sessionalready detaches an attach session instead of killing it (test "stop-session detaches an attach session instead of killing it"). But the sidebar shows it as Stop, the same control that kills a regular session. Ask: on an attached tab, label the control Detach, with a tooltip saying the agent keeps running in the background, and keep a separate, confirmed Stop for actually stopping the job.devsuitup commentedon Oct 11, 2026
JB: the reference model is the remote SSH/tmux sessions (docs/remote-hosts.md). There, leaving the tab detaches and the session keeps running on the host, while stopping it is a separate, explicit action. An attached background agent should behave the same way: same controls, same wording, same 'still running elsewhere' state on the sidebar row.
devsuitup commentedon Oct 11, 2026
The product audit at v0.0.94 (
docs/product/CONCEPT-MAP.mdM2, #561) adds facts to the Detach part of this issue.One stop control, three meanings. The same square means:
Its label changes with the surface:
public/sidebar.js:1766,public/grid-view.js:192)public/index.html:112)public/app.js:1457)What a detach shows. It sets
stopRequested(main.js:1789-1791). The grid card footer then reads Stopped and the exit banner reads "session stopped" while the job is still running.Other gaps:
Proposed convergence: two distinct controls on every surface, never one icon with three meanings:
devsuitup commentedon Oct 13, 2026
Spec written (13/10, against f0b3094), kept locally for now. Split into four slices:
/bg, waits for the parked descriptor and thekind:"bg"descriptor with itsjobId, then hands the tab over.bgmark, so restore reattaches them on every start./bgtyped by hand (parkedJobId), triggers ((triggers): serialize and verify trigger delivery per tab (split from #579) #592), and subagent indicators.Found while writing it: today a backgrounded session is reattached at the first restart only. At the second restart the saved entry is kept but never reattached ("Not reopened: … is live in pid …"). Half B fixes this and needs no live measurement.
Maintainer decision on Q2: when subagents are running in the tab, ask for confirmation, naming how many will be lost, with Send to background and Cancel.
Still open:
/bg, and whether the worker survives when the parked tab is killed.devsuitup commentedon Oct 13, 2026
Q1 decided by the product review (13/10): convert the tab in place into a
claude attach <jobId>tab under the new id, which matches the remote-session model (docs/remote-hosts.md:205-208; docs/background-agents.md:126-133).bgbadge at once, without waiting for the Agents view.Exceptions:
bgmark and quit.fix(restore): a closed background tab is not reattached at the next s…
(restore): reattach a session sent to the background at every restart (…
devsuitup commentedon Oct 13, 2026
Live measurement M1/M2/M3, 13/10, CLI 2.1.296 on Windows 11. A real
claude.exeran in a node-pty ConPTY in a throwaway cwd;/bg, then 300 ms, then\rwere written programmatically, the way Switchboard writes them. Raw logs:.work-files/spec-546-probe/live-m1m2/./bgplus Enter runs/background (bg)through the completion menu./bgdoes not park the tab.waiting/dialog open.parkedJobIdnever appears, and the agents list is not shown.backgrounded · <jobId>and the attach, logs and stop hints, then the process exits.{"type":"continued-in","continuedInSessionId":"<new>"}.kind:"bg",jobId= the first 8 hex digits of the newsessionId, and the samecwd. It has nostatusfor about 0.7 s, thenidle.claude.exe --session-id <new> --fork-session --resume <old>.jsonlunder--bg-pty-host … 120 40, so it inherits the tab size.claude agents --json --allshows it asidle/blocked, and the worker pid stays alive. Closing a parked CLI is moot, because nothing stays parked.Consequence for slice 1, half A: Switchboard sees an ordinary PTY exit after
/bg. It converts the tab usingcontinued-inplus the live bg descriptor. It must tolerate the bg descriptor's first ~0.7 s with nostatus. There is no parked process to kill.(sessions): close the review findings on Send to background (#546)
(sessions): send a local tab to the background and keep it open as an…
(sessions): close the review findings on Send to background (#546)
(bg): keep Send to background working after Esc and drop the stale co…
(sessions): send a local tab to the background and keep it open as an…
devsuitup commentedon Oct 15, 2026
Slice 2 (with #562) — decided by Jean-Baptiste in person, 2026-10-15, after an independent refutation of the slice-2 spec:
/bgsent during a turn ((restore): a backgrounded session is restored from its pre-fork transcript #518 reports the CLI deferring it until the current tool finishes) on its own issue, and lift slice 1's busy refusal if it is safe. Until then, the dialog waits for busy sessions to become idle, within a bound the user can cancel.Spec facts the revision must carry (verified 15/10 against 76edc5c and #646 head 0ff13e7):
before-quitnorquiton shutdown/restart/logoff (electron.d.ts:172-173,618-619), so (index): move every session index write to a writer worker (#616) #646'sbefore-quitdrain never runs there; a main-side flush must start fromquery-session-end/session-end.closecarries no origin: aWM_CLOSEfrom Task Manager,taskkillwithout/For the NSIS per-machine installer looks like the X button (same limit as (window): ask before a window close ends the app #478) — document it.preventDefault, soapp.quit()then emits the windowclose; the close veto must pass a committed attempt.cpSpawnchildren outsideactiveSessions: not threatened, no scheduler hook ((schedules): run a scheduled task as a background session so it can be joined while it runs #611 ownsschedule-runner.js).publishRekeyis skipped if the source leftactiveSessionsbefore the metadata acknowledgement; the CLI exits ~2 s after/bg.