Debugging tmux Session Isolation in Multi-Project SwarmForge Swarms: Complete Troubleshooting Guide

SwarmForge isolates AI agent swarms using project-local tmux sockets stored in .swarmforge/tmux-socket, preventing cross-project interference when multiple swarms run concurrently.

Debugging tmux session isolation in multi-project SwarmForge swarms requires understanding how the orchestration layer creates per-project sockets, launches terminal adapters, and manages the handoff daemon. When isolation fails, agents from one project bleed into another's windows—often due to stale environment variables, incorrect socket paths, or daemon misconfiguration. This guide walks through the architecture, diagnostic steps, and fixes based on the SwarmForge source code implementation.

How SwarmForge Isolates tmux Sessions

SwarmForge enforces strict project boundaries through a socket-based architecture. Each swarm operates within its own tmux server instance, with all runtime data confined to a local .swarmforge/ directory.

The Socket Creation Sequence

When you launch ./swarm from a project directory, the system executes this initialization chain:

  1. Parse configuration – swarmforge/swarmforge.conf is read for window and window-invisible role definitions
  2. Generate unique socket – A socket file is created at .swarmforge/tmux-socket with absolute path resolution
  3. Bind all tmux operations – Every subsequent command uses -S "$TMUX_SOCKET" for that project
  4. Spawn terminal surfaces – The appropriate adapter from swarmforge/scripts/terminal-adapters/ opens windows using the correct socket
  5. Start handoff daemon – handoffd.bb monitors outbox directories and sends wake-up messages via the project-specific socket

According to the README (lines 374-377), this socket isolation is fundamental to multi-project safety. The TMUX environment variable is explicitly cleared before project launch to prevent accidental attachment to existing sessions.

Key Components in Session Isolation

