summaryrefslogtreecommitdiffstats
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.md55
1 files changed, 53 insertions, 2 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 7fedaf3..8f19d6b 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
@@ -56,8 +56,8 @@ taking that too literally.
| 15 | Attachments are parsed but unreachable from the UI | information | M | **done** |
| 16 | Delete on an already-deleted thread should undelete | behavior | S | open |
| 17 | No completion for tags in the query bar | workflow | M | **done** |
-| 18 | No visual cue that there are unsynced edits | feedback | S | open |
-| 19 | No prompt to sync on exit when edits are pending | behavior | S | open |
+| 18 | No visual cue that there are unsynced edits | feedback | S | **done** |
+| 19 | No prompt to sync on exit when edits are pending | behavior | S | **done** |
| 20 | Thread view does not match the user's mental model | presentation | ? | open, unspecified |
| 21 | Default shortcuts are not sensible enough | discoverability | S | open |
| 22 | Translatability audit and i18n wiring | correctness | M | open |
@@ -847,6 +847,27 @@ resets.
survives. Then sync and confirm it clears. Then make the sync fail and confirm
it does not.
+### Outcome (done)
+
+Built as specced. `m_pendingEdits` counts confirmed mutations, incremented in
+`onTagsApplied()` and reset only by `onSyncFinished(true, ...)`. The count is
+shown as a permanent status-bar widget, hidden entirely at zero.
+
+**Counted where a write is confirmed, not where one is sent.** An optimistic
+update the worker later rejects must not leave the indicator claiming an edit
+that never landed, so the increment sits in the `tagsApplied` handler.
+
+That handler was a lambda; it is now a named slot, which is both better
+structure and what lets a test drive it. An earlier draft tried to reach the
+worker with `findChild` to emit the real signal: the worker is deliberately
+parentless because it moves to its own thread, so that cannot work, and
+contorting the test to reach it was the wrong instinct. What matters is the
+counter's behaviour, not the signal's origin.
+
+**Both tests were verified by breaking the code.** Clearing the count on any
+sync outcome is caught, and so is clearing it where the undo stack is cleared,
+which is the naive design this item exists to avoid.
+
## 19. No prompt to sync on exit when edits are pending
**Observed (user, 2026-08-04):** quitting with unsynced edits is silent. The
@@ -891,6 +912,36 @@ for the reason item 18 documents.
each `sync_on_exit` value and confirm the behavior matches. Then quit with a
deliberately broken sync command and confirm the app neither hangs nor lies.
+### Outcome (done)
+
+Built as specced, with the user confirming the three-value key over their
+original on/off idea. `closeEvent()` consults the count, and a sync started for
+exit holds the window open until `finished` arrives rather than being killed
+mid-run.
+
+**A failed exit-sync does not quit.** It restores the window, shows the log and
+says what happened. Quitting there would discard the user's choice silently,
+which is the exact failure the prompt exists to prevent.
+
+**With no sync command configured the prompt degrades** to a warning offering
+Discard or Cancel, rather than offering a sync that cannot run.
+`MailSync::isAvailable()` is already false in that case.
+
+**Testing a modal needs care, and the first attempt was wrong.** A test that
+sends a close event hangs forever if an unexpected dialog opens, because the
+modal spins its own event loop. The first version appeared to pass in 19
+seconds; it had actually opened a real dialog on the user's screen, and the
+user dismissed it. `CloseProbe` now polls for `activeModalWidget()`, closes it,
+and records that one appeared, turning "a dialog opened" into an assertion
+instead of a hang or a prompt for whoever is watching.
+
+**One mutation test was a dud and is worth recording.** Removing the `Never`
+guard from `closeEvent` did NOT make the test fail, because the branches below
+match only `Ask` and `Always`, so `Never` fell through to closing anyway. The
+guard is redundant belt-and-braces rather than load-bearing. Confirming the
+probe really detects a prompt needed the config mutated to `ask` instead, which
+does fail it.
+
## 20. Thread view does not match the user's mental model
**Observed (user, 2026-08-04), in passing while deciding item 2:** "My view for