docs: design tmux window index reset
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user