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 | 96 |
1 files changed, 96 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 8de26f2..c37f250 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 @@ -10099,3 +10099,99 @@ on the server. Those are existing messages in the drafts folder, and nothing here retracts them; the four found on the user's own mail were deleted by hand as a separate act. +--- + +## 192. A sent message does not appear in the Sent view until the next sync + +**Observed (user, 2026-09-06, by hand):** "after sending, the sent message +doesn't appear immediately in the sent view." + +**Cause, verified in the code and measured on the index.** `ComposeWindow` +files the sent copy into the account's sent folder with +`DraftStore::write(folder, built.bytes, "S")` (`composewindow.cpp:1614`) and +uses the result only to test `filed.ok()`. **`filed.path` is discarded.** No +signal carries it, so nothing tells notmuch the file exists, and the Sent +view is a `path:` query over the INDEX rather than a directory listing. The +message is on disk and invisible until the next `notmuch new`, which on this +setup is a cron tick up to ten minutes away (item 184). + +Measured immediately after a send: 65 files in the account's `Sent/cur`, 64 +messages in the index for the same path. The one missing was the message just +sent. + +**This is item 158's defect, one path over, and the asymmetry is the whole +story.** That item established the rule for drafts: a saved draft is indexed +immediately because the Drafts view is a path query and a sync is too far +away. `MainWindow` wires both halves of it (`mainwindow.cpp:1273` and +`:1277`): `draftSaved` to `indexDraftFile`, and `draftRemoved` to +`removeIndexedFile`. The send path therefore already tells the worker to +DROP the draft's entry, while never telling it to ADD the sent copy's. Half +a chain, and the half that was missing is the one the user sees. + +**Approach.** A `sentCopyFiled(const QString &path)` signal from +`ComposeWindow`, emitted where the write succeeds, connected to the worker's +existing `indexDraftFile` slot exactly as `draftSaved` is. + +`indexDraftFile` is generic despite its name: it calls +`notmuch_database_index_file` and, only when a previous path is given, +removes that entry. It applies no draft-specific tags or flags, and its +`previousPath` parameter already defaults to empty, so a sent copy passes +through it correctly with no second slot and no change to the worker. The +name is worth a note rather than a rename, since renaming it touches every +existing caller and test for no behavioural gain. + +**Constraints.** + +- **Only on the success branch.** `filed.ok()` is already tested and a + failure already raises "Sent, but not filed"; indexing a path that was + never written would put a ghost in the index, which is the defect + `removeIndexedFile` exists to prevent. +- **An account with no `sent` folder configured files nothing**, so there is + no path to emit and the signal must not fire. The existing + `!account.sent.isEmpty()` guard already scopes this. +- **Indexing is not repainting.** Neither this nor the draft path re-runs the + query, so a view already on screen does not gain the row by itself. That is + a separate question from the one this item answers, and the model cannot + insert a row optimistically for a thread its query never returned (the + constraint item 170 records). Whether the Sent view should refresh itself + after a send is left open here rather than assumed. + +**Verification.** The signal and its connection are measurable: a test drives +a send and asserts the path is emitted. Asserting the message is then FINDABLE +needs a real index, which `test_notmuchworker` already has for +`indexDraftFileMakesAFileFindable`; the same property for a sent copy is the +same call and does not need a second test of libnotmuch's behaviour. + +**Built 2026-09-06, and it is the three lines the entry predicted.** +`ComposeWindow::sentCopyFiled(const QString &path)` is emitted on the success +branch of the filing write, and `MainWindow` connects it to the worker's +existing `indexDraftFile`. + +**The connection is a lambda with `m_worker` as its CONTEXT OBJECT, and that +is load-bearing rather than incidental.** `indexDraftFile` takes two arguments +while the signal carries one, and a default argument does not fill that gap +across a `connect()`, so a direct slot connection will not compile. The +context object is what decides the thread: with `m_worker` given, Qt queues +the lambda onto the worker's thread and notmuch never touches the GUI thread. +A first attempt at simplifying this to a plain call was reverted for exactly +that reason, and the comment records it, because the simplification LOOKS +harmless and reads as tidier. + +**One test, asserting on the SIGNAL rather than on a notmuch query.** +`aFiledSentCopyIsAnnouncedForIndexing` drives a real send through +`ComposeFixture` and asserts the path is emitted, that the file at that path +exists, and that it is inside the account's Sent folder. Indexing itself is +already covered against a real database by +`indexDraftFileMakesAFileFindable` in `test_notmuchworker`; what was unproven +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. + |
