summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-29 10:11:59 +0200
committerDanilo M. <danix@danix.xyz>2026-08-29 10:11:59 +0200
commite2bd68a863513209a5881696164fd463ddc83503 (patch)
treefe9fc90462030f9f99d5298eb09785018cc60f40 /docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
parentfeb15bcec261501eb49ad0de1fdc05a9992227c2 (diff)
downloadqtmaildir-e2bd68a863513209a5881696164fd463ddc83503.tar.gz
qtmaildir-e2bd68a863513209a5881696164fd463ddc83503.zip
fix: say an edit is waiting for the sync instead of claiming it landed
Item 182, found by hand: a thread of 9 messages with 5 unread, marked read while a sync was running, reported "<subject>: mark as read" and then reported the same work again when the sync finished. The user read it as double reporting. Not a double write, and the mail was correct. It is one action reported twice because the FIRST report was the wrong one. A sync holds notmuch's exclusive write lock and the worker's read-write open blocks on it rather than failing, so an edit made during a sync is held and sent when the lock frees. All three hold branches say exactly that, in a label chosen deliberately: NOT transient, because it describes state lasting until the sync ends, and a message that expired would leave rows showing a tag the database has not got and no explanation of why. That label never survived. Every caller announced the action itself a line later through showTransientStatus(), which overwrote it, so the user was told the write had happened and the hold was never mentioned. The flush at the end of the sync then reported the same work again and read as a duplicate rather than as its completion. announceAction() asks whether a sync holds the lock and, when one does, sets a non-transient label naming the action AND the wait. The action is still named because that announcement is what stands in for the confirmation dialog this project rules out: it is how a user tells that something larger than they meant has just happened, so the hold is added to it rather than replacing it. The flush message is untouched and is the only signal that held work actually landed, whose absence was item 106. The test drives toggle_unread, the route the user took, and asserts both halves: the text mentions the sync, and it still says what is waiting. Asserting only the first would pass against an announcement that dropped the action entirely. Mutation-checked by forcing the non-held branch, which fails with the exact text the user reported. The new string is translated, since one that misses the Italian ships as English inside an otherwise Italian UI: lupdate found it with no context warnings, lrelease reports 552 finished and 0 unfinished. Suite: 42 of 43, with undoMovesTheMessageBack failing as it does on master (item 136).
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md1
1 files changed, 1 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 68c080c..54e4b43 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
@@ -255,6 +255,7 @@ taking that too literally.
| 179 | Undo is one level deep in practice, and there is no Redo | workflow | ? | open, 2026-08-29, from the notes. The `QUndoStack` is real and multi-level; what is missing is a `redo` action (absent from `knownActions()`, never called) and an answer to the stack being CLEARED on every new query (`mainwindow.cpp:3458`), which is what makes a deep stack behave like a shallow one. The clear has a correct reason and cannot simply be removed. Redo re-applies a write to real mail, so item 176's rule binds it too |
| 180 | The repaint rules are discovered one hole at a time | maintenance | S-L | open, 2026-08-29, from the notes, and a QUESTION rather than a defect. Items 105, 107, 109, 110 and 170 are each one hole in the same surface, all found by hand. Three mechanisms (optimistic repaint, `syncViewMembership()`, revert) agree by documentation rather than by code. Cheapest answer is one invariant test, not a rewrite; the user decides which, and that decides the size |
| 181 | The thread dashboard does not follow a write to the conversation it shows | defect | XS | **done 2026-08-29**, unreleased, on `thread-row-identity`, from the notes. The dashboard draws a `ThreadDigest` built by the worker from the INDEX, which arrived only on selection, so a tag write moved the model and the card and left the pane reporting the count the conversation had when it was opened. Reachable from the dashboard's OWN Mark all read button. Re-requested from `onTagsApplied()`, where the write is confirmed: queued beside the write it races it and answers from the state before it, which is how the first fix passed review and failed the test. Section in the closed file |
+| 182 | An edit made during a sync is announced twice and never says it is waiting | defect | XS | **done 2026-08-29**, unreleased, on `thread-row-identity`, found by hand. The hold branches set a deliberately NON-transient label; all three callers overwrote it a line later with the bare action, so the user was told the write had landed and then told again when it really did. `announceAction()` adds the wait to the action rather than replacing it, since that announcement is what stands in for the confirmation dialog this project rules out. Section in the closed file |
Sizes are rough: XS under an hour, S a sitting, M a session.