Component Location Isolation Responsibility
Configuration parser swarmforge/swarmforge.conf Defines which roles get visible vs. invisible windows
Socket manager .swarmforge/tmux-socket (runtime) Provides unique tmux server endpoint per project
Terminal adapters swarmforge/scripts/terminal-adapters/*.sh Bridge to host terminal using correct -S flag
Handoff daemon swarmforge/scripts/handoffd.bb Delivers notifications via project socket only

Common Session Isolation Failures

Agents Appear in Wrong Project Windows

Symptom: Agents from Project A display in Project B's tmux windows, or commands intended for one swarm affect another.

Root cause: The TMUX_SOCKET environment variable points to a stale or incorrect path. This occurs when:

  • A shell session retains TMUX or SWARMFORGE_TMUX_SOCKET from a previous project
  • The .swarmforge/tmux-socket file was manually moved or copied between projects
  • A terminal emulator restores environment variables from a previous session

Diagnostic commands:


# Verify socket isolation between two projects

cd /path/to/project-a
cat .swarmforge/tmux-socket

# Output: /path/to/project-a/.swarmforge/tmux-socket

cd /path/to/project-b
cat .swarmforge/tmux-socket

# Output: /path/to/project-b/.swarmforge/tmux-socket

# These paths must differ; if identical, isolation is compromised

Fix: Clear environment state before launching any swarm:

unset TMUX SWARMFORGE_TMUX_SOCKET
cd /path/to/your/project
./swarm

New Windows Fail to Open

Symptom: A role configured with window in swarmforge.conf starts but no terminal surface appears.

Root cause: The terminal adapter cannot locate the socket or the backend detection failed. The swarm-terminal-adapter.sh dispatcher determines which adapter script to invoke, and each adapter must pass the socket path correctly.

Diagnostic steps:


# Run with explicit terminal selection and verbose output

SWARMFORGE_TERMINAL=ghostty ./swarm 2>&1 | tee swarm-launch.log

# Check adapter exit codes

echo $?  # Non-zero indicates adapter failure

# Verify the adapter references the socket correctly

grep -n "tmux -S" swarmforge/scripts/terminal-adapters/ghostty.sh

Fix: Confirm the adapter script includes proper socket handling. The ghostty.sh reference implementation uses:


# From ghostty.sh - adapter contract

tmux -S "$SWARMFORGE_TMUX_SOCKET" new-window -t "$session_name" ...

Handoff Notifications Stop Working

Symptom: Agents no longer respond to task assignments; the swarm appears frozen despite running processes.

Root cause: The handoffd.bb daemon attached to a stale socket, or the socket file was removed (e.g., by tmux kill-server or manual cleanup).

Diagnostic verification:


# Check daemon socket attachment in logs

grep "using tmux socket" /tmp/handoffd.log

# Verify socket file existence

ls -la .swarmforge/tmux-socket

# Test direct tmux communication

tmux -S "$(cat .swarmforge/tmux-socket)" list-sessions

Fix: Restart the daemon with correct socket detection:


# Terminate stale daemon

pkill -f "handoffd.bb.*$(basename $PWD)"

# Restart with verified socket path

export SWARMFORGE_TMUX_SOCKET=$(cat .swarmforge/tmux-socket)
./swarmforge/scripts/handoffd.bb >> /tmp/handoffd.log 2>&1 &

tmux Copy-Mode Pauses Agent Execution

Symptom: An agent stops processing after you scroll or select text in its window.

Root cause: The tmux pane entered copy-mode, which blocks send-keys and other programmatic interaction until exited.

Immediate fix:


# Exit copy-mode in specific window

tmux -S "$(cat .swarmforge/tmux-socket)" \
     send-keys -t "$session:$window" q

# Or force interrupt

tmux -S "$(cat .swarmforge/tmux-socket)" \
     send-keys -t "$session:$window" C-c

Preventing Cross-Project Bleed-Over

Environment Hygiene Checklist

  • Never reuse $TMUX — SwarmForge unsets this, but shell profiles or terminal emulators may restore it
  • Avoid global tmux configurations — Remove TMUX_TMPDIR overrides that force shared socket directories
  • Use absolute socket paths — The .swarmforge/tmux-socket file contains fully resolved paths, preventing ../ traversal issues

Clean Teardown Procedures

The dashboard's Teardown button executes proper isolation cleanup:


# Equivalent manual cleanup

tmux -S "$(cat .swarmforge/tmux-socket)" kill-server
rm -f .swarmforge/tmux-socket
pkill -f "handoffd.bb.*$PROJECT_NAME"

Always use teardown rather than kill -9 on tmux processes, which can leave orphaned socket files.

Window Visibility Strategy

Configure roles appropriately in swarmforge/swarmforge.conf:

  • window — Opens visible terminal surface; useful for monitoring but susceptible to user interference (copy-mode, manual closing)
  • window-invisible — Runs in background tmux window without terminal surface; eliminates accidental user disruption

For multi-project stability, prefer window-invisible for automated agents and reserve window only for roles requiring human supervision.

Working with Terminal Adapters

SwarmForge's adapter architecture in swarmforge/scripts/terminal-adapters/ provides terminal-agnostic socket handling.

Socket Verification in Custom Adapters

When extending SwarmForge with new terminals, ensure your adapter follows this contract:


# Required: validate socket environment

if [[ -z "$SWARMFORGE_TMUX_SOCKET" ]]; then
    echo "Error: SWARMFORGE_TMUX_SOCKET not set" >&2
    exit 1
fi

if [[ ! -S "$SWARMFORGE_TMUX_SOCKET" ]]; then
    echo "Error: $SWARMFORGE_TMUX_SOCKET is not a valid socket" >&2
    exit 1
fi

# All tmux commands must use -S flag

tmux -S "$SWARMFORGE_TMUX_SOCKET" new-session -d -s "$session_name"

Registering New Terminal Backends

To add WezTerm support (as referenced in the source analysis):

  1. Create swarmforge/scripts/terminal-adapters/wezterm.sh implementing the socket-forwarding contract
  2. Update swarmforge/scripts/swarm-terminal-adapter.sh detection logic:

# Add to the case statement in swarm-terminal-adapter.sh

case "$SWARMFORGE_TERMINAL" in
  ghostty) terminal_backend="ghostty" ;;
  wezterm) terminal_backend="wezterm" ;;  # New entry

  *) terminal_backend="default" ;;
esac
  1. Verify with: SWARMFORGE_TERMINAL=wezterm ./swarm

Deep Dive: How handoffd.bb Maintains Isolation

The handoff daemon in swarmforge/scripts/handoffd.bb is designed to never issue raw tmux commands directly to agents. Per swarmforge/handoff-protocol.md, it:

  • Watches outbox/ directories for handoff files
  • Sends generic wake-up messages via tmux -S "$socket" send-keys
  • Never parses or forwards tmux commands from agent output

This design prevents malicious or buggy agents from injecting tmux commands into other projects. The socket path is read once at daemon startup and cached, making daemon restart necessary after any socket recreation.

Summary

  • Socket isolation is the foundation — Every SwarmForge project creates a unique tmux socket at .swarmforge/tmux-socket; all commands must use -S to reference it
  • Environment contamination causes bleed-over — Always unset TMUX before switching projects; never reuse shell sessions with stale SWARMFORGE_TMUX_SOCKET values
  • Daemon restart resolves notification failures — handoffd.bb caches its socket path; restart after any socket changes
  • Terminal adapters enforce the boundary — Custom adapters must validate and consistently use SWARMFORGE_TMUX_SOCKET
  • Clean teardown prevents state leakage — Use dashboard Teardown or manually remove socket files and kill the daemon

Frequently Asked Questions

How do I verify that two SwarmForge projects are properly isolated?

Check that their socket files differ and that no shared tmux server processes exist. Run cat .swarmforge/tmux-socket in each project directory—the outputs should be distinct absolute paths. Then run lsof +D .swarmforge/tmux-socket (or fuser .swarmforge/tmux-socket) to confirm separate tmux server processes are attached to each socket.

Can I run multiple SwarmForge projects from the same terminal window?

Yes, provided you fully clear tmux-related environment variables between launches. Use unset TMUX SWARMFORGE_TMUX_SOCKET before each ./swarm invocation. For shell convenience, wrap project switching in a function that performs this cleanup automatically.

Why does my agent stop responding after I scroll its window?

tmux copy-mode captures keyboard focus and blocks send-keys delivery. Press q to exit copy-mode, or run tmux -S "$(cat .swarmforge/tmux-socket)" send-keys -t <target> q from another terminal. Consider using window-invisible for automated roles to prevent this user-interaction issue.

What happens if I delete .swarmforge/tmux-socket while a swarm is running?

The tmux server continues running but becomes unreachable through standard SwarmForge commands. The handoff daemon will fail to send wake-up messages. Recover by identifying the orphaned tmux process (ps aux | grep tmux), killing it, and restarting the swarm with fresh socket creation via ./swarm.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →