aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/plans')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md44
1 files changed, 43 insertions, 1 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 051d6c4..3fed54e 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
@@ -189,7 +189,7 @@ taking that too literally.
| 121 | The thread list shows nothing while a query is running | feedback | S | open, 2026-08-20, from the notes. Follows item 74, which fixed the status-bar half and left the list itself blank |
| 122 | The README documents a version of the app that no longer exists | documentation | M | open, 2026-08-20, from the notes. Delete-to-trash is entirely undocumented, including a config key a user must now set |
-| 123 | Sending mail is not designed | v2 | L | **specified** 2026-08-20, on branch `compose-and-send`. Design in `docs/superpowers/specs/2026-08-20-compose-and-send-design.md`; read that, not this row. Send is a per-account `send_command` on stdin, so the no-network-protocol rule stands. Composer is a separate window, body is markdown via cmark-gfm, drafts autosave to the account's drafts folder. No code written |
+| 123 | Sending mail is not designed | v2 | L | **specified** 2026-08-20, on branch `compose-and-send`. Design in `docs/superpowers/specs/2026-08-20-compose-and-send-design.md`; read that, not this row. Send is a per-account `send_command` on stdin, so the no-network-protocol rule stands. Composer is a separate window, body is markdown via cmark-gfm, drafts autosave to the account's drafts folder. Tasks 1 to 12 of 13 built 2026-08-20 to 2026-08-22; task 13, the close-out, is the remainder. **Never hand tested**: nothing had wired a composer to an action until task 12, so no composer has yet been opened by a human. Twenty-two defects were found in the plan's own draft code across tasks 4 to 12, so treat every code block in it as a draft |
| 124 | The worker reads the index directory as the mail root | defect | S | **done** 2026-08-20, unreleased. `mailRootOf()` over `NOTMUCH_CONFIG_MAIL_ROOT`, correct under both layouts. Verified by migrating the developer's own index to NVMe the same day: cold start 38.6 s to 0.67 s |
@@ -206,6 +206,7 @@ taking that too literally.
| 134 | The busy indicator is built inline and is about to be built twice | maintenance | S | done, 2026-08-20, af902e0. `BusyIndicator` (`src/busyindicator.h`) carries both modes: `MainWindow` uses the indeterminate one, and item 123's send popup takes the determinate half for its undo countdown, switching the same widget over when the command starts. Only the BAR was extracted, not the status label this row paired with it. `m_statusLabel` has 34 uses across `MainWindow` for transient messages, selection counts and sync phases, so it belongs to the window rather than to the indicator, and the send popup owns its own phase text |
| 135 | The formatting toolbar's buttons stack rather than toggle | v2 | S | open, 2026-08-21, asked for by the user during item 123 task 8 and reverted the same session. **A spec change, not a defect**: it conflicts with spec:236 ("deliberately no live toggle") and spec:187-190. Both sites need amending FIRST, and the amendment must resolve what replaces bold-then-italic, which is the gesture spec:187's preserved selection exists to serve and which a toggle makes unreachable. That question is the work; the state machine is understood and written up in the section |
| 136 | `undoMovesTheMessageBack` fails about one run in six | defect | ? | open, 2026-08-21, found while running the suite during item 123 task 10. A pre-existing race in the test or in Delete's file move, NOT caused by 123: reproduced on a clean tree with the branch's work stashed out, 1 failure in 6 runs, and the failing run took 70s against a normal 25s. Unrelated to `SendDialog`. Size unknown until the race is located |
+| 137 | A reply to a message that arrived at two accounts can come from the wrong one | defect | S | open, 2026-08-22, found while building item 123 task 12. `ComposeContextBuilder::accountForReply()` takes `messagePaths` PLURAL to disambiguate, and nothing upstream ever gives it more than one path, so the disambiguation is inert |
Sizes are rough: XS under an hour, S a sitting, M a session.
@@ -1215,6 +1216,47 @@ whether to open the spec at all, and leave the rest there.
Name the spec `<date>-<name>-design.md`, and state in its header which backlog
items it resolves, so the numbering stays traceable in both directions.
+## 137. A reply to a message that arrived at two accounts can come from the wrong one
+
+**Observed.** A message that exists in more than one maildir, because it was
+sent to two of the user's addresses or duplicated across accounts by mbsync,
+can open its reply from either account. Which one is picked is arbitrary. The
+consequence is visible in the composer's From field, so it is not silent, but
+it is only visible to somebody who thinks to look: the reply is otherwise
+correct and sendable, and the recipient sees a From the user did not intend.
+
+**Cause, verified in the code.** The disambiguation exists and is unreachable.
+`ComposeContextBuilder::accountForReply()` (`src/composecontext.cpp:405`) takes
+`messagePaths` as a `QStringList` precisely so it can resolve this case: with
+more than one candidate account it prefers the one whose own address appears
+among the recipients, which is the reason the copy landed there. Nothing
+upstream ever gives it more than one path. `NotmuchWorker::loadMessage()`
+(`src/notmuchworker.cpp:573`) builds its `MessageRef` from
+`notmuch_message_get_filename()`, the SINGULAR accessor, so `MessageRef` holds
+one `filePath` and `MainWindow::openComposerFor()` can only pass a
+one-element list. The plural parameter is therefore inert, and the branch that
+consumes it is dead code today.
+
+`notmuch_message_get_filenames()`, the plural accessor that would supply the
+rest, exists in libnotmuch and is used nowhere in this repository.
+
+**Approach.** Add `QStringList filePaths` to `MessageRef` (`src/types.h:123`)
+ALONGSIDE the existing `filePath` rather than replacing it, and populate it in
+`loadMessage()` from `notmuch_message_get_filenames()`. `filePath` stays as the
+render path, so `MainWindow::renderMessages()` and everything else that opens
+one file are untouched; only `openComposerFor()` reads the new field. That
+keeps the change to two files plus the one call site.
+
+**Constraints.** The test has to put the same message id in two accounts'
+maildirs, which `NotmuchFixture` can do by writing the same `Message-ID` into
+two folders before indexing. Assert on the account CHOSEN rather than on a
+count of paths: a test that only checks `filePaths.size() == 2` passes against
+`accountForReply()` still ignoring them. The recipient-preference branch is
+what needs covering, so the two accounts must have different addresses and the
+message must be addressed to one of them, or either answer is correct and the
+test proves nothing.
+
+
## 136. `undoMovesTheMessageBack` fails about one run in six
**Observed.** `test_mainwindow` failed during a full-suite run while item 123