diff --git a/docs/superpowers/specs/2026-06-11-tmux-window-index-reset-design.md b/docs/superpowers/specs/2026-06-11-tmux-window-index-reset-design.md new file mode 100644 index 0000000..f1a8a64 --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-tmux-window-index-reset-design.md @@ -0,0 +1,134 @@ +# Reset tmux window indexes + +**Date:** 2026-06-11 +**Status:** Approved + +## Goal + +Add reusable functionality that compacts tmux window indexes inside the `hqt` +tmux session so keyboard shortcuts such as `Alt+1`, `Alt+2`, and `Alt+3` stay +useful after windows have been closed. + +This reset affects only tmux window indexes. It does not change database session +IDs, `tmux_session_name`, harness conversation IDs, nicknames, panes, or running +processes. + +## Behavior + +The reset operation works on the configured `hqt` tmux session: + +- The home/TUI window always ends at tmux index `0`. +- Every non-home window keeps its current name, pane, process, and content. +- Non-home windows are sorted by their current tmux index. +- Those non-home windows are moved into contiguous indexes starting at `1`. + +Example: + +```text +0: ⌂ HQT +1: hqt-foo +4: hqt-bar +9: hqt-baz +``` + +becomes: + +```text +0: ⌂ HQT +1: hqt-foo +2: hqt-bar +3: hqt-baz +``` + +If the home window is not already at `0`, it is moved to `0` first, then the +remaining windows are compacted after it in their original index order. + +## Architecture + +Add the reset at the tmux layer rather than the session or database layer. +Existing app and service code targets windows by name, so changing tmux indexes +should not disturb attach, stop, delete, status polling, or label syncing. + +### Runner + +Add a low-level method on `TmuxRunner`, for example: + +```python +async def reset_window_indexes(self, home_window: str) -> bool: + ... +``` + +The method should: + +1. List windows for `self.session_name` with `#{window_id}`, + `#{window_index}`, and `#{window_name}`. +2. Find the row whose name matches `home_window`. +3. Move the home window to index `0` if needed. +4. Sort all other windows by their original index. +5. Move each non-home window to `1..N` in that order. + +Use `move-window` with `window_id` as the source target so changing one index +cannot make a later move target the wrong window. The destination should target +the configured tmux session and explicit index. + +The implementation should be idempotent: running it against an already compact +session should return success without changing anything meaningful. + +### Manager + +Add a thin delegate on `TmuxManager`, for example: + +```python +async def reset_window_indexes(self, home_window: str) -> bool: + return await self.runner.reset_window_indexes(home_window) +``` + +No `SessionService` change is needed for the initial implementation. This is a +tmux maintenance operation, not a session lifecycle operation. + +### Command Surface + +Implement the reusable operation now and defer user-facing wiring. A command +palette is planned separately, so this feature should expose a callable method +that the future command can invoke. + +Do not add a TUI keybinding or footer item in this change. + +## Error Handling + +- If the configured tmux session does not exist or cannot be listed, return + failure and leave the database untouched. +- If the home window is missing, return failure rather than guessing which + window should become index `0`. +- If a `move-window` command fails, return failure. The operation may have + partially moved tmux windows before the failure; this is acceptable because + tmux indexes are maintenance metadata and no persisted hqt state is changed. +- Log tmux stderr for failed operations to aid debugging. + +## Testing + +Add focused tests in `tests/test_tmux.py`: + +- Missing home window returns failure and performs no moves. +- Already compact windows return success. +- Sparse indexes compact in current index order: + `0 home, 1 foo, 4 bar, 9 baz` moves `bar` to `2` and `baz` to `3`. +- Home initially not at `0` is moved to `0`, and all other windows compact to + `1..N` in original order. +- Moves target stable window identities, not mutable index-only targets. + +The implementation must pass the project checks after code changes: + +```bash +uv run ruff format src tests +uv run ruff check src tests +uv run ty check +``` + +## Out of Scope + +- Changing database primary keys. +- Changing `Session.tmux_session_name`. +- Changing harness session IDs or resume behavior. +- Adding command palette UI, keybindings, or footer labels. +- Enabling permanent tmux automatic renumbering.