diff --git a/.gitignore b/.gitignore index 51b81e2..7e8143d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .opencode/ +.clawmux/ target/ .clawdmux/ *.log .env -docs/research.md \ No newline at end of file +docs/research.md diff --git a/src/tui/mod.rs b/src/tui/mod.rs index c2aba48..e3d0d55 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1493,6 +1493,9 @@ pub fn handle_input(event: Event, app: &mut App) -> Option { KeyCode::Tab => { app.active_tab = (app.active_tab + 1) % 9; } + KeyCode::BackTab => { + app.active_tab = (app.active_tab + 8) % 9; + } _ => {} } } @@ -1688,6 +1691,29 @@ mod tests { assert_eq!(app.active_tab, 0); } + #[test] + fn test_backtab_cycles_tabs_backward() { + let mut app = App::test_default(); + app.active_tab = 2; + + let backtab = key_event(KeyCode::BackTab, KeyModifiers::SHIFT); + handle_input(backtab.clone(), &mut app); + assert_eq!(app.active_tab, 1); + + handle_input(backtab.clone(), &mut app); + assert_eq!(app.active_tab, 0); + } + + #[test] + fn test_backtab_wraps_from_first_to_last() { + let mut app = App::test_default(); + assert_eq!(app.active_tab, 0); + + let backtab = key_event(KeyCode::BackTab, KeyModifiers::SHIFT); + handle_input(backtab, &mut app); + assert_eq!(app.active_tab, 8); + } + /// Builds a minimal App with one story containing one task that has one unanswered question. fn app_with_task_and_question() -> App { use crate::tasks::models::{Question, Task, TaskId, TaskStatus}; diff --git a/tasks/11.2.md b/tasks/11.2.md new file mode 100644 index 0000000..d989fde --- /dev/null +++ b/tasks/11.2.md @@ -0,0 +1,188 @@ +Story: 11. Enhancements +Task: 11.2 +Status: COMPLETED +Assigned To: [Human] + +## Description + +Add Shift+Tab (BackTab) support to navigate tabs in reverse. Currently, pressing Tab +cycles forward through the 9 right-pane tabs. BackTab should cycle backward, wrapping +from the first tab to the last. + +In `src/tui/mod.rs`, add a `KeyCode::BackTab` arm next to the existing `KeyCode::Tab` +arm in `handle_task_list_input`. The logic should decrement `app.active_tab` with +wrapping: `(app.active_tab + 8) % 9` (i.e., add TAB_COUNT - 1 modulo TAB_COUNT). + +Tests: test_backtab_cycles_tabs_backward, test_backtab_wraps_from_first_to_last. + +## Questions + +Q1 [Code Quality Agent]: Shell commands are being denied so I cannot run cargo fmt, cargo clippy, or cargo test. Could you run these three commands and share the output so I can complete the quality review? +A1: + +Q2 [Code Quality Agent]: Shell commands are being denied so I cannot run cargo fmt, cargo clippy, or cargo test. Could you run these three commands and share the output (or confirm they pass) so I can complete the quality review? +A2: All command ran succesfully. + +## Design + +### Overview +Add `KeyCode::BackTab` (Shift+Tab) handling to cycle `app.active_tab` backward through the 9 right-pane tabs, wrapping from tab 0 to tab 8. + +### Current State +- `handle_task_list_input` in `src/tui/mod.rs` (line ~1493) has a `KeyCode::Tab` arm that increments `active_tab`: `(app.active_tab + 1) % 9`. +- There are 9 tabs (indices 0-8) defined in `tab_titles()` in `src/tui/tabs/mod.rs`. +- No `TAB_COUNT` constant exists; the magic number `9` is used inline. +- `KeyCode::BackTab` is not referenced anywhere in the codebase. + +### Changes Required + +#### 1. `src/tui/mod.rs` - `handle_task_list_input` function (~line 1495) +Add a `KeyCode::BackTab` arm immediately after the existing `KeyCode::Tab` arm: +```rust +KeyCode::BackTab => { + app.active_tab = (app.active_tab + 8) % 9; +} +``` +This uses modular arithmetic `(current + 8) % 9` which is equivalent to decrementing with wrap (avoids underflow on `usize`). + +#### 2. `src/tui/mod.rs` - Tests +Add two tests following the existing `test_handle_input_tab_cycles` pattern: +- `test_backtab_cycles_tabs_backward`: Start at tab 2, press BackTab twice, assert tab goes 2 -> 1 -> 0. +- `test_backtab_wraps_from_first_to_last`: Start at tab 0, press BackTab, assert tab becomes 8. + +Both tests use `key_event(KeyCode::BackTab, KeyModifiers::SHIFT)` and call `handle_input`. + +### Design Decisions +- No `TAB_COUNT` constant is introduced since the existing code uses the literal `9` and refactoring is out of scope for this task. +- No changes needed to `handle_input` (the top-level dispatcher) since `BackTab` will flow through to `handle_task_list_input` via the existing default path. +- The questions-tab `Tab` handler (line ~1094) cycles between answer textareas, not tabs. BackTab support for that context is out of scope. + +### Files Modified +- `src/tui/mod.rs` (1 new match arm + 2 new tests) + +## Work Log + +1 2026-03-09T04:22:16.733815+00:00 [Human] Status changed to IN_PROGRESS +2 2026-03-09T04:22:16.733817+00:00 [Human] Assigned to Intake Agent +3 2026-03-09T04:22:36.929466+00:00 [Intake Agent] Reviewed task 11.2: all fields are present and unambiguous — clear description, exact code location confirmed (KeyCode::Tab arm at line 1493 in src/tui/mod.rs), correct wrapping formula, no existing BackTab handling, and two measurable test cases specified. +4 2026-03-09T04:22:36.929474+00:00 [Intake Agent] Assigned to Design Agent +5 2026-03-09T04:23:25.781198+00:00 [Design Agent] BackTab support requires adding a single KeyCode::BackTab match arm in handle_task_list_input that decrements active_tab with wrapping, plus two unit tests. +6 2026-03-09T04:23:25.781213+00:00 [Design Agent] Assigned to Planning Agent +7 2026-03-09T04:23:50.750728+00:00 [Planning Agent] Add a KeyCode::BackTab match arm in handle_task_list_input and two unit tests for backward tab cycling. +8 2026-03-09T04:23:50.750747+00:00 [Planning Agent] Assigned to Implementation Agent +9 2026-03-09T04:24:52.349565+00:00 [Implementation Agent] Added KeyCode::BackTab match arm in handle_task_list_input with (active_tab + 8) % 9 wrapping logic and two unit tests (test_backtab_cycles_tabs_backward, test_backtab_wraps_from_first_to_last) in src/tui/mod.rs. +10 2026-03-09T04:24:52.349572+00:00 [Implementation Agent] Assigned to Code Quality Agent +11 2026-03-09T04:25:51.271961+00:00 [Code Quality Agent] Assigned to Human +12 2026-03-09T04:26:43.609106+00:00 [Human] Assigned to Code Quality Agent +13 2026-03-09T04:32:01.476763+00:00 [Human] Task resumed at Code Quality Agent +14 2026-03-09T04:32:31.047583+00:00 [Code Quality Agent] Assigned to Human +15 2026-03-09T04:35:56.069252+00:00 [Human] Assigned to Code Quality Agent +16 2026-03-09T04:36:19.781912+00:00 [Code Quality Agent] BackTab implementation and tests are correct, properly placed, and all quality checks (fmt, clippy, test) pass. +17 2026-03-09T04:36:19.781922+00:00 [Code Quality Agent] Assigned to Security Review Agent +18 2026-03-09T04:36:40.166457+00:00 [Security Review Agent] No security issues found; the BackTab implementation is a purely local UI keyboard handler using safe bounded modular arithmetic with no external input, injection surfaces, or credential exposure. +19 2026-03-09T04:36:40.166468+00:00 [Security Review Agent] Assigned to Code Review Agent +20 2026-03-09T04:37:30.626680+00:00 [Code Review Agent] Code passes review with no blocking issues. + +Files changed: `src/tui/mod.rs` (1 new match arm, 2 new tests) + +### Implementation Review + +**New match arm (`KeyCode::BackTab`)** at line 1496: +- Placed immediately after the existing `KeyCode::Tab` arm — correct location within `handle_task_list_input`. +- Uses `(app.active_tab + 8) % 9` for backward wrapping, which is the standard `usize`-safe modular decrement. Mathematically equivalent to `(n - 1) mod 9` without underflow risk. +- The magic number `9` matches the existing `KeyCode::Tab` arm and the 9 entries returned by `tab_titles()` in `src/tui/tabs/mod.rs`. Consistent with current codebase style (no `TAB_COUNT` constant exists). + +**Tests:** +- `test_backtab_cycles_tabs_backward`: Starts at tab 2, presses BackTab twice, asserts 2→1→0. Correct. +- `test_backtab_wraps_from_first_to_last`: Starts at tab 0, presses BackTab, asserts result is 8. Correct. +- Both tests use `key_event(KeyCode::BackTab, KeyModifiers::SHIFT)` and call `handle_input`, following the exact same pattern as the existing `test_handle_input_tab_cycles` test. +- Human confirmed `cargo fmt`, `cargo clippy`, and `cargo test` all pass. + +**Scope check:** +- The questions-tab `Tab` handler (line ~1094) cycles between answer textareas in a different input context (`QuestionsAnswering`). Not adding BackTab there is correct per the task scope. +- No other input handlers reference `KeyCode::Tab` in a way that would need a BackTab counterpart. + +**Bugs/Risks:** None found. The arithmetic is correct for all 9 tab indices (0–8). + +**Adherence to project standards:** +- Idiomatic Rust, minimal change. ✅ +- No warnings, no clippy issues (confirmed by human). ✅ +- Tests added for the new feature. ✅ +- No stubs or TODOs. ✅ +- No print/eprint statements. ✅ + +**Non-blocking suggestions:** None. The change is minimal and correct. +21 2026-03-09T04:37:30.626700+00:00 [Code Review Agent] Status changed to PENDING_REVIEW +22 2026-03-09T04:37:30.626701+00:00 [Code Review Agent] Assigned to Human +23 2026-03-09T04:38:17.059237+00:00 [Human] Status changed to COMPLETED + +## Implementation Plan for Task 11.2 — BackTab Support + +### Step 1: Add KeyCode::BackTab arm in `src/tui/mod.rs` + +File: `src/tui/mod.rs`, function `handle_task_list_input`, line ~1495 (immediately after the existing `KeyCode::Tab` arm closing brace on line 1494). + +Insert a new match arm: +```rust +KeyCode::BackTab => { + app.active_tab = (app.active_tab + 8) % 9; +} +``` + +This goes between the `KeyCode::Tab` closing brace (line 1494) and the `_ => {}` arm (line 1495). The existing `_ => {}` and closing braces remain unchanged. + +### Step 2: Add test `test_backtab_cycles_tabs_backward` in `src/tui/mod.rs` + +Insert after the existing `test_handle_input_tab_cycles` test (which ends at line ~1688). Follow the same pattern: + +```rust +#[test] +fn test_backtab_cycles_tabs_backward() { + let mut app = App::test_default(); + app.active_tab = 2; + + let backtab = key_event(KeyCode::BackTab, KeyModifiers::SHIFT); + handle_input(backtab.clone(), &mut app); + assert_eq!(app.active_tab, 1); + + handle_input(backtab.clone(), &mut app); + assert_eq!(app.active_tab, 0); +} +``` + +### Step 3: Add test `test_backtab_wraps_from_first_to_last` in `src/tui/mod.rs` + +Insert immediately after the previous new test: + +```rust +#[test] +fn test_backtab_wraps_from_first_to_last() { + let mut app = App::test_default(); + assert_eq!(app.active_tab, 0); + + let backtab = key_event(KeyCode::BackTab, KeyModifiers::SHIFT); + handle_input(backtab, &mut app); + assert_eq!(app.active_tab, 8); +} +``` + +### Step 4: Validate + +Run the following commands and confirm zero errors/warnings: +1. `cargo fmt` +2. `cargo build` +3. `cargo test` +4. `cargo clippy` + +### Step 5: Update documentation + +Update `README.md` and/or `docs/design.md` to mention Shift+Tab reverse tab navigation if keyboard shortcuts are documented there. + +### Summary of Changes + +| File | Change | +|---|---| +| `src/tui/mod.rs` line ~1495 | Add `KeyCode::BackTab` match arm with `(app.active_tab + 8) % 9` | +| `src/tui/mod.rs` tests section (~line 1688) | Add `test_backtab_cycles_tabs_backward` | +| `src/tui/mod.rs` tests section (~line 1688) | Add `test_backtab_wraps_from_first_to_last` | +