diff --git a/docs/superpowers/specs/2026-06-05-terminal-review-design.md b/docs/superpowers/specs/2026-06-05-terminal-review-design.md new file mode 100644 index 0000000..7c2aa08 --- /dev/null +++ b/docs/superpowers/specs/2026-06-05-terminal-review-design.md @@ -0,0 +1,99 @@ +# Terminal-style review controls + +**Date:** 2026-06-05 +**Status:** Approved (design) + +## Problem + +Review feedback today is a multi-line `TextBox` plus four buttons (Approve / Reject / +Park / Cancel) tucked into the WorkConsole **Session** tab +(`WorkConsole.axaml:169-193`). It feels disconnected from the live terminal. Entering +feedback should feel like typing into the terminal, with action buttons docked at the +bottom — and merge/approve actions should live in an obvious, dedicated place. + +## Goal + +- Type review feedback directly in the **Output (terminal)** tab, prompt-style. +- Bottom-docked action strip on the terminal: `[Retry]` `[Reset]`. +- Move all git/merge/worktree actions (including **Approve**) into a new **Git** tab so + it is obvious where each action lives. + +## Tab structure + +Three tabs in WorkConsole: **Output** · **Git** · **Session**. + +| Tab | Contents | +| --- | --- | +| **Output** | Live `Log` (unchanged) + new review footer (below), footer gated on `IsWaitingForReview`. | +| **Git** | The current "Merge & worktree" block — merge-target dropdown, mergeability indicator, **Approve**, Open Diff, Merge, Worktree, Review Combined Diff, Merge All Subtasks. Visibility gated on `ShowMergeSection` / `IsWaitingForReview` as today. | +| **Session** | Child outcomes + empty-state only. | + +### ViewModel changes (`DetailsIslandViewModel`) + +- Add `public bool IsGitTab => SelectedTab == "git";` +- Add `[NotifyPropertyChangedFor(nameof(IsGitTab))]` alongside the existing + `IsOutputTab` / `IsSessionTab` notifications on `SelectedTab` (`:139-144`). +- `SelectTab` already accepts a string parameter — no change beyond the new `"git"` + value wired from XAML. +- No command renames (avoids breaking hand-rolled test fakes). + +## Terminal footer (Output tab) + +A `Border` docked `Bottom` inside the Output tab body, visible only when +`IsWaitingForReview`: + +- Background `Surface2Brush`, top border `LineBrush` (`BorderThickness="0,1,0,0"`). +- A `❯` prompt-prefix `TextBlock` (mono, `TextMuteBrush`) + a borderless mono `TextBox`: + - Bound `Text="{Binding ReviewFeedback, Mode=TwoWay}"`. + - `AcceptsReturn="True"`, `TextWrapping="Wrap"`, transparent background, no border. + - Starts ~1 line tall; grows with content up to `MaxHeight≈160`, then scrolls. + - `PlaceholderText` e.g. "Feedback for the next run…". +- Right-aligned button strip: + - `[Retry]` — `Classes="btn accent"` → `RejectReviewCommand`. + - `[Reset]` — `Classes="btn"` → `ParkReviewCommand`. + +`[Accept]` is **not** in the footer; approval happens on the Git tab via +`ApproveReviewCommand`. The old `Cancel` review button is dropped from this UI; cancel +remains reachable through the task's existing cancel control (`CancelReviewCommand` +stays on the ViewModel, just not surfaced here). + +### Enter handling (`WorkConsole.axaml.cs`) + +- Handle `KeyDown` on the input `TextBox`: + - **Enter** without Shift → execute `RejectReviewCommand` (if it can execute) and set + `e.Handled = true`. + - **Shift+Enter** → fall through to default behavior (inserts newline). +- `RejectReviewAsync` already returns early on whitespace-only feedback + (`DetailsIslandViewModel.cs:1464`), so pressing Enter with an empty prompt is a no-op + with no extra guard needed. + +## Command mapping + +| Button | Location | Command | Effect | +| --- | --- | --- | --- | +| `[Retry]` | Output footer | `RejectReviewCommand` | Reject-to-queue with feedback; resumes the session (Queued). | +| `[Reset]` | Output footer | `ParkReviewCommand` | Park back to Idle. | +| `[Approve]` | Git tab | `ApproveReviewCommand` | Merge `SelectedMergeTarget` → Done (conflict keeps it in review). | + +## Copy / empty state + +- Update the Session empty-state text (`WorkConsole.axaml:270`) — it currently says + "review and merge controls appear here once the run finishes", which is no longer + accurate. Reword to reflect that only outcomes live on Session. +- Button labels remain literal strings (`Retry`, `Reset`, `Approve`), matching the + existing review buttons (no new localization keys). + +## Out of scope + +- No changes to worker-side review/merge logic or `IWorkerClient` signatures. +- No merge-target selector duplicated into the terminal footer (Approve uses the Git + tab dropdown / default target). +- No command renames on the ViewModel. + +## Testing / verification + +- Build `ClaudeDo.App` and `ClaudeDo.Worker` in `-c Release`. +- Manual visual verification (must be flagged — cannot be auto-verified): + - Footer appears only in `WaitingForReview`, on the Output tab. + - Enter sends Retry; Shift+Enter inserts a newline; empty Enter does nothing. + - Git tab shows Approve + merge/worktree controls; Session shows only outcomes.