# How the SwarmForge Window Watchdog Prevents Data Loss in Tmux Sessions

> Discover how the SwarmForge window watchdog prevents data loss in tmux by monitoring for missing windows and automatically respawning them.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-09

---

**The SwarmForge window watchdog prevents data loss by continuously monitoring the window-state file for missing tmux windows, automatically respawning any window that remains missing for three consecutive checks, and executing a full session cleanup if the cleanup owner window itself disappears.**

The `unclebob/swarm-forge` repository provides a resilient terminal session management system built around tmux. At its heart, the **SwarmForge window watchdog** ensures that transient terminal crashes or window closures never leave user data in an orphaned state by maintaining a persistent monitoring loop over every tracked window.

## The Three-Stage Protection Loop

The watchdog operates from `swarmforge/scripts/swarm-window-watchdog.bb` as a Babashka process that executes a continuous `recur` loop with a 2000-millisecond pause. Each iteration performs three critical safety checks to guarantee session integrity.

### Stage 1: Detecting Missing Windows

For every entry in the window-state file, the watchdog queries the terminal backend via `terminal_window_exists` to verify the window ID remains present in the graphical environment. If the check fails, the watchdog increments a **missing-count** for that specific row index.

```clojure
(if (terminal-ok? … "terminal_window_exists" window-id)
  (assoc counts index 0)                     ; window present → reset counter
  (let [count (inc (get counts index 0))]
    …))                                      ; window missing → bump counter

```

*Source:* `swarmforge/scripts/swarm-window-watchdog.bb` (lines 94-101)

This counter-based approach prevents the watchdog from reacting to transient rendering delays or momentary window manager hiccups.

### Stage 2: Recovering Lost Windows

When a window's missing-count reaches the configurable `missing-threshold` (defaulting to 3), the watchdog initiates recovery. It calls `terminal_open_session` to spawn a new terminal window with the original session name and title, attaches it to the **cleanup window**, and atomically updates the state files with the new window ID via `rewrite-window-id!`.

```clojure
(when (< count missing-threshold)
  (assoc counts index count)
  …)
(let [new-window-id (terminal-out … "terminal_open_session" session title cleanup-window-id)]
  (rewrite-window-id! window-state-file window-ids-file index new-window-id))

```

*Source:* `swarmforge/scripts/swarm-window-watchdog.bb` (lines 101-107)

This "re-spawning" mechanism ensures that even if a terminal emulator crashes or is accidentally closed, the tmux session resumes inside a fresh window without data loss.

### Stage 3: Fail-Safe Termination

If the **cleanup owner** window—the special window designated as the recovery anchor—disappears and its own missing-count reaches the threshold, the watchdog assumes catastrophic failure of the tmux environment. It immediately invokes `kill-all-sessions!` to terminate every tracked session and close all terminal windows, ensuring no stale state persists on disk.

```clojure
(if (>= count missing-threshold)
  (kill-all-sessions! script-dir window-state-file working-dir tmux-socket backend)
  …)

```

*Source:* `swarmforge/scripts/swarm-window-watchdog.bb` (lines 112-118)

This final safeguard prevents the system from entering an undefined state where windows exist but cannot be managed or recovered.

## File Structure and Integration

The watchdog relies on three core components in the `swarmforge/scripts/` directory:

- **[`swarm-window-watchdog.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm-window-watchdog.sh)** — A thin Bash wrapper that launches the Babashka script with the correct classpath and arguments.
- **`swarm-window-watchdog.bb`** — The core Clojure implementation containing the monitoring loop, state parsing, and recovery logic.
- **[`swarm-terminal-adapter.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm-terminal-adapter.sh)** — The abstraction layer used by the watchdog to communicate with specific terminal backends (e.g., iTerm2, GNOME Terminal).

The main [`swarmforge.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge.sh) launcher starts the watchdog automatically alongside other Swarm Forge components, passing the window-state file, IDs file, cleanup owner index, tmux socket path, working directory, and backend identifier as positional arguments.

## Running and Configuring the Watchdog

While typically invoked by [`swarmforge.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge.sh), you can run the watchdog manually for debugging or custom deployments:

```bash

# Standard invocation

swarm-window-watchdog.sh \
  /tmp/window-state.tsv \
  /tmp/window-ids.txt \
  0 \
  /tmp/tmux.sock \
  "$(pwd)" \
  terminal-app

```

To force-update a window ID after manual session recovery:

```bash
swarm-window-watchdog.sh --rewrite-window-id \
  /tmp/window-state.tsv \
  /tmp/window-ids.txt \
  3 \
  abcdef1234567890

```

Programmatic invocation from Clojure uses Babashka's process library:

```clojure
(require '[babashka.process :as process])

(process/sh ["bb" "swarmforge/scripts/swarm-window-watchdog.bb"
             "/tmp/window-state.tsv"
             "/tmp/window-ids.txt"
             "0"
             "/tmp/tmux.sock"
             "/home/user/project"
             "terminal-app"])

```

## Summary

- The **SwarmForge window watchdog** monitors the `window-state` file every 2000 milliseconds to verify all tmux windows remain accessible via `terminal_window_exists`.
- It tracks missing windows using a counter system that tolerates up to three detection failures (`missing-threshold`) before triggering recovery.
- Lost windows are automatically recreated via `terminal_open_session` and reattached to the cleanup window, with new IDs persisted through `rewrite-window-id!`.
- If the cleanup owner window itself disappears, the watchdog executes `kill-all-sessions!` to prevent orphaned processes and data corruption.
- The implementation resides primarily in `swarmforge/scripts/swarm-window-watchdog.bb` and is invoked through [`swarm-window-watchdog.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm-window-watchdog.sh).

## Frequently Asked Questions

### What triggers the SwarmForge window watchdog to recreate a window?

The watchdog recreates a window when its internal **missing-count** for that window reaches the `missing-threshold` value (default 3). This count increments each time the watchdog loop detects—via `terminal_window_exists`—that the window ID no longer exists in the terminal backend, indicating the window was closed or crashed.

### How does the watchdog decide when to kill all sessions?

The watchdog kills all sessions only when the **cleanup owner window**—the designated recovery anchor—disappears and its own missing-count reaches the threshold. This behavior, implemented in the `kill-all-sessions!` function, assumes that if the cleanup owner is gone, the entire tmux environment is unrecoverable, making a full shutdown safer than leaving stale state.

### Can I configure the detection threshold or polling interval?

Yes. The `missing-threshold` variable controls how many consecutive detection failures are allowed before recovery triggers (default 3). While the polling interval is hardcoded to 2000 milliseconds (`Thread/sleep 2000`) in `swarm-window-watchdog.bb`, both values can be modified in the source before deployment to trade off between reaction speed and tolerance for transient window manager delays.

### What files does the window watchdog monitor and modify?

The watchdog reads from the **window-state file** (TSV format listing sessions and titles) and the **window-ids file** (containing terminal window IDs) passed as the first two arguments. During recovery, it modifies both files via `rewrite-window-id!` to persist new window IDs after respawning lost sessions, ensuring the state remains synchronized with the actual terminal environment.