aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-13 09:32:08 +0200
committerDanilo M. <danix@danix.xyz>2026-08-13 09:32:08 +0200
commit4929cb81ac2cda79e4f4b411711130d6e0f8173f (patch)
treed362112fc9de3ddd8522aacb41e7d91f7e778b22 /CLAUDE.md
parent8b67ed8d6d727aa99c091c6eb46160ecb6300f72 (diff)
downloadqtmaildir-4929cb81ac2cda79e4f4b411711130d6e0f8173f.tar.gz
qtmaildir-4929cb81ac2cda79e4f4b411711130d6e0f8173f.zip
docs(backlog): record item 44 as done
The tagging rules moved from the shell post-new hook to a shared JSON store both qtmaildir and mailctl read. Seventeen real rules were converted, each keeping its shell comment as a note, and the conversion was proved against the real index before anything was installed. Four findings are recorded in CLAUDE.md rather than only here, because they will outlive the item: a stored query carries no scope and the hook parenthesises it (a disjunction would otherwise escape tag:new and match everything); notmuch's parser rejects almost nothing, so a test asserting a provoked query failure fails against correct code; rule counts must count messages rather than threads; and a count request must not bump the query generation, which would blank the message pane.
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md23
1 files changed, 23 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index d1d2984..c9d6cb3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -214,6 +214,29 @@ completion descriptions. Query syntax itself is not user-facing text — notmuch
like `tag:` and `date:` are wire format and must never be translated, only the prose
describing them. Pre-existing code has not been audited against this rule.
+**The auto-tagging rules are NOT in this repo, and notmuch's parser rejects
+almost nothing.** Rules live in `~/.config/mailrules/rules.json`, applied by a
+notmuch `post-new` hook that ships from the companion `mailctl` project;
+`TagRules` here reads and writes the same file and `TagRulesDialog` edits it.
+Two things bite. A stored query carries NO scope: the hook supplies `tag:new`
+and wraps the query in parentheses, because `tag:new and a or b` binds as
+`(tag:new and a) or b` and a rule that is a disjunction of senders would escape
+its scope and match everything. And **a malformed query is not an error to
+notmuch**: `from:((((` parses cleanly and matches nothing, so a test asserting
+a failure or a `-1` count fails against correct code. This was recorded in
+`test_notmuchworker.cpp` for thread counts and then learned again, twice, while
+building the rules. Assert on the positional contract, never on a provoked
+failure.
+
+**Rule counts must count MESSAGES.** `requestCounts` counts threads, which is
+right for the placeholder pane because a click there produces thread rows. A
+rule tags messages, so a thread count understates every rule matching part of a
+large thread; `requestMessageCounts` exists beside it for that reason. The two
+are separate signals with separate generation counters, and a count request
+must never bump `m_generation`: that is the *query* generation, and bumping it
+discards any thread load in flight, blanking the message pane because the user
+asked for counts.
+
**Config format gotcha:** QSettings treats `/` in a section name as a group separator, so
account sections are `[account.work]`, not `[account/work]`. `childKeys` returns keys
sorted alphabetically, never in file order. **`[general]` keys are read WITHOUT the