From 847f3051b0918efc315321d4b65ad0109ea7979d Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 14 Aug 2026 11:17:59 +0200 Subject: docs: spec turning a saved query into a tagging rule Item 81. A context-menu action on a stored saved query, seeding the rules dialog with the query and a sanitised id, tags left empty and focused. Three decisions worth the record. The backlog's own proposal, a checkbox in the Save query dialog, is rejected: it would make one dialog write both queries.json and the shared rules.json, and SaveQueryDialog is deliberately pure UI that writes nothing. Generated entries are excluded, since their query is composed from the accounts at runtime and a rule made from one would freeze a snapshot that goes stale when an account is added. And the empty tags are load-bearing rather than an omission: validate() refuses a rule that tags nothing, so the one field the user must supply is the one the dialog opens on. This also turns out to be a single-repo change. The rule it creates is an ordinary one, so mailrules.py is untouched; the backlog's note that item 81 spans two repos was about the file it lands in, not the work. Item 78 becomes a second caller of the same seeded-dialog path. Co-Authored-By: Claude Opus 5 --- .../plans/2026-08-03-post-0.1.0-usability.md | 63 +++++++++------------- 1 file changed, 25 insertions(+), 38 deletions(-) (limited to 'docs/superpowers/plans') 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 e60b88c..316b5dc 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 @@ -145,7 +145,7 @@ taking that too literally. | 78 | No way to build a rule from something visible in a message | workflow | M | open; wants 76 first, so the created rule lands in a form that can hold it | | 80 | A rule with many conditions squeezes the rule list to one visible row | defect | XS | **done** 2026-08-13, shipped in 0.17.0. Follows item 76 | | 79 | Opening the rules dialog and saving destroys the first rule | defect | XS | **fixed on `rule-builder`** 2026-08-13, unreleased. Shipped in 0.16.0; damaged one real rule, repaired by hand | -| 81 | No way to turn a saved query into a tagging rule | workflow | S | open; 23 has shipped, so the dialog it depends on exists. Writes to the shared rules file, so it spans this repo and `mailctl` | +| 81 | No way to turn a saved query into a tagging rule | workflow | S | open, specified 2026-08-14; see `specs/2026-08-14-query-to-rule-design.md`. A context-menu action seeding the rules dialog, single repo | | 82 | A saved query cannot be edited, unpinned or deleted from the UI | defect | S | **done** 2026-08-13, shipped in 0.18.0. Right-click offers Edit, Pin/Unpin and Delete | | 83 | A rule named with spaces is written to the file and dropped by every reader | defect | S | **done** 2026-08-14, unreleased. The name is sanitised into an id, save validates, a bad id loads for repair | @@ -504,43 +504,30 @@ in CLAUDE.md. ## 81. No way to turn a saved query into a tagging rule -**Observed (user, 2026-08-13):** raised while describing item 23. The save-query -dialog could optionally keep the query as a tagging rule as well as a saved -query, "going the opposite route of creating a filter in the dialog and running -a preview first". Item 77 shipped that opposite route: a rule in the dialog can -be previewed in the thread list. This is the same journey backwards, from a -query the user has already run and can see the results of, to a rule that tags -it from then on. - -**Why it is its own item rather than part of 23.** A saved query is a view and -costs nothing if it is wrong. A rule is applied to real mail by a `post-new` -hook every ten minutes, and lives in `~/.config/mailrules/rules.json`, which has -two independent implementations that agree by test rather than by shared code. -Item 23 is otherwise a presentation change; folding this in would make it carry -a two-repo commitment and a live blast radius. Kept separate so 23 can ship -without it. - -**Approach.** A checkbox or a second button in the save dialog that hands the -query to `TagRules` alongside the `queries.json` write, with the tags to apply -asked for at that point: a rule needs an action, and a saved query has none. -Preserve unknown fields, per "Changing the shared rule format" in CLAUDE.md. -Note that two separate files are then written from one dialog, `queries.json` -here and `rules.json` shared with mailctl, so decide what happens when the -second write fails after the first succeeded. - -**Constraints.** A stored query carries no scope: the hook supplies `tag:new` -and parenthesises the query, because `tag:new and a or b` binds as -`(tag:new and a) or b`. A saved query written for a view is frequently a -disjunction, so this is the common case here rather than an edge one, and a -rule made this way is scoped differently from the query the user just ran. Say -so in the dialog. The hook also refuses to remove `unread` or `inbox`, so a -dialog that offers to write such a rule produces one the hook will not honour; -that is the correct direction, but it has to be said in the UI rather than -failing silently. - -**Depends on item 23**, which builds the dialog this hangs off. - -**Size: S** on top of 23, and not meaningful before it. +**Observed (user, 2026-08-13):** raised while describing item 23. A query the +user has already run and can see the results of should be turnable into a rule +that tags it from then on. Item 77 shipped the opposite route, previewing a +rule's mail in the thread list; this is that journey backwards. + +**Specified 2026-08-14. Read `specs/2026-08-14-query-to-rule-design.md` +instead of planning from here.** + +The three things that decide whether it can be picked up: + +- **A context-menu action, not a checkbox in the Save query dialog.** The + original approach below was rejected: it would make one dialog write both + `queries.json` and the shared `rules.json`, and `SaveQueryDialog` is + deliberately pure UI that writes nothing. +- **It is a single-repo change after all.** The rule it creates is an ordinary + one, so nothing about the shared format changes and `mailrules.py` is + untouched. The earlier note that this item spans two repos was about the file + it lands in, not about the work. +- **Stored queries only.** A generated entry's query is composed from the + accounts at runtime, so a rule made from one would freeze a snapshot that goes + stale when an account is added. + +**Item 78 folds into the same path**, as a second caller with a different seed +rather than a second mechanism. ## Deferred, unsized, or split out -- cgit v1.2.3