diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-11 19:02:43 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-11 19:02:43 +0200 |
| commit | ddce3f3b198b733bd1815cd11c083f0d5ae0beb7 (patch) | |
| tree | c0b1e1ce2bd9fb65da34c690d4c6462e3185adea | |
| parent | 44d62143a83af8acbd1c1d14653d39da37e5de4a (diff) | |
| download | qtmaildir-ddce3f3b198b733bd1815cd11c083f0d5ae0beb7.tar.gz qtmaildir-ddce3f3b198b733bd1815cd11c083f0d5ae0beb7.zip | |
docs(backlog): reconcile with the user's notes, and settle item 68
Eight entries from the notes had no item here. Appended as 66 to 73 with
each cause verified in the code rather than copied from the note: a blank
message pane on a first click (66), missing sent and drafts counts (67),
a forwarded-subject tag (68), passed and replied as words rather than
glyphs (69), pane icons against system icons (70), no sync after a
toolbar action (71), khard/khal (72), and this document's own size (73).
Item 68 arrived as "the passed tag appears for Fwd: but not Fw:, expand
it". Measured against the real database, that correlation does not
exist: 6 messages carry the tag in total, 194 Fwd: subjects carry none,
and every tagged message has P in its Maildir flags. The tag is the
Maildir P flag translated by notmuch under maildir.synchronize_flags,
written by whichever client forwarded the message. Nothing anywhere
reads a subject line, so there is no rule to expand. The section now
carries the measurements and costs the two real options, a display-only
mark against writing the flag out to 222 messages, and stays open
pending that decision.
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 244 |
1 files changed, 244 insertions, 0 deletions
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 a187874..e71f354 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 @@ -122,6 +122,14 @@ taking that too literally. | 63 | No way to see sent mail, and no filter for it | workflow | M | **done** 2026-08-11; see `specs/2026-08-11-sent-mail-design.md` | | 64 | The Sync button carries a mailbox icon, not a refresh one | presentation | XS | **done** 2026-08-11 | | 65 | No full code review and optimization pass | correctness | ? | open, unspecified | +| 66 | Selecting a thread root leaves the message pane blank until a reply has been selected | defect | S | open; needs a reproduction before a fix | +| 67 | The placeholder pane counts unread, flagged and inbox, but not sent or drafts | information | XS | open | +| 68 | A forwarded subject gets no `passed` tag | workflow | S | open; no subject rule exists, measured 2026-08-11. Decision needed: display mark (XS) or write the flag (S, syncs out) | +| 69 | `passed` and `replied` read as words where every other state is a glyph | presentation | S | open; depends on 68 for what `passed` means | +| 70 | Pane icons are a private set where the main window uses the system theme | presentation | M | open | +| 71 | A toolbar action does not sync, so the edit sits until the next cron run | workflow | S | open; user decision on the delay | +| 72 | No khard/khal integration | workflow | ? | open, unspecified; the user places it after send, so v2 at the earliest | +| 73 | This backlog is past four thousand lines | maintenance | S | open | Sizes are rough: XS under an hour, S a sitting, M a session. @@ -4231,6 +4239,242 @@ recorded. **Size: `?`, unspecified.** Do not propose a design for this; ask. +## 66. Selecting a thread root leaves the message pane blank until a reply has been selected + +**Observed (user, from the notes):** clicking the main message of a thread shows +nothing in the right pane. After expanding the thread and selecting a reply, +clicking the main message again renders it correctly. The user filed a second +observation as a possible duplicate: clicking the main message shows the whole +thread with stubs and the last two replies expanded, and after clicking a reply +and going back to the root, only the root's own message is shown. + +**Cause (partly verified, and the two halves are not the same fault).** + +The second half is not a defect at all. A thread row renders the conversation and +a message row renders one message; `onThreadSelected` (`src/mainwindow.cpp:1847`) +branches on `isMessageRow` for exactly that. What breaks the user's expectation is +that going back to the root should return to the conversation view, and it does +not always: `m_currentMessageId` is set by the message branch +(`src/mainwindow.cpp:1860`) and `onMessageLoaded` refuses to render when it is +empty (`src/mainwindow.cpp:1924`), so the two paths are ordered by which reply +lands first, not by which row is current. This is the "a queued load can outlive +the state that started it" class already recorded in CLAUDE.md. + +The first half, a blank pane on the very first click, has a candidate and only a +candidate: `onThreadSelected` returns without loading anything when the index is +not yet selected (`src/mainwindow.cpp:1804`), a guard added so that a view taking +focus does not open and mark-read a message the user never looked at. If a click +can deliver `currentRowChanged` before the selection model is updated, which +CLAUDE.md records as this signal's documented behaviour, that guard drops the +load and `onSelectionChanged` only reloads when the row CHANGED +(`src/mainwindow.cpp:1721`), which it did not. + +**Approach:** do not fix from this reading. The candidate above is exactly the +kind of plausible cause the project has been burned by twice; instrument the +running application and watch the order of `currentRowChanged`, +`selectionChanged` and the worker reply for one first click on a cold view. +`test_mainwindow` cannot reproduce it: it has no worker and never fires +`threadLoaded`. + +**Constraints:** the mark-read guard at 1804 must survive whatever is done, it is +item 35b's fix and removing it re-opens marking mail read without the user having +seen it. A fix that only re-orders the two guards will look correct and will still +be timing-dependent. + +**Size: S**, and a defect rather than an enhancement. + +## 67. The placeholder pane counts unread, flagged and inbox, but not sent or drafts + +**Observed (user, from the notes):** "stats in the blank pane should show also +drafts and sent emails." + +**Cause:** `kPlaceholderQueries` drives three counts and the labels are written +positionally against them, `%n unread`, `%n flagged`, `%n in inbox` +(`src/mainwindow.cpp:1410-1412`). The worker's `requestCounts` takes an arbitrary +list of queries and answers one count per query +(`src/notmuchworker.cpp:621-648`), so the machinery is already general; only the +list and its labels are fixed. + +**Approach:** add two entries. Sent is not `tag:sent`: item 63 established that +the sent query is composed from the configured per-account folders, and +`allSentQuery()` already builds it. Drafts has no such composition yet and the +obvious `tag:draft` should be checked against the real database before it is +shipped, since a count that always reads 0 is worse than no count. + +**Constraints:** the -1 convention is load-bearing. The worker returns -1 rather +than skipping an entry precisely because the caller pairs answers with labels by +position (`src/notmuchworker.cpp:632-634`), so a new entry must be added to the +query list and the label list at the same index. + +**Size: XS.** + +## 68. A forwarded subject gets no `passed` tag + +**Observed (user, from the notes):** "passed tag should appear when subject is +`Fwd:` and `Fw:`." Refined in session on 2026-08-11: the user had noticed +`passed` appearing on messages whose subject carried `Fwd:` and not on `Fw:`, +and asked to expand the rule to both. + +**Cause:** there is no rule to expand. `passed` is the Maildir `P` flag in the +message filename, translated into a tag by notmuch because +`maildir.synchronize_flags=true`. The flag is written by whichever client +forwarded the message, or by the server over IMAP; nothing reads a subject line +anywhere in the chain. qtmaildir only ever colours the tag +(`src/tagcolors.cpp:36-37`) and the database's `post-new` hook does not mention +it either. + +**Measured against the real database (2026-08-11):** + +| Query | Count | +|---|---| +| `tag:passed` | 6 | +| `tag:passed and subject:"Fwd:"` | 1 | +| `tag:passed and subject:"Fw:"` | 0 | +| `subject:"Fwd:" and not tag:passed` | 194 | +| `subject:"Fw:" and not tag:passed` | 28 | + +Six tagged messages in the whole database, and every one of them carries `P` in +its filename flags. The single overlap with `Fwd:` is a message that was +forwarded and whose subject was already a forward, not evidence of a rule: 194 +`Fwd:` subjects carry no tag at all. The correlation the observation rests on +does not exist. + +**Approach and the decision it needs first.** Two different features, and the +measurements above decide how far apart they are. + +*Display only.* The card shows a forwarded mark when the subject matches. Touches +no mail, changes no flag, reversible by deleting the rule. XS. + +*Write the tag.* qtmaildir sets `P` from a subject heuristic. With +`maildir.synchronize_flags=true` that flag is a filename change that mbsync +carries out to the server, on 222 existing messages, on a guess about a string. +Not cleanly undoable, and it asserts a meaning for a flag this application did +not define. Recommended against; recorded so the choice is deliberate rather than +forgotten. + +**Constraints:** localised clients use their own prefixes, and `Fwd:` can appear +inside a subject rather than at its head, so whatever matches must be anchored. +If the tag is ever written, it must not be re-applied on every sync in a way that +produces pending edits the user never made, item 28 is the record of a count +going wrong. The display-only route avoids that entirely, since it derives the +mark at paint time and stores nothing. + +**Size: S** as written, XS if it is display only. Most of it is the decision, not +the code. + +**Status:** left open deliberately on 2026-08-11. The cause is settled and the +options are costed; the user has not chosen, and no code was written. + +## 69. `passed` and `replied` read as words where every other state is a glyph + +**Observed (user, from the notes):** "tags like Passed and Replied should use +icons instead." + +**Cause:** both are ordinary tags, drawn as text chips with a built-in colour +(`src/tagcolors.cpp:36-37`) by the same `TagChip` painting helpers as every other +tag. Flagged already went the other way in item 57 and is a star, so the card +mixes one glyph state with two word states. + +**Approach:** treat this as an extension of what item 57 built rather than as new +machinery, a small map from tag name to glyph consulted before the chip painter. +Assert in `CardLayout`, not on a render: a glyph and a chip reserve different +widths, and the date's reserved width is computed from a sample string, which is +the trap item 62 hit. + +**Constraints:** depends on 68 only in that the meaning of `passed` may change +under it. The glyph must survive a card that carries neither tag without leaving +a gap, and the tag must remain readable to a user who does not know the glyph, +so keep the tooltip or the accessible name carrying the word. + +**Size: S.** + +## 70. Pane icons are a private set where the main window uses the system theme + +**Observed (user, from the notes):** "we should rework the icons used in the two +panes, leaving the main UI to use system icons." + +**Cause:** every action icon is resolved through `QIcon::fromTheme` +(`src/mainwindow.cpp:1057`), which is the system theme and is what the user wants +kept. The panes are the other half: the card and the message pane draw their own +marks, and item 57's star and item 15's paperclip arrived independently of each +other and of the theme. + +**Approach:** unspecified in shape until the user says what they pictured for the +panes, but the split they stated is clear and is the constraint worth recording +now: the toolbar and menus stay on `fromTheme`, the panes get a deliberate, +self-contained set that does not change under the user's icon theme. + +**Constraints:** an icon shipped as an asset needs to work on both light and dark +message-pane CSS, which item 12 already made theme-aware. `noTwoActionsShareAnIcon` +covers actions only and will not catch a collision between pane marks. + +**Size: M**, and it overlaps 69, which should probably be done inside it rather +than before it. + +## 71. A toolbar action does not sync, so the edit sits until the next cron run + +**Observed (user, from the notes):** "clicking one action in the toolbar should be +synced automatically (maybe after a configurable delay). EG I hit 'mark all read', +the view is updated but I still have to sync manually or wait for the cronjob." + +**Cause:** by design, and the design is recorded. Tag edits reach the notmuch +index at edit time and are held as pending until a sync carries them out to the +server, which is what the unsynced-changes indicator counts (items 18, 28, 54). +Nothing schedules that sync on the user's behalf. + +**Approach:** a debounced timer after a mutation, firing the existing sync path. +Item 49 already narrowed a sync to the accounts that actually changed, so the +automatic one is not the whole-mailbox operation it would have been before that. + +**Constraints and the decision needed.** The delay is the user's call and the +default matters: too short and every keystroke of tagging spawns an mbsync, too +long and it is indistinguishable from the cron job they already have. A sync +started this way must not fight the cron one, `SyncMonitor` watches +`/tmp/mbsync.lock` and the automatic sync has to skip rather than queue when the +lock is held. The undo stack has to survive it, which is what item 35 built. + +**Size: S** once the delay is chosen. + +## 72. No khard/khal integration + +**Observed (user, from the notes):** "investigate khard/khal integration (light +PIM, probably worthy after we add send capabilities)." + +**Cause:** not a defect. v1 is read-and-organize; there is no address book and no +calendar anywhere in the codebase. + +**Why this cannot be planned.** The user's own note places it after send, and +send is v2. What "integration" means is undecided: completing recipients from +khard when composing, showing a sender's card, or acting on an invitation. +Those are three different features. + +**Size: `?`, unspecified**, and out of scope until v2 exists. Ask before designing +anything. + +## 73. This backlog is past four thousand lines + +**Observed (user, from the notes):** "cleanup pass on the backlog in the project. +~4K lines is starting to become a problem." + +**Cause:** every item keeps its full Observed/Cause/Approach section forever, +including the fifty-odd that are done. The rule that would have prevented it +exists now, in "Adding to this document", but it was written on 2026-08-11 for +item 63 and nothing has been applied retroactively. + +**Approach:** the done items are the bulk, and their sections are history rather +than backlog. Move the closed ones out to a companion file, leaving the status +table intact and each section replaced by nothing at all, the table row already +carries the date and the outcome. Where a closed item records a trap that is +still true, that trap belongs in CLAUDE.md, which is where it would actually be +read, and several already are. + +**Constraints:** do not renumber and do not delete. The numbering is referenced +from commit messages, from CLAUDE.md, and from the specs, so a moved section has +to stay findable under its number. + +**Size: S**, and it is bookkeeping, so it competes with real work rather than +blocking it. + ## Deferred, unsized, or split out Items noted while triaging but not part of the original list. Same numbering |
