[PATCH b4 1/2] Detect naked cover letters that lack a 0/N prefix and diffstat

Christian Brauner <[email protected]> Wed, 01 Jul 2026 15:51:21 +0200
Newsgroups org.kernel.linux.tools
Message-ID <[email protected]>
LoreMailbox recognized a cover letter only when the message carried an
explicit zero counter ([PATCH 0/N]) or, via the in-reply-to fallback in
get_series(), when the thread root carried a diffstat.  A "naked" cover
-- one with neither a [PATCH 0/N] subject prefix nor a diffstat in the
body -- matched neither test.  It was filed under self.unknowns and
dropped, so LoreSeries.has_cover stayed False.

The user-visible fallout is in shazam -M, which then reports "No cover
letter provided by the author" and links the first patch instead of the
real cover.  This bites postings whose author writes a short prose cover
with no diffstat, e.g. Christoph Hellwig's "don't build bios/contexts
over multiple iomaps" series, whose cover carries no [PATCH 0/N] prefix
and no diffstat:

  https://lore.kernel.org/linux-xfs/[email protected]

Extend the in-reply-to fallback to also accept the parent as the cover
when it is a non-patch, same-author thread root (in_reply_to is None and
not itself a reply).  Requiring the same author and the thread root keeps
us from mistaking an unrelated message the series was merely sent
in-reply-to -- a bug report from someone else, say -- for a cover letter.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 src/b4/__init__.py | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index d7b41ee..ad5f62f 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -393,10 +393,15 @@ class LoreMailbox:
             for member in lser.patches:
                 if member is not None and member.in_reply_to is not None:
                     potential = self.msgid_map.get(member.in_reply_to)
-                    if (
-                        potential is not None
-                        and potential.has_diffstat
-                        and not potential.has_diff
+                    if potential is None or potential.has_diff:
+                        continue
+                    # A diffstat marks the classic cover letter; a naked cover
+                    # (no [PATCH 0/N], no diffstat) is only trusted when it's a
+                    # same-author thread root.
+                    if potential.has_diffstat or (
+                        potential.in_reply_to is None
+                        and not potential.reply
+                        and potential.fromemail == member.fromemail
                     ):
                         # This is *probably* the cover letter
                         lser.patches[0] = potential

-- 
2.53.0