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 | 51 |
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. |
