Skip to content

fix(tmux): use short metadata-backed session names#2888

Open
rasitakyol wants to merge 2 commits into
generalaction:mainfrom
rasitakyol:agent/fix-tmux-friendly-session-names
Open

fix(tmux): use short metadata-backed session names#2888
rasitakyol wants to merge 2 commits into
generalaction:mainfrom
rasitakyol:agent/fix-tmux-friendly-session-names

Conversation

@rasitakyol

Copy link
Copy Markdown
Contributor

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-sessions formats. 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

  • Complete desktop Node project: 280 files / 2012 tests
  • Complete main-db project: 26 files / 161 tests
  • Complete migrations project: 7 files / 20 tests
  • Complete browser project: 3 files / 37 tests (CI=1 --maxWorkers=1)
  • Complete scripts project: 1 file / 14 tests
  • Focused tmux/local/SSH/reconciliation suites: 6 files / 109 tests
  • TERM waiter/owner regressions: 15 repeated runs / 30 assertions passed
  • pnpm --filter @emdash/emdash-desktop format:check
  • pnpm nx lint emdash-desktop
  • pnpm nx typecheck emdash-desktop
  • git diff --check

Screenshot/Recording (if applicable)

Not applicable; the shorter name is covered through executable tmux stubs because tmux is not installed in the test environment.

Checklist
  • I kept this PR small and focused
  • I ran a self-review before opening this PR
  • I ran the relevant local checks or explained why not
  • I updated docs when behavior or setup changed (not applicable; no setup or user workflow changed)
  • I added or updated tests when behavior changed, or explained why not
  • I only added comments where the logic is not obvious
  • I used Conventional Commits for commit
    messages and, when possible, the PR title

@rasitakyol
rasitakyol marked this pull request as ready for review July 16, 2026 08:14
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces long encoded tmux names with short names backed by persisted identity metadata. The main changes are:

  • Short workspace-labelled session names with deterministic tokens.
  • Serialized local and SSH creation with legacy-session fallback.
  • Identity-based stop, deletion, reaping, and restart reconciliation.
  • Tests for races, stale locks, old tmux versions, and shell argument safety.

Confidence Score: 4/5

The metadata discovery and cleanup failure paths need fixes before merging.

  • Literal option placeholders can hide sessions on supported old tmux versions.
  • Transient listing failures can make stop or delete succeed while leaving the process running.
  • Creation locking and shell argument handling have focused coverage.

apps/emdash-desktop/src/main/core/pty/tmux-reaper.ts

Important Files Changed

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]
Loading
%%{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]
Loading
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

Comment on lines +112 to +114
.filter((session): session is EmdashTmuxSession => session !== null);

await Promise.all(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

@rasitakyol

Copy link
Copy Markdown
Contributor Author

Addressed both tmux discovery findings in d127fa130.

  • Literal #{@emdash_*} values now trigger the name-only / show-options fallback instead of becoming an identity.
  • Only explicit "no tmux server/session" conditions are benign; transient list / show-options failures propagate.
  • Conversation, terminal, and task records are mutated only after cleanup succeeds.
  • Failed task teardown retains the lifecycle entry for retry without firing a false task:torn-down; project shutdown keeps the previous non-retaining behavior.

Validation:

  • Desktop Node suite: 282 files / 2030 tests
  • Focused app suites: 9 files / 71 tests
  • Shared package: 15 files / 163 tests
  • Desktop/shared format, lint, typecheck, and git diff --check

The literal-placeholder thread is now outdated; the broader discovery thread remains unresolved pending reviewer confirmation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: Tmux session name is too long

1 participant