From 1fa8abd93fb9ada4c3dbad06a363da0b6a09e392 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sat, 15 Aug 2026 16:20:07 +0200 Subject: docs(backlog): specify items 91 and 92 from the user's clarifications Both were "open, unspecified" and could not be planned from the backlog alone. 91 is not a second window. The user meant a drill-down to the selected thread or message, the same result an `id:` query gives, reached by double-clicking instead of typing. That removes the constraint the entry was mostly made of, since no second QWebEngineView and no extra render process are involved, and takes it from ? to S. What is left to decide is how the user gets back, and that runQuery() clears the undo stack, which a gesture-triggered query would do silently. 92 is the expensive half of the two it held: the user wants a per-message hint, rule-written against hand-applied, and doubts its utility in the same sentence. Nothing records that today, so it is a format change across both repos before it is any pixels, and it stays blank on every message already tagged. Recorded beside it is the cheaper question that may be the real one, "which rule would tag this message", answerable from rules.json at read time with no stored provenance and no hook change. Co-Authored-By: Claude Opus 5 --- .../plans/2026-08-03-post-0.1.0-usability.md | 107 ++++++++++++++------- 1 file changed, 72 insertions(+), 35 deletions(-) (limited to 'docs/superpowers') 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 4a68f0b..e55bad4 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 @@ -155,8 +155,8 @@ taking that too literally. | 86 | A right-click search can replace or narrow, but never exclude | workflow | S | **done** 2026-08-14, unreleased; see `specs/2026-08-14-exclude-from-search-design.md`. Follows 85. The `extend` bool became a `SearchMode` enum across four signatures | | 89 | A sync moves the list under the user's hands, and the auto-sync skips rather than retries | workflow | XS | **done** 2026-08-15, unreleased. The timer half only: a skipped auto-sync re-arms instead of giving up. The list-churn half is **dropped**, not built: the user resolved it as a mental-model question, an Unread view is SUPPOSED to be volatile | | 90 | A saved-query button clears the account selection | workflow | S | **folded into 93** 2026-08-15. Not fixed in place: the button that misbehaves stops being a saved query at all. See `specs/2026-08-15-builtin-filters-design.md` | -| 91 | Double-clicking a thread could open it in its own window | workflow | ? | open, unspecified; the user marked it "(?) UX not sure" | -| 92 | Nothing distinguishes a tag written by a rule from one the user applied | information | ? | open, unspecified; the user asked it as a question, and the answer decides whether it is a display item or a format change across two repos | +| 91 | Double-clicking a thread should open it on its own | workflow | S | open, specified 2026-08-15. NOT a second window: a drill-down to that one thread, the same result an `id:` query gives. One decision left, how the user gets back | +| 92 | Nothing distinguishes a tag written by a rule from one the user applied | information | M | open, specified 2026-08-15. The user wants per-MESSAGE provenance, which nothing records: a two-repo format change, blank on all existing mail. Put the cheaper "which rule would tag this?" to the user first | | 93 | The query buttons are whatever the user pinned, not a designed set of filters | workflow | M | **done** 2026-08-15, unreleased; see `specs/2026-08-15-builtin-filters-design.md`. Absorbs item 90. Four built-in filters composing with the account dropdown; the user's own queries unpinned, never deleted | | 95 | A query in the overflow menu cannot be run | defect | XS | **done** 2026-08-15, unreleased. Pre-existing and not caused by 93: the entry's action owned a submenu, and Qt emits no `triggered` for those, so the connection had never fired. Surfaced because 93 moved every query into the menu | | 94 | `pinned` has nothing left to decide once the buttons are built-in | maintenance | S | open; **blocked on 93**, and deliberately not part of it. A user-visible removal: the row becomes built-ins only and every saved query lives in the menu | @@ -493,49 +493,86 @@ bump either way: an ignored optional field is not a breaking change. **Size: S.** Removing a field, two UI affordances and their tests. -## 91. Double-clicking a thread could open it in its own window +## 91. Double-clicking a thread should open it on its own **Observed (user, notes):** "double clicking a message/thread should open it in its own view (?) UX not sure." -**Unspecified, and the user says so in the note itself.** Recorded so it is not -lost, not planned. What would have to be decided first: whether "its own view" -is a separate top-level window or a tab, whether it stays live as the list -refreshes under it, and what it does that the message pane does not. There is no -cause to verify because nothing is broken. - -**One constraint worth writing down now**, because it decides the size: the -message pane is a `QWebEngineView` inside `MessageView`, and a second window -means either a second web view, which spawns a Chromium render process per -window, or moving the one view between parents. `CLAUDE.md` records the -per-view process cost as the reason the pane renders a list into a single view. -A design that opens three messages in three windows is three render processes. +**Clarified by the user, 2026-08-15**, and it is NOT a second window: "I didn't +mean open in a new window, I meant that double clicking on a message/thread in +the left pane should open it by itself, same as searching by id and displaying +only that message." So this is a drill-down into the existing view, the same +result an `id:` query produces, reached with a gesture instead of by typing. + +That removes the constraint this entry was mostly made of. No second +`QWebEngineView`, no second render process, nothing reparented: the pane and +the list are the ones already on screen. Size drops from `?` to S. + +**Cause (verified in code):** nothing is wired to `doubleClicked` on +`ThreadListView`, so a double-click today is two single-clicks and selects the +row twice. The machinery a drill-down needs already exists and is exercised by +the query bar: `SearchTerm` builds the `id:` query and `runQuery()` runs it. + +**Approach.** On `doubleClicked`, run the query naming that row: `id:` for a +message row, `thread:` for a thread row, built through `SearchTerm` so the +quoting rules stay in one place. + +**Decide before building: how the user gets back.** A drill-down that cannot be +reversed is a trap, and this is the only real design question left in the item. +Three candidates, and they are not equivalent: + +- Esc. Already means "clear the pane", then "clear the selection". A third + meaning is one too many. +- A Back action, with its own binding and toolbar button. Honest, and the only + option that survives a drill-down FROM a drill-down. +- Restoring the previous query text. Cheapest, and wrong the moment the user + edits the bar while drilled in. + +**Constraint.** `runQuery()` clears the undo stack, by design, since undo +entries refer to rows that are about to be discarded. A drill-down is still a +query, so a double-click would silently discard undo history. Either accept it +and say so, or the drill-down is not a query at all but a filter over the model, +which is item 40's machinery and makes this M rather than S. ## 92. Nothing distinguishes a tag written by a rule from one the user applied **Observed (user, notes):** "should the UI allow to discriminate when a message has been tagged by a rule?" -**Asked as a question, and the answer decides the size**, which is why this is -unspecified rather than sized. The information does not exist anywhere today: a -tag written by the `post-new` hook is an ordinary notmuch tag and carries no -provenance, so the UI cannot show what nothing records. - -That makes it two very different items wearing one sentence: - -- **Display only.** If the question is "which of these tags could a rule have - written", it can be answered from `~/.config/mailrules/rules.json` without - storing anything: the rules name their tags, so a chip for a tag that some - rule writes can be drawn differently. Cheap, and honest only as "a rule writes - this tag", never as "a rule wrote THIS one". -- **Real provenance.** If the question is "was THIS message tagged by a rule", - something has to record it, and that is a format change to the shared rules - file or an extra tag per rule, applied by the hook. That is a two-repo change - under "Changing the shared rule format" in `CLAUDE.md`, with a live hook - running every ten minutes on real mail. - -**Approach.** Ask which question the user meant. Do not start on the second one -by inferring it from the first. +**Clarified by the user, 2026-08-15**, including the doubt: "I want a small +visual hint that an email has been tagged by a rule vs tagged by me, but I'm +questioning the utility of such a hint." + +That is the expensive half of the two this item used to hold. "Tagged by a rule +vs tagged by me" is per-MESSAGE provenance, and the information does not exist +anywhere today: a tag written by the `post-new` hook is an ordinary notmuch tag, +indistinguishable from one applied by hand. The UI cannot show what nothing +records, so the hint costs a format change before it costs any pixels. + +**What it would take**, and this is why the user's own doubt is the right +instinct: + +- A marker the hook writes beside every tag it applies, either an extra tag per + rule (`rule:justeat`) or a field in the shared file. Both are two-repo changes + under "Changing the shared rule format" in `CLAUDE.md`, against a hook running + every ten minutes on real mail. +- Retroactively, nothing. Every tag already on every message stays unattributed + forever, so the hint is blank on the entire existing Maildir and fills in only + as new mail arrives. A hint that is absent on most mail teaches the user to + ignore it. + +**A cheaper question that may be the real one.** If what the user actually wants +to know is "why does this message carry this tag", that is answerable with no +stored provenance at all: the rules name their tags, so the UI can say which +rule WOULD tag a given message, read from `~/.config/mailrules/rules.json` at +the moment it is asked. No format change, no hook change, nothing to backfill, +and it works on mail tagged before the feature existed. It answers "a rule +writes this tag" and never "a rule wrote THIS one", which is a weaker claim and +an honest one. + +**Approach.** Put the cheaper question to the user before building either. If +the answer is that the per-message distinction is genuinely wanted, this is an M +spanning both repos and should be specced, not slipped into a session. **Constraint.** The hook is not this repo's to weaken. If provenance is wanted, the marker has to be something the hook can write without touching `unread` or -- cgit v1.2.3