aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-19 10:30:28 +0200
committerDanilo M. <danix@danix.xyz>2026-08-19 10:30:28 +0200
commit6f39b6350efc140a0b7f49701ffd52c54ec90bac (patch)
tree4d10cc671d1ed994b23263e80bdbc1c77c8df330 /docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
parent69e2173b71e94e5a89c39b20d4a5962aadb715e9 (diff)
parent2e0db925d5ca7100d8405ffc352ae435cdbdb73d (diff)
downloadqtmaildir-6f39b6350efc140a0b7f49701ffd52c54ec90bac.tar.gz
qtmaildir-6f39b6350efc140a0b7f49701ffd52c54ec90bac.zip
Merge branch 'delete-to-trash'
Delete moves mail into the account's trash folder instead of only tagging it, with a Trash filter, Restore from trash, and a repeatable cleanup for the mail the old behaviour stranded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.md87
1 files changed, 87 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 f46fec5..ef51b5c 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
@@ -6612,3 +6612,90 @@ Three lessons, in the order they were paid for:
`image/png` that is demonstrably on the clipboard: a Wayland clipboard-manager
interaction, or GIMP's own paste path. Neither is this repository's, and neither
needs an item here until it is shown to be.
+
+## 103. What Delete does to mail on the server is undocumented and unverified
+
+**Observed (user, from the notes):** "verify how 'delete' works", with two
+sub-questions of their own: "trash bin (?)" and "delete from server (?)".
+
+**This is a question first.** The user is not reporting a defect; they are saying
+they do not know what the button does to their mail, which for a destructive
+action is its own problem regardless of the answer.
+
+**What the code does (verified).** `src/mainwindow.cpp:825` adds and removes the
+`deleted` tag, and nothing else. It is a toggle, it goes through the undo stack,
+and it writes a notmuch tag.
+
+**What that means downstream is what needs verifying, and it is NOT in this
+repo.** `maildir.synchronize_flags` is true, so notmuch maps certain tags to
+Maildir filename flags, and mbsync carries filename flags to the server. Whether
+`deleted` is one of those, whether the user's `~/.mbsyncrc` has `Expunge Both`
+(it does, on every channel), and what each provider does with a message flagged
+deleted, together decide whether this button is reversible. The undo stack makes
+the TAG reversible; it says nothing about what a sync did with it in between.
+
+**Measured 2026-08-17, and the answer is that Delete does not delete.** notmuch's
+tag-to-flag table has no row for `deleted` and no `T` flag, confirmed by a probe
+on a throwaway database: `+deleted` left the filename untouched while the
+control `+flagged` immediately produced `:2,F`. mbsync carries filename flags, so
+`Expunge Both` never sees anything to expunge, and `assets/mailsync.sh` contains
+no delete path. Both sub-questions answer no: no trash bin, no deletion from the
+server, and the mail stays in the Maildir and the index forever.
+
+**Specified in
+`specs/2026-08-17-delete-to-trash-design.md`. Read that before writing code.**
+Delete becomes a real move into the account's trash folder, with a `Trash`
+filter beside the other built-ins and a Restore action. Three constraints decide
+whether the spec is worth opening:
+
+- It needs a **mandatory per-account `trash` key**, so an existing config warns
+ until five keys are added. User-visible: minor bump and an `### Upgrading`
+ note.
+- The worker gains its **first non-tag mutation**, a rename plus a reindex,
+ deliberately shaped as `moveMessages(ids, destFolder)` so Send in v2 reuses it
+ for Drafts and Sent.
+- **848 messages carry the old tag** while sitting in an inbox, and would be
+ invisibly half-deleted after the change. A repeatable menu entry queries them
+ into the list for review; it is not a startup migration.
+
+The no-confirmation rule survives, with its justification amended: the mail
+lands in a browsable folder, but reversibility is now bounded by the provider
+where the trash is purged on a timer.
+
+**Size: S** for the investigation, which is done. The build is **M**.
+
+**Built 2026-08-19, over eight commits on `delete-to-trash`.** Delete moves the
+file into the account's `trash` folder and records where it came from in a
+`deleted-from:<folder>` tag, because the file has moved and nothing else still
+knows. A `Trash` filter sits beside the other four, `Restore from trash`
+(`Ctrl+R`) is the inverse, and `Find stranded deleted mail` (`Ctrl+Alt+T`, a
+menu entry only at the user's request) lists the 848 messages the old behaviour
+left tagged but unmoved. It reports and moves nothing: acting on its own would
+be a bulk delete with no selection behind it.
+
+**Ten defects were found by hand testing, not by the suite**, each reproduced
+with a probe before being fixed. One had already damaged real mail: a folder
+name containing a space was truncated by the origin tag, creating a bogus folder
+beside the real one and stranding four messages of a thread. mbsync had adopted
+it and written state files for it. That is the fact worth carrying out of this
+item: **under `Create Both` a wrongly named origin folder propagates to the mail
+server**, so any code composing a folder name is reaching the server whether it
+means to or not. Tags are joined by a TAB across the thread boundary for the
+same reason, since a notmuch tag may contain a space.
+
+**Two process gaps closed alongside it.** Adding an action is FIVE places and
+this repository's CLAUDE.md said four; the missing one is a menu, and nothing
+enforced it, so `restore` shipped reachable by a chord and by nothing a user
+could see. `everyActionIsReachableFromAMenu()` now asserts it and found three
+more of the same (`open_thread`, `clear_pane`, `clear_selection`). And a guard
+in `deletingTwiceLeavesNoOriginTagBehind()` queried through the query bar in the
+gap between the file rename and the tag writes, where a run returns zero rows
+forever because `QTRY_VERIFY` re-reads `rowCount()` and never re-runs the query:
+3 failures in 12 runs, each burning a full 15s timeout, 0 in 8 after asking the
+database directly.
+
+**Emptying the trash is item 118 and remains deferred**, at the user's request.
+
+**Verified:** clean build with no warnings from any changed file, 24 of 24
+suites over three consecutive runs, 211 tests in `test_mainwindow`. Every one of
+the spec's six testing bullets has a test, each mutation-checked.