aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
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.md47
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.