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.md55
1 files changed, 55 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 ff2bbba..730c722 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
@@ -4984,3 +4984,58 @@ 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.
+
+## 74. The first query after boot sits on "Searching..." for seconds
+
+**Observed (user, 2026-08-11):** the first start of the day takes noticeably
+longer to show its default view, and the delay happens while the status bar
+reads "Searching...".
+
+**Cause: the notmuch index paging in from disk, not this application's code.**
+Measured by instrumenting `main()`, the `MainWindow` constructor and
+`NotmuchWorker::runQuery` behind an environment variable, then running the same
+`tag:inbox` query (4444 threads) warm and again after evicting the index from
+the page cache with `posix_fadvise(POSIX_FADV_DONTNEED)`:
+
+| phase | warm | cold |
+|---|---|---|
+| `notmuch_query_search_threads` returns | 0 ms | 411 ms |
+| first batch of 200 reaches the model | 10 ms | 642 ms |
+| walk complete, all 4444 threads | 154 ms | 5714 ms |
+
+A 37x difference over the identical code path. The index measured 1.1 GB. The
+in-process startup costs nothing by comparison: `QApplication` in 20 ms, the
+whole `MainWindow` constructor in ~120 ms, and the window is shown and
+interactive at ~196 ms in both the warm and the cold run.
+
+**There is nothing to fix in the query path**, and the measurement exists mainly
+so this is not re-investigated. Two things it did establish that are worth
+keeping. The default startup view is whichever saved query `startup_query` names,
+defaulting to `Unread`, so a user with an empty unread view never sees this at
+all and a user whose default is Inbox always does. And batching already works:
+rows land from 642 ms cold, long before the 5714 ms finish.
+
+**The one real defect it exposed is the status bar.** "Searching..." is set once
+in `runQuery` and cleared only on `queryFinished`, so it keeps claiming the
+query is running for the full 5.7 s while rows are visibly arriving behind it.
+That makes a slow query read as a frozen one. The fix is to update the text per
+batch with the count so far rather than holding one string, which changes no
+timing and only stops the bar from lying.
+
+**The user declined this on 2026-08-11**, having asked for the explanation
+rather than a change. Recorded as open because the status bar is still
+inaccurate, not because anything is expected to happen.
+
+**Size: XS** for the status bar. The cold-cache cost itself is not addressable
+here and should not be attempted: prefaulting 1.1 GB at startup to make one
+query look fast is a worse trade than the wait.
+
+**Closed 2026-08-15, the status-bar half only.** `onThreadsReady` now sets the
+bar to `Searching... %n thread(s)` from the model's own row count after each
+batch, so a slow query reports progress instead of looking frozen. The refresh
+branch returns before that line, which keeps a background refresh silent as
+`onQueryFinished` already does; that silence is a test of its own, and it fails
+when the write is moved above the guard.
+
+The cold-cache cost measured above was not touched and should not be. Nothing
+about the timing changed.