diff options
Diffstat (limited to 'docs')
3 files changed, 135 insertions, 99 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. 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 6a5e64d..1f71006 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 @@ -167,7 +167,7 @@ taking that too literally. | 100 | The message pane offers Back, Forward, Reload and Save page, none of which mean anything | defect | XS | **done** 2026-08-17, unreleased. `MessageView::removeBrowserActions()` filters the standard menu by `pageAction()` POINTER, never by text; `ViewSource` went with them, and stranded separators are swept | | 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 | open; a question first, possibly no code at all | +| 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 | ? | open, reported 2026-08-16, cause NOT established. Most likely outside this repo; see the entry before writing code | | 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 | @@ -485,57 +485,6 @@ reaches it (item 42), so most of this exists. **Size: S** for the on-demand button, XS for the visibility half. Ask which. -## 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**. - ## 104. Mail visible in Thunderbird never reaches qtmaildir **Observed (user, from the notes):** "sync doesn't work compared to thunderbird. diff --git a/docs/superpowers/plans/2026-08-17-delete-to-trash.md b/docs/superpowers/plans/2026-08-17-delete-to-trash.md index 4e52789..70742b5 100644 --- a/docs/superpowers/plans/2026-08-17-delete-to-trash.md +++ b/docs/superpowers/plans/2026-08-17-delete-to-trash.md @@ -52,7 +52,7 @@ No new files. Every change lands in a file that already owns that responsibility - Modify: `src/config.cpp` (`Account::trashQuery()` beside `sentQuery()` at line 130, and the account parser) - Test: `tests/test_config.cpp` -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** Add to `tests/test_config.cpp`, and declare both slots in the `private slots:` block: @@ -94,7 +94,7 @@ void TestConfig::aBracketedTrashFolderIsQuoted() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build 2>&1 | tail -5 @@ -102,7 +102,7 @@ cmake --build build 2>&1 | tail -5 Expected: FAIL to compile, `'trash' is not a member of 'Account'`. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** In `src/config.h`, add to `Account` immediately after the `sent` member: @@ -143,7 +143,7 @@ Then find where the account parser reads `sent` (search for `QStringLiteral("sen account.trash = settings.value(QStringLiteral("trash")).toString(); ``` -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config @@ -151,7 +151,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config Expected: PASS, all tests. -- [ ] **Step 5: Commit** +- [x] **Step 5: Commit** ```bash git add src/config.h src/config.cpp tests/test_config.cpp @@ -170,7 +170,7 @@ First read how warnings are currently raised: search `src/config.cpp` for `m_warnings` and copy the surrounding form exactly. Do not invent a new mechanism. -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** ```cpp void TestConfig::anAccountWithoutATrashFolderWarns() @@ -204,7 +204,7 @@ void TestConfig::anAccountWithoutATrashFolderWarns() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config -functions | grep -i trash @@ -213,7 +213,7 @@ QT_QPA_PLATFORM=offscreen ./build/tests/test_config anAccountWithoutATrashFolder Expected: FAIL, `warnings` is empty. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** In the account-loading loop in `src/config.cpp`, after the account is parsed and before it is appended: @@ -232,7 +232,7 @@ before it is appended: } ``` -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config @@ -240,7 +240,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config Expected: PASS. -- [ ] **Step 5: Commit** +- [x] **Step 5: Commit** ```bash git add src/config.cpp tests/test_config.cpp @@ -256,7 +256,7 @@ git commit -m "feat(config): warn when an account configures no trash folder" - Modify: `src/config.cpp` (`kQueryGenerators` line 60, `builtinFilter()` line 757, `resolvedQuery()` line 790, `allTrashQuery()` beside line 140) - Test: `tests/test_config.cpp` -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** ```cpp void TestConfig::theTrashFilterComposesPerAccount() @@ -307,7 +307,7 @@ void TestConfig::theTrashFilterMatchesNothingWithoutAFolder() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config theTrashFilterComposesPerAccount @@ -315,7 +315,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config theTr Expected: FAIL, `trash.isGenerated()` is false because the generator is unknown. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** In `src/config.cpp`, add to `kQueryGenerators` at line 60, last so it sits rightmost on the query row: @@ -377,7 +377,7 @@ And in the per-account branch, beside the `sent` case: } ``` -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config @@ -385,13 +385,13 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_config Expected: PASS. -- [ ] **Step 5: Verify the mutation fails** +- [x] **Step 5: Verify the mutation fails** Temporarily change the per-account branch to `return allTrashQuery();` and rebuild. `theTrashFilterComposesPerAccount` must FAIL on the `QCOMPARE`. Revert the mutation. This proves the test asserts on the string rather than on rows. -- [ ] **Step 6: Commit** +- [x] **Step 6: Commit** ```bash git add src/config.h src/config.cpp tests/test_config.cpp @@ -412,7 +412,7 @@ This is the first mutation in the project that is not a notmuch tag. Read uses is required, not stylistic, because notmuch permits one open handle per process. -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** Add to `tests/test_notmuchworker.cpp`, declaring each slot in `private slots:`: @@ -536,7 +536,7 @@ void TestNotmuchWorker::moveMessagesReportsOnlyWhatMoved() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build 2>&1 | tail -5 @@ -544,7 +544,7 @@ cmake --build build 2>&1 | tail -5 Expected: FAIL to compile, `'moveMessages' is not a member of 'NotmuchWorker'`. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** In `src/notmuchworker.h`, beside `applyTags()`: @@ -675,7 +675,7 @@ void NotmuchWorker::moveMessages(const QStringList &messageIds, } ``` -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_notmuchworker @@ -683,7 +683,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_notmuchworke Expected: PASS, all tests. -- [ ] **Step 5: Verify the ordering mutation fails** +- [x] **Step 5: Verify the ordering mutation fails** Move the `notmuch_database_remove_message` call to immediately BEFORE the `notmuch_database_index_file` call and rebuild. @@ -691,7 +691,7 @@ Move the `notmuch_database_remove_message` call to immediately BEFORE the the mutation. This is the single most important check in the task: the wrong order silently destroys user tags and every other test still passes. -- [ ] **Step 6: Commit** +- [x] **Step 6: Commit** ```bash git add src/notmuchworker.h src/notmuchworker.cpp tests/test_notmuchworker.cpp @@ -716,7 +716,7 @@ selection, resolved through `everySelectedRowHasTag()`, and `CLAUDE.md` records two separate bugs that lived in those three lines. Preserve the toggle: Delete twice still means "put it back". -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** ```cpp void TestMainWindow::deleteMovesTheMessageToTrash() @@ -793,7 +793,7 @@ whichever helpers are missing, following the form of the ones already there. observable state with `QTRY_VERIFY_WITH_TIMEOUT` and never on worker signals or a fixed `qWait(n)`. -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow deleteMovesTheMessageToTrash @@ -801,7 +801,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow d Expected: FAIL, the message is still in Inbox because Delete only tags. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** Add a `MoveCommand` in `src/mainwindow.h`, beside `MessageTagCommand` around line 1176, following its shape exactly: @@ -888,7 +888,7 @@ the current path, and pushes one `MoveCommand` carrying `deleted` and `deleted-from:<origin>`. An account with no `trash` key contributes nothing and reports through `statusMessage`, since Task 2 already warned at load. -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow @@ -896,7 +896,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow Expected: PASS, all tests. -- [ ] **Step 5: Commit** +- [x] **Step 5: Commit** ```bash git add src/mainwindow.h src/mainwindow.cpp tests/test_mainwindow.cpp @@ -918,7 +918,7 @@ are enforced by tests that fail confusingly: `KeyMap::knownActions()` (a `defaultBindings()` (every action must be keyboard-reachable), and the icon table (every action must carry one). -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** ```cpp void TestMainWindow::restoreIsOnlyEnabledInTheTrashView() @@ -985,7 +985,7 @@ void TestMainWindow::restoreFallsBackToInboxWithoutAnOriginTag() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow restoreIsOnlyEnabledInTheTrashView @@ -993,7 +993,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow r Expected: FAIL, there is no `restore` action. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** Add `"restore"` to `KeyMap::knownActions()` and give it a binding in `defaultBindings()`. Check what is free first: @@ -1030,7 +1030,7 @@ generator's, for the current account selection. `restoreSelected()` reads each row's `deleted-from:` tag for its destination and falls back to `Inbox`, reporting which through `statusMessage`. -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow @@ -1040,7 +1040,7 @@ ctest --test-dir build --output-on-failure Expected: PASS, 24 of 24. The keymap and icon-table tests fail loudly if a place was missed. -- [ ] **Step 5: Commit** +- [x] **Step 5: Commit** ```bash git add src/mainwindow.h src/mainwindow.cpp src/keymap.cpp tests/test_mainwindow.cpp @@ -1059,7 +1059,7 @@ git commit -m "feat: restore mail from the trash view" The user's constraint, verbatim: "the cleanup should be a menu entry only, not to be confused with the filter Trash". Do not add a sixth button. -- [ ] **Step 1: Write the failing test** +- [x] **Step 1: Write the failing test** ```cpp void TestMainWindow::theCleanupQueryFindsStrandedMail() @@ -1109,7 +1109,7 @@ void TestMainWindow::theCleanupQueryExcludesMailAlreadyInTrash() } ``` -- [ ] **Step 2: Run test to verify it fails** +- [x] **Step 2: Run test to verify it fails** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow theCleanupQueryFindsStrandedMail @@ -1117,7 +1117,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow t Expected: FAIL, there is no `cleanup_stranded` action. -- [ ] **Step 3: Write minimal implementation** +- [x] **Step 3: Write minimal implementation** Register the action in `knownActions()`, `defaultBindings()` and the icon table as in Task 6, then: @@ -1141,7 +1141,7 @@ as in Task 6, then: Add it to a menu, not to the query row. Find where the other menu entries are built and follow that form. -- [ ] **Step 4: Run test to verify it passes** +- [x] **Step 4: Run test to verify it passes** ```bash cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow @@ -1149,7 +1149,7 @@ cmake --build build && QT_QPA_PLATFORM=offscreen ./build/tests/test_mainwindow Expected: PASS. -- [ ] **Step 5: Commit** +- [x] **Step 5: Commit** ```bash git add src/mainwindow.h src/mainwindow.cpp src/keymap.cpp tests/test_mainwindow.cpp @@ -1168,7 +1168,7 @@ Every user-facing string added above needs a translation, and `lrelease` silently DROPS an unfinished string and ships it as English inside an otherwise Italian UI. Item 108 shipped fifteen strings that way. -- [ ] **Step 1: Refresh the translation source** +- [x] **Step 1: Refresh the translation source** ```bash lupdate-qt6 src/ -ts translations/qtmaildir_it_IT.ts -no-obsolete -locations none @@ -1178,7 +1178,7 @@ Expected: a clean run reporting zero context warnings. A "tr() cannot be called without context" warning means a literal needs `QT_TRANSLATE_NOOP("TheClass", "Text")` rather than `tr()`. -- [ ] **Step 2: Translate every new string** +- [x] **Step 2: Translate every new string** Open `translations/qtmaildir_it_IT.ts` and fill in each `<translation type="unfinished">`. The new strings are the Trash filter name, the Restore and @@ -1188,7 +1188,7 @@ messages. Do NOT translate notmuch query syntax. `tag:deleted`, `path:` and `deleted-from:` are wire format. -- [ ] **Step 3: Verify nothing is unfinished** +- [x] **Step 3: Verify nothing is unfinished** ```bash lrelease-qt6 translations/qtmaildir_it_IT.ts @@ -1197,7 +1197,7 @@ lrelease-qt6 translations/qtmaildir_it_IT.ts Expected: "Generated N translation(s) (N finished, 0 unfinished)". A nonzero unfinished count means a string will ship as English. -- [ ] **Step 4: Run the translations test** +- [x] **Step 4: Run the translations test** ```bash ctest --test-dir build -R translations --output-on-failure @@ -1205,7 +1205,7 @@ ctest --test-dir build -R translations --output-on-failure Expected: PASS. -- [ ] **Step 5: Write the changelog entry** +- [x] **Step 5: Write the changelog entry** Under `## [Unreleased]` in `CHANGELOG.md`, with an `### Upgrading` section, since a working config now warns until five keys are added: @@ -1243,7 +1243,7 @@ Note that Delete's reversibility depends on your provider: a trash folder that the provider purges on a timer will eventually remove the mail for good. ``` -- [ ] **Step 6: Commit** +- [x] **Step 6: Commit** ```bash git add translations/qtmaildir_it_IT.ts CHANGELOG.md @@ -1254,7 +1254,7 @@ git commit -m "i18n: translate the trash strings, and document the trash key" ## Task 9: Full verification -- [ ] **Step 1: Clean build** +- [x] **Step 1: Clean build** ```bash rm -rf build @@ -1264,7 +1264,7 @@ cmake --build build Expected: no warnings from the changed files. -- [ ] **Step 2: Full suite** +- [x] **Step 2: Full suite** ```bash ctest --test-dir build --output-on-failure @@ -1272,13 +1272,13 @@ ctest --test-dir build --output-on-failure Expected: 24 of 24 passing. -- [ ] **Step 3: Confirm the spec's claims hold** +- [x] **Step 3: Confirm the spec's claims hold** Re-read `docs/superpowers/specs/2026-08-17-delete-to-trash-design.md` and check each testing bullet has a test. The spec lists six; all six are covered by Tasks 1, 3, 4, 5, 6 and 7. -- [ ] **Step 4: Hand the build to the user** +- [x] **Step 4: Hand the build to the user** Do NOT run `./build/src/qtmaildir`. Report what to look at: |
