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:
- Parse configuration –
swarmforge/swarmforge.confis read forwindowandwindow-invisiblerole definitions - Generate unique socket – A socket file is created at
.swarmforge/tmux-socketwith absolute path resolution - Bind all tmux operations – Every subsequent command uses
-S "$TMUX_SOCKET"for that project - Spawn terminal surfaces – The appropriate adapter from
swarmforge/scripts/terminal-adapters/opens windows using the correct socket - Start handoff daemon –
handoffd.bbmonitors 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
TMUXorSWARMFORGE_TMUX_SOCKETfrom a previous project - The
.swarmforge/tmux-socketfile 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_TMPDIRoverrides that force shared socket directories - Use absolute socket paths — The
.swarmforge/tmux-socketfile 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):
- Create
swarmforge/scripts/terminal-adapters/wezterm.shimplementing the socket-forwarding contract - Update
swarmforge/scripts/swarm-terminal-adapter.shdetection 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
- 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-Sto reference it - Environment contamination causes bleed-over — Always
unset TMUXbefore switching projects; never reuse shell sessions with staleSWARMFORGE_TMUX_SOCKETvalues - Daemon restart resolves notification failures —
handoffd.bbcaches 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →