Skip to content

feat(ui): per-entry selection in the finished-review cleanup - #50

Merged
sdavisde merged 6 commits into
mainfrom
qol/cleanup-selection
Jul 30, 2026
Merged

feat(ui): per-entry selection in the finished-review cleanup#50
sdavisde merged 6 commits into
mainfrom
qol/cleanup-selection

Conversation

@sdavisde

Copy link
Copy Markdown
Owner

Fifth PR of the submit/modals chain (stacked on #49). The finished-review cleanup modal (X) was all-or-nothing — no way to keep one review and delete the rest, despite per-entry unpublished-work warnings already being displayed.

Changes

  • Per-entry checkboxes, all selected by default; Space toggles the highlighted entry; j/k/arrows move a new clamped cursor (rendered via ListState reverse-video highlight). Title reads N of M selected; confirm hint reads delete selected (N) and dims at zero.
  • Enter runs the existing per-entry delete loop on the selected subset only; zero selected is a true no-op. Deselected entries are untouched on disk and simply reappear in the next finished-set recompute. Warnings stay attached to their entries regardless of selection.
  • Cursor lives in Mode::CleanupReviews { cursor } (keeps Mode: Copy, matching the launcher's pattern); the selection mask lives on App beside the entry snapshot. Keys added to the shared CLEANUP_REVIEWS_KEYS table — footer and ? help pick them up generically.
  • Safety unchanged: refuses during remote ops/PR checkouts; deletion stays synchronous on the render thread (documented, deliberate).

Tests

9 added/updated: toggle + count, cursor walk + clamping, zero-selected no-op, default-all-checked render, deselected-keeps-warning render, drift test for the new table rows, and a real-git tempdir integration test proving a confirm with a deselected entry deletes exactly the selected subset and the deselected review's worktree/branch/state survive.

Tracking: ENG-177 (Linear). All four gates pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB

redquill test and others added 5 commits July 30, 2026 00:08
The submit-review modal rendered one unscrollable Paragraph in a 72%x72%
box, so a review batch taller than the modal was clipped with no scroll
and no indicator — in the one surface whose job is confirming exactly
what will be sent.

The body now scrolls: Up/Down by a line, PageUp/PageDown by a real
viewport, added to SUBMIT_FORGE_KEYS so the help overlay and footer stay
in sync. Printable characters still fall through to the summary field,
which is why the scroll keys are the arrow/page keys and not j/k. The
offset is a Cell clamped to the content at render time (the help
overlay's model) and reset on every fresh open, and when content is
clipped the modal spends one row top and bottom on a marker naming how
many lines are hidden in that direction. A blocked request-changes
confirm jumps to the bottom so its hint can't land off-screen.

Lines are pre-wrapped rather than handed to Paragraph's Wrap, so the
count the scroll math clamps against is the row count the terminal
really shows — otherwise a long batch's last rows stay unreachable and
the hidden-line count understates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
Replies in the submit-review modal previewed as `thread <id>: <body>`
— a raw numeric id is meaningless at confirm time. Resolve each
reply's target from the fetched thread overlay instead: root author
and anchor (`path:line`, or `path (file-level)` once outdated), same
conventions the thread overlay itself uses. Falls back to the id form
only when the thread has dropped out of the overlay (e.g. a failed
refresh).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
The submit modal's summary was a single-line push/pop field, so the
substantive body of a forge review was capped at one line with no cursor
motion or word-delete. `Ctrl-e` now hands it to the Compose editor,
seeded with the text so far; saving returns to the modal with the whole
body, cancelling leaves it as it was.

The field shows the summary's first line plus a dim count of the lines it
keeps off screen, and once the summary is multi-line the in-modal
push/pop gestures step aside for `Ctrl-e` rather than silently mutate a
line the reviewer can't see. Direct typing of a one-line summary is
unchanged, as is Enter to confirm.

Compose's two-mode `thread_id: Option<u64>` discriminant becomes a
three-variant `ComposeKind`, so no combination of flags can describe a
compose that is an annotation and a summary at once. The change is folded
in here rather than split out because the third variant is the feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
A stopped submit left everything in one transient status line: honest
about the counts, silent about which comments actually landed, so the
only way to find out was to open the PR in a browser.

The sequence now records what it set out to send (`SubmitAttempt`) into
its report before it starts writing. Joined against the published and
draft lists, that makes each item's fate knowable — published, pending
draft, or never reached — rather than inferred from a diff of local
state. A run that stops opens a read-only modal grouping every item
under its outcome, named exactly as the submit preview named it
(including the humanized "to <author> @ <path>:<line>" reply labels),
with the review itself leading the not-sent group when the verdict
never landed and the diagnostic underneath.

The one-line status is unchanged and still set in both cases; a submit
that publishes everything opens no modal.

Keys come from a new SUBMIT_RESULT_KEYS table: Enter/Esc/q dismiss, U
reopens the submit modal to retry the remainder, and j/k/arrows plus
the page keys scroll (no summary field here, so the letter keys are
free, unlike the submit modal). Long lists scroll with the render-time
clamp the help overlay uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
Adds a cursor and per-entry checkbox to the finished-review cleanup
modal (X on the Pull Requests tab): j/k/arrows move the highlight,
Space toggles the highlighted entry (all checked by default), and
confirm deletes only the selected subset. Zero-selected Enter is a
no-op; deselected entries keep their unpublished-work warnings
visible since the warning belongs to the entry, not to the deletion.

New CleanupReviewsAction rows (MoveDown/MoveUp/Toggle) go through the
shared CLEANUP_REVIEWS_KEYS table so the footer strip and ? help stay
in sync automatically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
Base automatically changed from qol/submit-04-result-view to main July 30, 2026 13:46
Takes main's integrated side everywhere: 20 help sections, the unioned
modes.rs imports, and the scrubbed section comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWX3hBxudUxZooeph9ngEB
@sdavisde
sdavisde merged commit 9d1bf50 into main Jul 30, 2026
5 checks passed
@sdavisde
sdavisde deleted the qol/cleanup-selection branch July 30, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant