aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
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.md95
1 files changed, 95 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 ba8b99e..e6c7840 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
@@ -5101,3 +5101,98 @@ keep resetting the account.
Kept in full because the cause, the comment defending the reset, and the rules
preview that motivated it are all still true of the code.
+
+## 93. The query buttons are whatever the user pinned, not a designed set of filters
+
+**Observed (user, 2026-08-15),** reached by explaining item 90 rather than from
+the notes:
+
+> The queries that show as buttons shouldn't be in the same league as the ones I
+> write and store in the "more queries" menu. If I see "Unread" as a button, I
+> read it as "filter all my mails and show me only what is not yet read", but
+> being a button, in my head it should cooperate with other UI elements. So if a
+> dropdown offers to select an account, that same button should work with that
+> selection transparently, not fight it.
+
+**Cause.** Nothing ships as a default. `Config::startupSavedQuery()` falls back
+to `m_savedQueries.first()` and a fresh install has an empty query row, so every
+button the user has is one they wrote into `[queries]` and that the queries.json
+migration pinned (`src/config.cpp:455`). The buttons became "whatever is pinned"
+by migration, never by design. The user's own words: "that's the direction I
+wanted from the start, we drifted to what is today".
+
+**Approach.** Four built-in filters, Unread, Inbox, Flagged and Sent, as
+generated entries in the closed set `kQueryGenerators` that already exists for
+Sent. They compose with the account dropdown; saved queries keep setting the
+account from what they stored. The user's own pinned queries are UNPINNED once
+the buttons are confirmed working, never deleted, so they fold into the menu and
+stay recoverable.
+
+**Absorbs item 90.** The button that clears the account selection stops being a
+saved query at all, so there is nothing left to fix in `runSavedQuery()`.
+
+**Specified in `specs/2026-08-15-builtin-filters-design.md`.** Three constraints
+worth knowing before opening it: a generator must answer PER ACCOUNT rather than
+having its all-accounts query wrapped in a scope, or Sent double-scopes and works
+only by accident of `path:` being hierarchical; Sent stays `flat` and the other
+three do not, so the four match in scope and not in view mode; and changing the
+account deliberately runs nothing, since the button is the verb.
+
+**Size: M.**
+
+**Closed 2026-08-15, unreleased.** Four built-in filters ship as generated
+entries in `kQueryGenerators`, resolved through a new
+`Config::resolvedQuery(query, accountKey)` that asks a generator for the
+ACCOUNT'S OWN query rather than wrapping its all-accounts one. `runFilter()`
+reads the account box and never writes it, which is the whole of item 90;
+`runSavedQuery()` still sets the account from what the entry stored, because a
+saved query is a destination.
+
+Three things worth keeping.
+
+The Sent trap the spec predicted is real and the tests assert on the query
+STRING because of it: wrapping gives `path:"a/**" and (path:"a/Sent/**" or
+path:"b/Sent/**")`, which returns the right rows because `path:` is
+hierarchical, so a row-count assertion passes against the wrong query. The
+mutation putting the wrap back fails two tests.
+
+Migration unpins and never deletes, in both directions: Sent is no longer
+migrated out of the INI at all, and a stored entry naming a known generator is
+unpinned on load, which is what every install upgraded through 0.19.0 carries.
+The user unpinned their own queries by hand, as they had asked to.
+
+A rendering probe had to be fixed rather than adapted, and it is the documented
+failure mode. `replyRowsKeepTheirTextUnderTheThreadLine` sized the window to
+300px; four more buttons pushed the reply row below the viewport, the pixel loop
+ran zero times, and it reported "0 pixels, the row was painted over", which is a
+different defect from the one it exists to catch. It has 600px and a guard
+asserting the row is inside the viewport, verified by putting 300 back: it now
+names the row at 83..165 in an 82px viewport.
+
+## 95. A query in the overflow menu cannot be run
+
+**Observed (user, 2026-08-15),** hand testing item 93:
+
+> now no entry in that menu is runnable, they all look like submenus and offer
+> all options to edit, show as buttons, etc.
+
+**Cause, and it is NOT item 93's.** `buildSavedQueryRow` gave each menu entry
+both a `triggered` connection and a submenu of edit actions. **Qt emits no
+`triggered` for an action that owns a menu**: clicking the entry opens the
+submenu and does nothing else, so that connection had never fired, in any
+release that had an overflow menu.
+
+It went unnoticed because the menu was the rarely-used half while the user's
+queries were pinned buttons. Item 93 moved every query into the menu, which is
+how it surfaced, and item 94 makes the menu their only home, so this is now the
+path that has to work.
+
+**Closed 2026-08-15, unreleased.** Running is an item INSIDE the submenu, first
+and above a separator, with Edit, the pin toggle and Delete below it. The entry
+keeps its submenu, because an unpinned query must not become the one thing that
+cannot be edited or deleted.
+
+The test asserts the Run item exists, that it is first, that triggering it
+reaches the query, and that the edit actions survived beside it. Restoring the
+old wiring fails it on the first of those and names the Qt behaviour rather than
+reporting a wrong query string.