diff options
Diffstat (limited to 'docs/superpowers/plans')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md | 92 | ||||
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 96 |
2 files changed, 93 insertions, 95 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 f2b977b..ae6443d 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 @@ -7942,3 +7942,95 @@ what turns a stale path into two server-side MESSAGES rather than one replaced file. That wants its own item. A draft's id is not yet the sent message's id, so changing it is not obviously free. + +## 104. Mail visible in Thunderbird never reaches qtmaildir + +**Observed (user, from the notes):** "sync doesn't work compared to thunderbird. +New mail received on thunderbird did not appear in qtmaildir. Need to investigate +further." + +**Cause: ESTABLISHED 2026-08-25, and it was this repository after all.** This +entry previously named mbsync's folder `Patterns` as the leading theory and +concluded "most likely not a code change here at all". That was wrong; the +superseded reasoning is kept at the bottom because the reproduction is what +overturned it. + +**`NotmuchWorker` opened one read-only notmuch handle and kept it for the +process lifetime.** A read-only handle is a Xapian SNAPSHOT taken when it is +opened; it never observes a write made by another process afterwards. The sync +script's `notmuch new` is exactly such a process, so every query the worker +answered after startup was served from the index as it stood when the +application launched. `openReadOnly()` returned early on `if (m_db) return +true;` and there was no `notmuch_database_reopen` anywhere in the tree. + +This accounts for every symptom, including the ones that defeated three earlier +theories during the diagnosis: + +- The post-sync refresh found nothing, so `refreshCurrentQuery()` and + `ThreadListModel::reconcile()` were each suspected in turn. Both are correct. +- A query the user typed BY HAND also found nothing. That is what rules out the + model, the generation counter and the account scope together: a fresh query + clears the model and re-runs from scratch, and it still hits the same stale + handle. +- A restart showed the mail instantly, with no sync in between. +- Tag WRITES were never affected, which is why the defect reads as "reading is + broken" rather than "notmuch is broken". `applyTags` opens its own read-write + handle per call, so it always sees current data. + +**Why it survived from 2026-08-16 to 2026-08-25.** The symptom needs mail to +arrive from outside the process while the window stays open, which is the +ordinary way this application is used and the one thing no test did: every +fixture opens a worker, queries it, and drops it. `TestNotmuchWorker::runQuery()` +builds a FRESH worker per call, so the suite was structurally incapable of +reproducing it, and a test written through that helper passes against the bug. + +**Fixed** in `NotmuchWorker::openReadOnly()`: when a handle already exists, +`notmuch_database_reopen(m_db, NOTMUCH_DATABASE_MODE_READ_ONLY)` before +returning it. Every read path begins by asking for the handle, so one call +covers all of them; putting it at the call sites instead would be one more +place to forget. A reopen failure is deliberately NOT fatal, since the existing +handle is still usable and answering from a slightly stale index beats refusing +to answer at all. + +Covered by `aQuerySeesMailIndexedAfterTheWorkerOpened`, which holds ONE worker +across two queries and runs `notmuch new` in a second process between them. +Mutation-checked: `after.size()` is 0 without the fix, 1 with it. The first +query asserts zero results before the message is written, so "found nothing" +cannot mean "the query was malformed". + +**The reproduction, kept because this entry's Approach section asked for exactly +this and it took three wrong turns to get there.** Four messages sent to one +account on 2026-08-25, viewed in that account's Inbox, synced with the app's own +Sync button. The three layers resolved as: on disk (yes), indexed (yes), shown +(no), which is layer 3 and therefore this repository. Two of the four matched +the running view's exact query (`path:"<account>/**" and (tag:inbox)`, 2 results +from the shell) and were absent from a window that had been open across the +sync. + +Two measurement errors made during that diagnosis, both worth repeating because +each produced a confident wrong answer: + +- `notmuch count 'inbox and path:...'` was used to check the view's contents. A + bare `inbox` is a FREE-TEXT term, not a tag term; the app generates + `tag:inbox`. The bare form returned 0 where the real query returns 2, which + briefly made the defect look like a tagging problem. +- The messages' tags were first read across every file matching the subject, + including the sender-side Sent copies in other accounts. That mixed three + accounts' messages into one answer. + +**Superseded theory, kept for the record.** mbsync fetches Gmail folders by +pattern and three of the five channels name their folders explicitly, so a +message labelled anything else is in a folder mbsync never asks for while +Thunderbird, speaking IMAP directly, sees it. That mechanism is real and would +produce a similar symptom, but it is not what was happening here: the mail was +on disk and indexed. It remains a plausible cause of any FUTURE report of this +shape, so check layer 1 before assuming this fix covers it. + +**One inconsistency worth reporting regardless**, found while checking the above +and still true: one of the Gmail accounts is configured in `qtmaildir.conf` with +a sent and a drafts folder, while its mbsync channel has `Patterns "INBOX"` and +fetches neither. The Sent and Drafts filters for that account can therefore only +ever be empty. That is real, independent of this item, and outside this +repository. + +**Size: XS.** Done. diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index f1db28a..68bbaaf 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -170,7 +170,7 @@ taking that too literally. | 101 | Sync is account-aware for edits but not for the account the user is looking at | workflow | S | open; item 49 built the edit half deliberately. Needs a decision, see the entry | | 102 | The rules table shows no note, so the field explaining a rule is invisible until it is opened | workflow | XS | **done** 2026-08-17, unreleased. A Note column before `ColumnCount`, so the appended Matches column stays last. Found a second defect on the way: `restoreState` REFUSES a header state with a different column count, and the sized flags were being set regardless | | 103 | What Delete does to mail on the server is undocumented and unverified | clarification | S+M | done; Delete moves to the account trash, with Restore and a stranded-mail cleanup. Section in the closed file | -| 104 | Mail visible in Thunderbird never reaches qtmaildir | defect | XS | **fixed 2026-08-25**, awaiting hand test. The worker never reopened its read-only notmuch handle, so no query saw mail indexed after startup | +| 104 | Mail visible in Thunderbird never reaches qtmaildir | defect | XS | **done 2026-08-25**, hand-tested. The worker never reopened its read-only notmuch handle, so no query saw mail indexed after startup. Confirmed on a sync run from the application that added 20 messages: they appeared without a restart | | 109 | A root card's own message is invisible to a message-scoped write | defect | S | **done** 2026-08-16, unreleased. Found by hand-testing 108. `applyMessageTagChange` and `messageById` searched only the loaded replies, and a root's message is never among them, so the ORDINARY gesture repainted nothing and wiped the pane's chip row | | 110 | A card and the message pane show tags belonging to a message's siblings | defect | S | **done** 2026-08-16, unreleased. Found by hand-testing 109 against a real 4-message thread. `ThreadSummary::tags` is notmuch's UNION; a card standing for one message drew it. Also the reason a root card could not repaint at all | | 111 | A card should show its siblings' tags smaller, not drop them | presentation | S | **done** 2026-08-16, unreleased. The user's own design, from looking at 110's result: own tags full size, the thread's others smaller and muted, so nothing appears to vanish on selection | @@ -536,100 +536,6 @@ reaches it (item 42), so most of this exists. **Size: S** for the on-demand button, XS for the visibility half. Ask which. -## 104. Mail visible in Thunderbird never reaches qtmaildir - -**Observed (user, from the notes):** "sync doesn't work compared to thunderbird. -New mail received on thunderbird did not appear in qtmaildir. Need to investigate -further." - -**Cause: ESTABLISHED 2026-08-25, and it was this repository after all.** This -entry previously named mbsync's folder `Patterns` as the leading theory and -concluded "most likely not a code change here at all". That was wrong; the -superseded reasoning is kept at the bottom because the reproduction is what -overturned it. - -**`NotmuchWorker` opened one read-only notmuch handle and kept it for the -process lifetime.** A read-only handle is a Xapian SNAPSHOT taken when it is -opened; it never observes a write made by another process afterwards. The sync -script's `notmuch new` is exactly such a process, so every query the worker -answered after startup was served from the index as it stood when the -application launched. `openReadOnly()` returned early on `if (m_db) return -true;` and there was no `notmuch_database_reopen` anywhere in the tree. - -This accounts for every symptom, including the ones that defeated three earlier -theories during the diagnosis: - -- The post-sync refresh found nothing, so `refreshCurrentQuery()` and - `ThreadListModel::reconcile()` were each suspected in turn. Both are correct. -- A query the user typed BY HAND also found nothing. That is what rules out the - model, the generation counter and the account scope together: a fresh query - clears the model and re-runs from scratch, and it still hits the same stale - handle. -- A restart showed the mail instantly, with no sync in between. -- Tag WRITES were never affected, which is why the defect reads as "reading is - broken" rather than "notmuch is broken". `applyTags` opens its own read-write - handle per call, so it always sees current data. - -**Why it survived from 2026-08-16 to 2026-08-25.** The symptom needs mail to -arrive from outside the process while the window stays open, which is the -ordinary way this application is used and the one thing no test did: every -fixture opens a worker, queries it, and drops it. `TestNotmuchWorker::runQuery()` -builds a FRESH worker per call, so the suite was structurally incapable of -reproducing it, and a test written through that helper passes against the bug. - -**Fixed** in `NotmuchWorker::openReadOnly()`: when a handle already exists, -`notmuch_database_reopen(m_db, NOTMUCH_DATABASE_MODE_READ_ONLY)` before -returning it. Every read path begins by asking for the handle, so one call -covers all of them; putting it at the call sites instead would be one more -place to forget. A reopen failure is deliberately NOT fatal, since the existing -handle is still usable and answering from a slightly stale index beats refusing -to answer at all. - -Covered by `aQuerySeesMailIndexedAfterTheWorkerOpened`, which holds ONE worker -across two queries and runs `notmuch new` in a second process between them. -Mutation-checked: `after.size()` is 0 without the fix, 1 with it. The first -query asserts zero results before the message is written, so "found nothing" -cannot mean "the query was malformed". - -**The reproduction, kept because this entry's Approach section asked for exactly -this and it took three wrong turns to get there.** Four messages sent to one -account on 2026-08-25, viewed in that account's Inbox, synced with the app's own -Sync button. The three layers resolved as: on disk (yes), indexed (yes), shown -(no), which is layer 3 and therefore this repository. Two of the four matched -the running view's exact query (`path:"<account>/**" and (tag:inbox)`, 2 results -from the shell) and were absent from a window that had been open across the -sync. - -Two measurement errors made during that diagnosis, both worth repeating because -each produced a confident wrong answer: - -- `notmuch count 'inbox and path:...'` was used to check the view's contents. A - bare `inbox` is a FREE-TEXT term, not a tag term; the app generates - `tag:inbox`. The bare form returned 0 where the real query returns 2, which - briefly made the defect look like a tagging problem. -- The messages' tags were first read across every file matching the subject, - including the sender-side Sent copies in other accounts. That mixed three - accounts' messages into one answer. - -**Superseded theory, kept for the record.** mbsync fetches Gmail folders by -pattern and three of the five channels name their folders explicitly, so a -message labelled anything else is in a folder mbsync never asks for while -Thunderbird, speaking IMAP directly, sees it. That mechanism is real and would -produce a similar symptom, but it is not what was happening here: the mail was -on disk and indexed. It remains a plausible cause of any FUTURE report of this -shape, so check layer 1 before assuming this fix covers it. - -**One inconsistency worth reporting regardless**, found while checking the above -and still true: one of the Gmail accounts is configured in `qtmaildir.conf` with -a sent and a drafts folder, while its mbsync channel has `Patterns "INBOX"` and -fetches neither. The Sent and Drafts filters for that account can therefore only -ever be empty. That is real, independent of this item, and outside this -repository. - -**Size: XS.** Done. - - - ## 166. Mail you send to your own other account loses `inbox` **Observed (agent, 2026-08-25, while setting up msmtp.)** Four test messages |
