Add a tmux-grid terminal backend so the whole swarm fits one surface - #42
Open
tidoemanuele wants to merge 1 commit into
Open
Add a tmux-grid terminal backend so the whole swarm fits one surface#42tidoemanuele wants to merge 1 commit into
tidoemanuele wants to merge 1 commit into
Conversation
Every existing backend opens one window per role, so a six-pack scatters six windows across the desktop and leaves no single surface that is the swarm. Add `SWARMFORGE_TERMINAL=tmux-grid`, which tiles the roles instead. The tiling is done by tmux rather than by a terminal emulator, so it behaves identically on macOS, on Windows Terminal under WSL, and on any Linux terminal — including a host with no terminal automation at all, where the grid is simply attached by hand. The grid does not move any role pane. Handoff delivery targets a bare session name, which tmux resolves to that session's current window's active pane, so `join-pane` would destroy the role's session and its delivery address. Instead the backend creates one disposable viewer session whose panes each attach a role session. Every role session stays one session, one window, one pane, and the daemon, the handoff routing and CURRENT_TASK are untouched. When cleanup kills the role sessions the viewer's attach processes exit and the viewer session ends on its own. The single OS window is opened by delegating to an existing single-window backend, auto-detected as usual or named with SWARMFORGE_GRID_TERMINAL, loaded in a child shell so it cannot overwrite the grid's own adapter functions. Tiles are labeled with the project and the role, so two live swarms stay tellable apart. `terminal_backend_tracks_windows` is false for this backend: window-ids, windows.tsv and the window watchdog assume one window per role, and there is no honest way to reopen one role's window inside a shared grid. SwarmForge already has that branch and reports it at startup. Tests drive tmux directly, so they assert the real pane layout, the pane titles, the survival of every role session, and that the viewer session does not outlive the swarm — with no dependency on any window system. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #41.
Thank you for SwarmForge. Running a six-pack against a real project is the first time a group of agents has behaved like an engineering team for me rather than a set of chat windows, and the reason is the discipline in this repo: role prompts, a layered constitution, worktree isolation, and a handoff queue with real lifecycle states. This PR touches only the presentation layer, and it is opt-in — nothing changes for anyone who does not set the variable.
The problem
Every existing backend opens one window per role. That is the right default: each window is independently trackable, and
swarm-window-watchdog.shcan reopen one a role loses. At six roles it also means the swarm has no single surface — six same-sized windows at cascading offsets, overlapping each other, with the window manager deciding which roles you can see.The change
SWARMFORGE_TERMINAL=tmux-gridtiles the roles into one surface:The tiling is done by tmux, so one adapter covers every host SwarmForge already runs on: macOS Terminal.app or iTerm2, Windows Terminal under WSL, and any Linux terminal — including a host with no terminal automation at all, where the grid is attached by hand, which is the normal Linux workflow. It arrives as one more adapter behind the existing contract: no new hook, and no new branch in
swarmforge.bb.No role pane is moved. Handoff delivery targets a bare session name, which tmux resolves to that session's current window's active pane, so
join-panewould destroy the role's session and its delivery address. Instead the backend creates one disposable viewer session whose panes each runtmux attach-session -t swarmforge-<role>. Every role session stays one session, one window, one pane. The daemon, the handoff routing andCURRENT_TASKare untouched, and dropping the variable restores one window per role.The viewer session holds no state. When cleanup kills the role sessions, its attach processes exit and it ends on its own — no addition to
swarm-cleanup.shorclose-swarmis needed. There is a test for exactly that.select-layout tileddecides the geometry, so no layout math is reimplemented here: a six-pack tiles 2x3, a four-pack 2x2, a two-pack side by side. Pane borders carry the project and the role, so two live swarms stay tellable apart. The label comes fromSWARMFORGE_LABEL, then.swarmforge/label, then the working directory name.terminal_backend_tracks_windowsis false for this backend.window-ids,windows.tsvand the watchdog assume one window per role, and there is no honest way to reopen one role's window inside a shared grid, so the backend opts out and SwarmForge takes its own already-existing branch: no ids file, no watchdog, and it prints that it has disabled the watchdog. The tmux sessions remain the durable thing; a closed grid or a single role is re-attachable by hand.Files
swarmforge/scripts/terminal-adapters/tmux-grid.sh— new, the whole feature.swarmforge/scripts/swarmforge.bb— one entry added toterminal-helpersso a missing adapter is still caught at startup.README.md— a "Watching The Whole Swarm In One Surface" section under Terminal Behavior.test/swarmforge/script_test.clj— six tests, described below.No existing backend is modified, and no default changes.
Evidence
Both runs are the same six-pack in the same working directory, with a stub agent standing in for a real backend so the measurement contains only window geometry — no project code, no model traffic.
Before, with a stock one-window-per-role backend:
After, with
SWARMFORGE_TERMINAL=tmux-grid:One OS window, six tiles. Every role session survives the change:
swarmforge-gridis the viewer; the six role sessions are unchanged, sotmux send-keys -t swarmforge-coderstill resolves exactly as before.Startup output, showing the backend declining window tracking rather than half-wiring it:
The panes above are small because tmux sizes a session to the terminal that attaches it and the demo attached an 80x24 window; the README says to run the grid in a large or full-screen window, and
SWARMFORGE_GRID_COLUMNS/SWARMFORGE_GRID_ROWS(default 240x60) size it before anything attaches.Tests
bb test— 30 tests, 121 assertions, 0 failures. Six are new, and they drive tmux directly rather than a window system, so they run anywhere tmux runs:.swarmforge/label-> directory nameNotes
tiledis offered. Named layouts (even-horizontal,main-vertical) would be easy to add later if anyone wants them; they seemed like speculation now.Happy to change anything here, including the name of the backend or the variable. Thank you again for the tool, and for the decades of work behind it.