aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-25 17:05:03 +0200
committerDanilo M. <danix@danix.xyz>2026-08-25 17:05:03 +0200
commitf64e33e5725a99416da53d3f8f387bcc590fb3fa (patch)
tree234a1177f290cb4ce6bc54af3f1318eda8b9390b /docs/superpowers/plans
parentec133222d393978fc96cb69a0e1c7acc45e2bf0c (diff)
downloadqtmaildir-f64e33e5725a99416da53d3f8f387bcc590fb3fa.tar.gz
qtmaildir-f64e33e5725a99416da53d3f8f387bcc590fb3fa.zip
docs(backlog): close item 104 after its hand test
The fix landed in b1db3e8 and the row said "awaiting hand test". A sync run from the application added 20 messages and they appeared without a restart, which is the property the reopened read-only handle exists to give, so the row is now done and the section moves to the closed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HFuRPtzFrSxCQjFk6tq7gD
Diffstat (limited to 'docs/superpowers/plans')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md92
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md96
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