Harden Hub startup and display-name handling

- Display names starting with '-' were parsed by tmux rename-window as
  flags. The alias was persisted before tmux rejected it, so every later
  start/refresh/open failed in refresh_labels until aliases.json was
  edited by hand. Guard rename-window with '--', apply the name before
  persisting it, and roll back the in-memory table on failure.
- Start the Hub tmux server with -f /dev/null by default. Reading the
  user's ~/.tmux.conf let theme status-formats replace the Sessions bar
  and root-table bindings leak into Herdr; HERDR_HUB_TMUX_CONFIG still
  opts into a custom file.
- An unreadable aliases.json blocked start/refresh/open/choose. Degrade
  to default names with a warning (stderr, or the popup message) and
  leave the file untouched for `doctor` to report.
- `doctor` crashed with a traceback when a *_BIN override pointed at a
  missing executable; report it as a failure instead.
- Refuse to run inside another tmux server before discovery and server
  creation, so no unattached Hub is left behind.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 02:58:45 +00:00
co-authored by Claude Fable 5.1
parent 6b906de476
commit ebbd5821fd
4 changed files with 184 additions and 21 deletions
+11
View File
@@ -128,6 +128,17 @@ class CliIntegrationTests(unittest.TestCase):
# Operational recovery must remain possible even if optional
# display-name configuration is later corrupted.
(root / "aliases.json").write_text("not json", encoding="utf-8")
degraded = subprocess.run(
[str(launcher), "refresh"],
cwd=repository_root,
env=environment,
capture_output=True,
text=True,
check=False,
)
self.assertEqual(degraded.returncode, 0, degraded.stderr)
self.assertIn("display names unavailable", degraded.stderr)
self.assertEqual(hub.managed_windows()[0].name, "L default")
stop = subprocess.run(
[str(launcher), "stop"],
cwd=repository_root,