Backing out of a thread already landed on the picker rather than the menu, but
on a brand-new one: the tool re-ran the whole picker each round and put the
cursor back with a default= restore, which recovers the cursor alone and only
while the row it names still exists. Anything typed to narrow the list was gone
every time, and the screen was rebuilt around two devstate reads.
The picker moves into the tool's own loop and stays pushed, so a chat nests
above it and Esc is one pop onto the row it was opened from — filter, scroll and
all. The row builder splits out as _picker_items, and the rows are swapped in
place after every chat and every delete: an exchange moves its thread up the
recency order and a deleted history hollows its dot and demotes the row, so the
list has to be re-read, and replace_items follows the highlighted thread there.
The picker tests now read _picker_items directly rather than intercepting a
select() call, and the two Del tests drive _delete_history and assert the
rebuilt row demotes — the same coverage, one layer down from the loop.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>