From 7a810640b2fabafd8da524b275cb4453a1953df6 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 25 Aug 2026 11:33:29 +0200 Subject: chore(hooks): log how many messages the sent carve-out matched Item 164 diagnostics. A `notmuch tag` that matches nothing succeeds, so the carve-out logged "applied over N folder(s)" whether it stripped `inbox` from four messages or from none. A draft kept `inbox` on a pass whose log claimed the carve-out had run, and that line could not tell the two cases apart. Count before tagging, since the tag is what makes the count zero, and report it alongside the folder count. Nothing branches on the value: a failed count yields `?` rather than failing the sync, because the tag reports its own status separately. This does not fix item 164, whose trigger is still unreproduced. It makes the next occurrence self-explaining: `0 message(s)` means the message was never in tag:new scope, and a non-zero count means the tag ran over it and something re-added the tag afterwards. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UUQS6n3cmsFrsjCNmwNtf8 --- assets/hooks/post-new | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/assets/hooks/post-new b/assets/hooks/post-new index 5102103..428ec29 100755 --- a/assets/hooks/post-new +++ b/assets/hooks/post-new @@ -104,10 +104,21 @@ def strip_inbox_from_sent(run): return True query = f"{SCOPE} and ({qtmaildirconf.sent_query(folders)})" + + # Counted BEFORE the tag, because the tag is what makes the count zero. + # A `notmuch tag` that matches nothing SUCCEEDS, so the old log line said + # "applied" whether it stripped four messages or none, and item 164 is + # exactly the case where that distinction is the whole question: a draft + # kept `inbox` on a pass whose log claimed the carve-out had run. The + # count is the only thing that separates "the tag ran and something + # re-added inbox afterwards" from "the message was never in scope". + matched = count(query) + if not run(["-inbox"], query): return False - log(f"sent-folder carve-out applied over {len(folders)} folder(s)") + log(f"sent-folder carve-out applied over {len(folders)} folder(s), " + f"{matched} message(s)") return True @@ -116,6 +127,20 @@ def protected_removals(rule): return sorted(PROTECTED_REMOVALS.intersection(rule.remove)) +def count(query): + """How many messages a query matches, or `?` if the count itself failed. + + Diagnostic only: nothing branches on this. A failure here must not fail + the sync, because the carve-out's own tag is what matters and it reports + its own status separately. + """ + result = subprocess.run(["notmuch", "count", "--", query], + capture_output=True, text=True) + if result.returncode != 0: + return "?" + return result.stdout.strip() or "?" + + def run_tag(arguments, query): result = subprocess.run(["notmuch", "tag"] + arguments + ["--", query], capture_output=True, text=True) -- cgit v1.2.3