diff options
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md index ed88693..ff2bbba 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md @@ -4937,3 +4937,50 @@ means something. **Item 66 did not reproduce**, which is recorded in that item rather than worked around here. The test was not widened until it failed; the negative result and what it narrows to are in item 66's own entry. + +## 66. Selecting a thread root leaves the message pane blank until a reply has been selected + +**Done 2026-08-14, unreleased.** Closed by removing the conversation view, at +the user's request. Not the defect it was filed as. + +**What it actually was.** The original note described a blank pane. Two probes +against a real database failed to reproduce that, and the user's own step-by-step +account on 2026-08-14 showed the pane was never blank: clicking an unexpanded +thread root rendered the whole CONVERSATION, stubs plus the last two messages +expanded. Clicking a reply then rendered one message, and clicking back to the +root rendered one message from then on. + +**The cause was timing, not a race.** `onThreadSelected` already preferred to +load the root's own first message, but the model learned that message id only +when the replies arrived (`ThreadNode::first`, populated in the +`threadTreeLoaded` handler). A row that had never been expanded therefore had no +id and fell through to a whole-thread render. The identical click behaved +differently once the thread had been opened, which is what the user was +reporting as inconsistency. + +**Fixed by carrying the id in the query.** `ThreadSummary::firstMessageId` comes +from `notmuch_thread_get_toplevel_messages` during the query walk, so it is +known before any expansion and the fallback is unreachable. Free: measured +indistinguishable from not collecting it over a 36,615-thread database, because +it reads the index rather than the message files. The Sent view takes the first +message the query MATCHED instead, since a Sent row stands for what the user +sent and not for whoever opened the thread; libnotmuch has no matched-messages +iterator, only a count, so that branch walks to the first +`NOTMUCH_MESSAGE_FLAG_MATCH` and stops. + +**The conversation view is gone, on the user's decision.** Asked whether to keep +it reachable another way, the user declined: "I have no use for a view that +shows the first n messages as stubs I can't expand and lets me see only the last +two replies." Worth recording that the stubs not expanding was itself a defect, +so the feature was judged in a broken state; the user chose removal anyway when +told. `NotmuchWorker::loadThread` survives with no UI caller, documented as such, +because it is a tested way to read a thread's messages with the match set +resolved. + +**Two defects came out of this and are open as items 87 and 88.** Auto mark-read +still marks the whole thread, which was coherent while a root rendered the +conversation and is not any more; and `threadAt(current.row())` answers about +the wrong thread for a reply row. The second was found the hard way: a fix for +87 was written, mutation-checked, shipped and reverted the same evening after it +marked an unrelated message read. The test that passed had asserted on a ROOT +selection, the one case where `current.row()` is correct. |
