fix(tmux): use short metadata-backed session names#2888
Conversation
Greptile SummaryThis PR replaces long encoded tmux names with short names backed by persisted identity metadata. The main changes are:
Confidence Score: 4/5The metadata discovery and cleanup failure paths need fixes before merging.
apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/main/core/pty/tmux-session-name.ts | Adds short names and a serialized shell wrapper for metadata, legacy lookup, stale-lock recovery, and attachment. |
| apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts | Adds metadata-backed discovery and batch cleanup, but old-tmux placeholders and listing failures can leave sessions orphaned. |
| apps/emdash-desktop/src/main/core/pty/tmux-reconcile.ts | Reconciles sessions through persisted identities while preserving project and lifecycle-session boundaries. |
| apps/emdash-desktop/src/main/core/pty/pty-spawn-platform.ts | Passes complete tmux identity configuration through local POSIX spawn paths. |
| apps/emdash-desktop/src/main/core/pty/spawn-utils.ts | Passes metadata-backed tmux configuration through SSH command generation. |
| apps/emdash-desktop/src/main/core/conversations/impl/local-conversation.ts | Migrates local conversation creation and cleanup to metadata-backed tmux sessions. |
| apps/emdash-desktop/src/main/core/conversations/impl/ssh-conversation.ts | Migrates SSH conversation creation and descendant-aware cleanup to metadata-backed sessions. |
| apps/emdash-desktop/src/main/core/terminals/impl/local-terminal-provider.ts | Migrates local terminal creation and cleanup to metadata-backed tmux sessions. |
| apps/emdash-desktop/src/main/core/terminals/impl/ssh-terminal-provider.ts | Migrates SSH terminal creation and descendant-aware cleanup to metadata-backed sessions. |
| apps/emdash-desktop/src/shared/core/pty/tmux.ts | Defines the shared tmux identity and session configuration shapes. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Start[Start or resume PTY] --> Build[Build short name and identity]
Build --> Lock[Acquire identity lock]
Lock --> Discover[Find metadata or legacy session]
Discover -->|Found| Attach[Attach existing session]
Discover -->|Missing| Create[Create session and set identity options]
Create --> Attach
Cleanup[Stop, delete, or reconcile] --> List[List emdash sessions]
List --> Resolve[Resolve metadata or legacy identity]
Resolve --> Kill[Kill matching session]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
Start[Start or resume PTY] --> Build[Build short name and identity]
Build --> Lock[Acquire identity lock]
Lock --> Discover[Find metadata or legacy session]
Discover -->|Found| Attach[Attach existing session]
Discover -->|Missing| Create[Create session and set identity options]
Create --> Attach
Cleanup[Stop, delete, or reconcile] --> List[List emdash sessions]
List --> Resolve[Resolve metadata or legacy identity]
Resolve --> Kill[Kill matching session]
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts:112-114
**Literal Metadata Becomes Identity**
When an older tmux returns `#{@emdash_project_id}` and the other option placeholders literally, these non-empty fields are accepted as an identity instead of triggering `show-options`. Stop, delete, and reconciliation then cannot match the friendly session, leaving it running; the shell-side lookup already handles this old-tmux response explicitly.
### Issue 2 of 2
apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts:219
**Discovery Failure Reports Cleanup Success**
When either session-list command fails transiently, `listEmdashTmuxSessions` returns an empty list and this cleanup resolves without killing anything. The changed stop and delete callers then forget or remove the session record, so its tmux process remains running with no application handle; previously those paths still attempted a direct kill using the deterministic name.
Reviews (1): Last reviewed commit: "fix(tmux): use short metadata-backed ses..." | Re-trigger Greptile
| .filter((session): session is EmdashTmuxSession => session !== null); | ||
|
|
||
| await Promise.all( |
There was a problem hiding this comment.
Literal Metadata Becomes Identity
When an older tmux returns #{@emdash_project_id} and the other option placeholders literally, these non-empty fields are accepted as an identity instead of triggering show-options. Stop, delete, and reconciliation then cannot match the friendly session, leaving it running; the shell-side lookup already handles this old-tmux response explicitly.
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts
Line: 112-114
Comment:
**Literal Metadata Becomes Identity**
When an older tmux returns `#{@emdash_project_id}` and the other option placeholders literally, these non-empty fields are accepted as an identity instead of triggering `show-options`. Stop, delete, and reconciliation then cannot match the friendly session, leaving it running; the shell-side lookup already handles this old-tmux response explicitly.
**Context Used:** AGENTS.md ([source](https://app.greptile.com/emdash/github/generalaction/emdash/-/custom-context?memory=c9624c9f-4bb0-42b3-aa19-4ea456b59255))
How can I resolve this? If you propose a fix, please make it concise.| ): Promise<void> { | ||
| const wanted = new Set(sessionIds); | ||
| if (wanted.size === 0) return; | ||
|
|
There was a problem hiding this comment.
Discovery Failure Reports Cleanup Success
When either session-list command fails transiently, listEmdashTmuxSessions returns an empty list and this cleanup resolves without killing anything. The changed stop and delete callers then forget or remove the session record, so its tmux process remains running with no application handle; previously those paths still attempted a direct kill using the deterministic name.
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts
Line: 219
Comment:
**Discovery Failure Reports Cleanup Success**
When either session-list command fails transiently, `listEmdashTmuxSessions` returns an empty list and this cleanup resolves without killing anything. The changed stop and delete callers then forget or remove the session record, so its tmux process remains running with no application handle; previously those paths still attempted a direct kill using the deterministic name.
**Context Used:** AGENTS.md ([source](https://app.greptile.com/emdash/github/generalaction/emdash/-/custom-context?memory=c9624c9f-4bb0-42b3-aa19-4ea456b59255))
How can I resolve this? If you propose a fix, please make it concise.|
Addressed both tmux discovery findings in
Validation:
The literal-placeholder thread is now outdated; the broader discovery thread remains unresolved pending reviewer confirmation. |
Description
Replace the full base64url PTY identifier in tmux session names with a short, recognizable
emdash-<workspace>-<token>name. The full project/task/leaf identity now lives in tmux user options, so reconciliation and cleanup remain lossless without consuming the status bar.The reader supports both metadata-backed sessions and legacy encoded names, including old tmux versions that cannot expose user options in
list-sessionsformats. Local and SSH creation, attach, stop, reaping, and restart reconciliation all resolve sessions through the persisted identity.Concurrent starts serialize per identity. The executable regression coverage includes label changes, legacy attach, old-tmux fallbacks, injection-safe arguments, competing creators, stale locks, PID reuse, signal cleanup, multi-cleaner replacement races, and cleanup-owner crashes.
Related issues
Fixes #2706.
Testing
CI=1 --maxWorkers=1)pnpm --filter @emdash/emdash-desktop format:checkpnpm nx lint emdash-desktoppnpm nx typecheck emdash-desktopgit diff --checkScreenshot/Recording (if applicable)
Not applicable; the shorter name is covered through executable tmux stubs because tmux is not installed in the test environment.
Checklist
messages and, when possible, the PR title