Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.opencode/
.clawmux/
target/
.clawdmux/
*.log
.env
docs/research.md
docs/research.md
26 changes: 26 additions & 0 deletions src/tui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,9 @@ pub fn handle_input(event: Event, app: &mut App) -> Option<AppMessage> {
KeyCode::Tab => {
app.active_tab = (app.active_tab + 1) % 9;
}
KeyCode::BackTab => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BackTab is already used by the Research Tab to switch between Plan and Act modes. I like your proposal to use BackTab for tab navigation. Do you mind changing the short-cut used for Plan/Act in the research tab as part of this PR? Maybe just a simple Shift+p to switch modes?

Copy link
Author

@strategicpause strategicpause Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a commit missing? I only see KeyCode::Tab referenced here and KeyCode::BackTab is only referenced in this PR. There also doesn't seem to be a research Plan & Act mode in the research tab which suggests something might be missing from the main branch.

app.active_tab = (app.active_tab + 8) % 9;
}
_ => {}
}
}
Expand Down Expand Up @@ -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};
Expand Down
188 changes: 188 additions & 0 deletions tasks/11.2.md
Original file line number Diff line number Diff line change
@@ -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` |