aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-07 10:12:10 +0200
committerDanilo M. <danix@danix.xyz>2026-09-07 10:12:10 +0200
commita00d35452fcef8c8bc723ba7045fcae38fe8f315 (patch)
tree510d0bb2d9265367648f38bbf43186f471379716 /docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
parent6326030b7179580b934ba852b0fd98577bfb464a (diff)
downloadqtmaildir-a00d35452fcef8c8bc723ba7045fcae38fe8f315.tar.gz
qtmaildir-a00d35452fcef8c8bc723ba7045fcae38fe8f315.zip
fix: refresh the current query when the index changes
Item 192's second half, answered by the user: indexing is not repainting. The sent copy became findable the moment it was sent and a Sent view already on screen still did not show it, because nothing re-ran the query. The model cannot insert the row optimistically either, since item 170's constraint applies: the query never returned that thread. NotmuchWorker::indexChanged() is emitted at the end of both indexDraftFile() and removeIndexedFile(), the only two entry points that change what a path query would return without any query having run. MainWindow connects it to refreshCurrentQuery(), which covers all three gestures a path view can miss: a sent copy indexed, a draft saved, a draft's entry dropped on send. Wiring only the indexing half would have left a ghost draft row visible in a Drafts view after a send. The signal carries nothing, so it cannot invite an optimistic insert. It is emitted after the database closes, so a refresh reaching notmuch on the next turn of the event loop cannot race the write handle. refreshCurrentQuery() rather than runCurrentQuery(): a send must not clear the selection, the expansions or the undo stack of the window behind the composer. aSentMessageAppearsInASentViewAlreadyOnScreen drives a real send through a worker-backed window, asserts the Sent view is empty first, and asserts the row arrives with no second returnPressed() and no sync. Mutation-checked by disabling the connection. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWeBw3UqxpBktSc1AkZ6ir
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.md51
1 files changed, 44 insertions, 7 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 c37f250..6613059 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
@@ -10187,11 +10187,48 @@ was that anything ever CALLS it for a sent copy. Asserting the path exists is
not decoration: announcing a path that was never written is the ghost entry
`removeIndexedFile()` exists to undo. Mutation-checked by removing the emit.
-**Left open deliberately, and it is a real question rather than an
-omission.** Indexing is not repainting. A Sent view already on screen does not
-gain the row from this, because neither this nor the draft path re-runs the
-query, and the model cannot insert a row optimistically for a thread its query
-never returned (item 170's constraint). The message is now findable the moment
-it is sent, which is what the user reported; whether the view should also
-refresh itself is a separate decision.
+**The open question was answered by the user on 2026-09-07: the Sent view
+should refresh and show the message.** Indexing is not repainting, and the
+first build left it there: the message became findable the moment it was sent
+and a Sent view on screen still did not show it, because nothing re-ran the
+query. The model cannot insert the row optimistically either, since item 170's
+constraint applies, the query never returned that thread.
+
+**`NotmuchWorker::indexChanged()`, connected to
+`MainWindow::refreshCurrentQuery()`.** Emitted at the end of BOTH
+`indexDraftFile()` and `removeIndexedFile()`, which are the only two entry
+points that change what a path query would return without any query having
+run. One connection covers all three gestures a path view can miss: a sent
+copy indexed, a draft saved, a draft's entry dropped on send. Wiring only the
+indexing half would have left a ghost draft row visible in a Drafts view after
+a send, which is the same defect one signal over.
+
+Four decisions in it that are not incidental:
+
+- **The signal carries nothing.** A payload would invite an optimistic insert,
+ which item 170 rules out; the refresh re-runs the whole query, so which file
+ moved is not the UI's question.
+- **`refreshCurrentQuery()`, never `runCurrentQuery()`.** A send must not clear
+ the selection, the expansions, the undo stack or the message being read in
+ the window behind the composer. The refresh reconciles instead of replacing,
+ returns early when no query has run, and leaves a view of another folder
+ showing no change at all.
+- **Emitted AFTER the database is closed**, so a refresh reaching the database
+ on the next turn of the event loop cannot race the write handle. notmuch
+ permits one open handle per process, the same ordering `applyTags()` and
+ `moveMessages()` obey.
+- **A send fires it twice**, once for the sent copy and once for the draft
+ removal. Both are queued and serialize, and the generation counter makes the
+ second supersede the first, so the cost is one redundant query and the final
+ state is correct. Not worth a coalescing timer.
+
+**One test, end to end through a real send.**
+`aSentMessageAppearsInASentViewAlreadyOnScreen` builds a worker-backed window
+with a sending account whose `sent` is configured, runs the Sent view's own
+`path:` query, asserts it is EMPTY (a test starting from a non-empty view
+could not tell a refresh from a row that was already there), composes and
+sends, and asserts the row arrives with no further gesture: no second
+`returnPressed()`, no sync. A test that re-ran the query by hand would pass
+against the defect. Mutation-checked by disabling the connection, which fails
+on the row count.