feat(dashboard): stop hook clears tmux state + right-pane row marker
test / unit (pull_request) Successful in 18s
test / integration (pull_request) Successful in 1m6s

PRD 0021 chunk 4 (final). Two adjustments to close the
split-pane loop:

1. `_stop_bottle_flow` clears `tmux_state['slug']` when the
   stopped bottle was the right-pane occupant. The pane itself
   stays in place (claude exits with "container not found");
   the operator presses Enter on a different agent to
   repurpose it via respawn-pane.
2. `_render` accepts `right_pane_slug` and marks the matching
   agents-pane row with a `*` prefix + A_BOLD (when it's not
   also the focused row — focused selection still wins for
   visibility). Gives the operator a clear visual link
   between which agent the dashboard says is "active right
   now" and which one is visible to their right.

Wired through `_main_loop`: passes `tmux_state` to
`_stop_bottle_flow` on `x`, and `tmux_state.get('slug')` to
`_render` on every tick.

479 unit tests pass (1 new for the tmux_state-preservation
on non-owned stop). PRD 0021 implementation complete pending
merge.
This commit is contained in:
2026-05-26 14:29:59 -04:00
parent 4991d5b3ee
commit 2ba84c5ba0
2 changed files with 35 additions and 1 deletions
@@ -455,6 +455,20 @@ class TestStopBottleFlow(unittest.TestCase):
self.assertIn("not dashboard-owned", msg)
self.assertIn("./cli.py cleanup", msg)
def test_non_owned_does_not_touch_tmux_state(self):
# PRD 0021: a stop on an unknown slug should never clear
# the right-pane occupant tracking, even if the slugs
# happen to match (defensive — non-owned can't be in the
# right pane via the dashboard's normal flow anyway).
tmux_state = {"pane_id": "%5", "slug": "live-bbb"}
dashboard._stop_bottle_flow(
stdscr=None, # type: ignore[arg-type]
bottles={},
slug="ghost-zzz",
tmux_state=tmux_state,
)
self.assertEqual({"pane_id": "%5", "slug": "live-bbb"}, tmux_state)
class TestOperatorEditFlowGuards(_FakeHomeMixin, unittest.TestCase):
"""Chunk-4 contract: the edit flow refuses when the selected