cmux NEXT: delete an incognito workspace's agent chats when it closes
Leo asked for incognito spaces (2026-10-31): "like incognito spaces, each new window is a space". The Spaces spec (design/spaces.md, "Incognito") says an incognito space keeps no history, is never restored, and is deleted when its window closes.
Problem
Agent chats started in an incognito workspace were ordinary saved acpmux chats. Closing the window ended the terminals (WorkspaceClose.close, endTerminals: true) but not the chats. Their acpmux logs and titles stayed, the harness's own transcripts (~/.claude/projects, ~/.codex) stayed, and the chats showed under Earlier in every normal window, in the palette and on New Tab. This is leak 1 of the incognito persistence audit.
Change
- acpmux (Rust):
_acpmux/workspace_purge {workspaceId}(server/chats/workspace_purge.rs), served on the local unix socket only. It stops and purges (Hub::killwith purge) every session acpmux recorded for that workspace when the session started (_meta.acpmux.workspaceId, which the app already stamps on every localsession/new, orCMUX_WORKSPACE_ID). That covers the app's chats plus their sub-sessions, forks and side sessions (each keeps its parent's workspace), and agents started from the workspace's terminals. For Claude Code and Codex sessions it also deletes the transcript the session wrote: the chat-index file named for each agent session id it recorded, plus Claude Code's sibling folder of that name. Once those are gone the chat leaves the device chat index. A transcript born before its session is kept, because that means an adopted chat that existed before. Watched chats are kept, and so is any file not named*<id>*.jsonl. - App (Swift, one forwarding call):
IncognitoChats.closinghangs off the existingWorkspaceClose.willClosehook, which every close path already calls: incognito window close (discard), Delete Space, the crash-ledger discard at launch, andcloseIncognitoWindowsForTerminationat quit. For an incognito workspace it sends the purge throughAcpmuxEnvironment.purgeWorkspace(key:). The quit runsIncognitoChats.drain()afterwindows.prepareForTermination(), so the purges reach acpmux before its agents end. - The method is registered in
rpc.rs,protocol/mod.rs,spec/acpmux-protocol.json, the generated webmethods.tsanddocs/acpmux-schema.json.
Why not _meta.acpmux.ephemeral (what the audit suggested)
I checked it: server/ephemeral.rs ties an ephemeral session to the connection that created it and purges it when that connection closes. The agent pane closes and reopens its acpmux WebSocket during normal use: replay backpressure (AgentPaneView.deliver), theme push failure, page teardown and remount. So an ephemeral incognito chat would be purged while its window is still open, which is user data loss. _acpmux/kill {purge: true} works per session (requests.rs), but it would miss sub-sessions, forks and terminal agents. A purge scoped to the workspace covers all of them, and the logic stays in Rust.
Earlier, the palette and New Tab: while the window is open, Earlier already skips chats an open workspace shows (SidebarChatHistoryFeed.liveChats). After the close, the sessions and their transcripts are gone, so nothing lists them.
Tests (two commits: red, then green)
workspace_purge_tests.rs:purges_only_the_workspaces_chats(two incognito chats purged, a chat in another workspace and one with no workspace kept, the uppercase key is accepted, a second purge finds nothing),only_the_local_socket_may_purge(LocalApp, Web and Peer get "Method not found"; a malformed id is refused), anda_transcript_older_than_its_session_is_kept(the birth-time rule and the file-name guard).IncognitoChatsTests.swift: only an incognito workspace's key is purged.- Fleet run of the fix (
cargo test -p acpmux --lib -- workspace_purge protocol_plus clippy-D warnings): 9 passed, clippy clean (run).
Follow-ups (not in this PR)
- After an app-only crash on a daemon with state resources, the daemon closes the ephemeral workspace at its next start, but its chats are not purged (audit leak 8).
- Harnesses other than Claude Code and Codex keep their own transcripts. Codex's
session_index.jsonlthread name may also persist. - Audit leaks 2 to 7 (shell history, mobile, scrollback, notifications, launch images, relaunch rows) are separate PRs.
Localization: no user-facing strings added.
Agent session: agent session
🤖 Generated with Claude Code