diff options
| -rw-r--r-- | docs/superpowers/plans/2026-09-12-mail-overview.md | 23 | ||||
| -rw-r--r-- | docs/superpowers/specs/2026-09-12-mail-overview-design.md | 11 | ||||
| -rwxr-xr-x | mail-overview/waybar-mail.sh | 10 |
3 files changed, 29 insertions, 15 deletions
diff --git a/docs/superpowers/plans/2026-09-12-mail-overview.md b/docs/superpowers/plans/2026-09-12-mail-overview.md index 63da50b..b1009ff 100644 --- a/docs/superpowers/plans/2026-09-12-mail-overview.md +++ b/docs/superpowers/plans/2026-09-12-mail-overview.md @@ -47,9 +47,14 @@ Do not re-derive these; they are why the code looks the way it does. `position` key (top is the default) and `margin: 0`. - There are **five** accounts, with notmuch tags `account-<key>` where `<key>` matches an `[account.<key>]` section in `qtmaildir.conf`. -- `notmuch` exits **0 even for a malformed query**: `notmuch count 'tag:unread - and (('` printed `40` and exited 0. Exit status alone cannot detect failure, - so every count is validated as a non-negative integer before use. +- `notmuch` handles a malformed query in two ways, and only one is + detectable. `notmuch count 'tag:unread and (('` returns `41` and exits 0, + because Xapian accepts the fragment and answers a different question: a + wrong count, indistinguishable from a right one. `notmuch count 'tag:unread + and ('` prints nothing and exits 1. Validating the output as a non-negative + integer catches the second, which is the one that would otherwise render as + an empty inbox. The first is not catchable and not worth chasing: these + queries are fixed strings, not user input. - An empty result is `0` from `count` and `[]` from `search --format=json`, both with exit 0. That is a legitimate zero, not an error. - `qtmaildir` accepts no command line arguments, so nothing can ask it to open @@ -205,12 +210,14 @@ Ctrl-C the script. - [ ] **Step 5: Verify the failure path renders as an error, not as zero** -Point the script at a query that notmuch accepts and then mangles, by -temporarily editing `QUERY` to `tag:unread and ((` and running it: +Point the script at a query notmuch rejects outright, which is the failure the +validation exists to catch: empty stdout and exit 1. Note that a *half* broken +query like `tag:unread and ((` will NOT do, because Xapian accepts it and +returns a plausible wrong number; use a single unclosed paren. ```bash -sed -i "s/^QUERY=.*/QUERY='tag:unread and (('/" mail-overview/waybar-mail.sh -./mail-overview/waybar-mail.sh | head -1 +sed -i "s/^QUERY=.*/QUERY='tag:unread and ('/" mail-overview/waybar-mail.sh +timeout 5 ./mail-overview/waybar-mail.sh | head -1 ``` Expected: `{"text":"!","tooltip":"notmuch count failed","class":"error"}` @@ -220,7 +227,7 @@ query would silently read as a real count. Restore the query: ```bash sed -i "s/^QUERY=.*/QUERY='tag:unread and tag:inbox'/" mail-overview/waybar-mail.sh -./mail-overview/waybar-mail.sh | head -1 +timeout 5 ./mail-overview/waybar-mail.sh | head -1 ``` Expected: the real count again. diff --git a/docs/superpowers/specs/2026-09-12-mail-overview-design.md b/docs/superpowers/specs/2026-09-12-mail-overview-design.md index a691d79..6b2423b 100644 --- a/docs/superpowers/specs/2026-09-12-mail-overview-design.md +++ b/docs/superpowers/specs/2026-09-12-mail-overview-design.md @@ -243,9 +243,14 @@ inotify watch on top of the timer. ## Error handling -- notmuch exits 0 even for a malformed query, printing something that is not - a count, so every count is validated as a non-negative integer and the exit - status is not the test. +- A malformed query is not reliably detectable. `notmuch count 'tag:unread + and (('` returns `41` and exits 0: Xapian accepts the fragment and answers a + different question, so a wrong count arrives looking exactly like a right + one. Nothing in this component can catch that, and it is not worth trying to; + the queries here are fixed strings, not user input. + What validation does catch is the clean failure: `notmuch count 'tag:unread + and ('` prints nothing and exits 1. Empty or non-numeric output therefore + renders as an error rather than as a zero. - notmuch missing, the database locked, or a count that fails validation: the count shows a dash, never a zero. A zero that is actually a failure reads as "no new mail", which is the same class of mistake as reporting a libvirt host side figure as guest diff --git a/mail-overview/waybar-mail.sh b/mail-overview/waybar-mail.sh index 06f8427..ee07ecb 100755 --- a/mail-overview/waybar-mail.sh +++ b/mail-overview/waybar-mail.sh @@ -30,10 +30,12 @@ emit() { local n n="$(notmuch count "$QUERY" 2>/dev/null)" - # notmuch exits 0 even for a malformed query, printing something that is - # not a count, so the exit status is not the test: the output is. Anything - # that is not a plain number is a failure, and a failure must not render - # as "no new mail". + # The output is the test, not the exit status. notmuch fails two different + # ways: a rejected query prints nothing and exits 1, while a query Xapian + # merely misparses ('tag:unread and ((') returns a plausible wrong number + # and exits 0. Only the first is detectable, and it is the one that matters + # here, because empty output would otherwise render as "no new mail". The + # second is why QUERY is a fixed string and not built from anything. if [[ ! "$n" =~ ^[0-9]+$ ]]; then printf '{"text":"!","tooltip":"notmuch count failed","class":"error"}\n' return |
