)]}'
{
  "log": [
    {
      "commit": "53e4aabf326337b1fdb6be9d878f1874abc5d1eb",
      "tree": "346bdd2bc1f8ee06b7f8e6f9c174cc7aff233369",
      "parents": [
        "cd5733ef455449f50aac8e276ea7dff1d638d04c"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Feb 28 15:35:34 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:18:14 2026 -0400"
      },
      "message": "review: add sandbox wrappers and docs for AI review agents\n\nAdd bwrap and firejail wrapper scripts that sandbox review agents\nagainst prompt injection in untrusted patches. Document agent\nconfiguration including Cursor CLI and sandboxing.\n\nSigned-off-by: Michael S. Tsirkin \u003cmst@redhat.com\u003e\n"
    },
    {
      "commit": "cd5733ef455449f50aac8e276ea7dff1d638d04c",
      "tree": "973499fed74a08f1bc253adb871119e69fde11f3",
      "parents": [
        "55ca583dbeba9d0cde0f0024a62794e051b2dd8e"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Feb 28 15:35:04 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:17:41 2026 -0400"
      },
      "message": "review: fix review with gemini cli\n\nGemini does not take -- before prompt. the prompt we supply can not\nbe mistaken for a flag, so it is not needed by any agents.\n\nSigned-off-by: Michael S. Tsirkin \u003cmst@redhat.com\u003e\n"
    },
    {
      "commit": "55ca583dbeba9d0cde0f0024a62794e051b2dd8e",
      "tree": "13433daa0379af225a71eed6b43b512ff61e45a0",
      "parents": [
        "51e0059a7da5816ff021816b7c925991f5dc95b0"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Feb 28 10:37:33 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:17:06 2026 -0400"
      },
      "message": "tests: add test for -b trailer collection with same patch-id across versions\n\nTest that when v1 and v2 on lore have the same patch-id (identical\ndiff), trailers are correctly collected from both: v2\u0027s trailer via\nMessage-ID match, and v1\u0027s trailer via patch-id carry-over in\ntrailer_map.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "51e0059a7da5816ff021816b7c925991f5dc95b0",
      "tree": "0720f1caf5f8de0c3de72e37e6036f374d792918",
      "parents": [
        "c1faf0b76624e5e54941c3963ddfc18b9f70f248"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Thu Jan 08 16:46:54 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:14:09 2026 -0400"
      },
      "message": "ez/trailers: simplify -b mode and trust Message-ID for trailer scoping\n\nSimplify the -b code path:\n1. Replace manual code-review trailer search with single get_series()\n   call\n   using codereview_trailers\u003dTrue - consistent with non-b path\n2. Remove patch-id verification check - with -b, the Message-ID is the\n   authoritative link between local commit and list patch. Trailers are\n   already correctly scoped via in_reply_to matching, so patch-id check\n   is unnecessary and prevents applying trailers when patch was modified\n   locally after review.\n\nThis allows the workflow: post v1, get reviews, post v2, get more\nreviews,\nmodify locally, run \"b4 trailers -b\" - trailers from the version\nmatching\nthe Message-ID are applied even if local content differs.\n"
    },
    {
      "commit": "c1faf0b76624e5e54941c3963ddfc18b9f70f248",
      "tree": "df12d4fe244a3767a8dfa97b4ba3822677497634",
      "parents": [
        "617f03974cc39a541393c3247216b0be6a55f5a6"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:59:28 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:11:35 2026 -0400"
      },
      "message": "tests: add test for multi-series trailer collection\n\nThe multi-series local mbox scenario already works correctly via the\nexisting retrieve_messages() code path. However, as we are going to\ntouch these codepaths, extend the testing to get good coverage.\n\nThe test verifies that when a local mbox contains multiple series\nversions (v1 and v2) with the same patch content, follow-up trailers\nfrom v1 (like Reviewed-by) are correctly applied when updating\ntrailers for commits matching the patch-id.\n\nSigned-off-by: Michael S. Tsirkin \u003cmst@redhat.com\u003e\n"
    },
    {
      "commit": "617f03974cc39a541393c3247216b0be6a55f5a6",
      "tree": "7410d7883d018d8daf25c5a16835bf05691efd8c",
      "parents": [
        "97d4ca08f3a080aef061f6f5b23b4ee9d52b082b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Nov 19 14:16:38 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:09:26 2026 -0400"
      },
      "message": "tests: drop unused email.parser import\n\nReferences: d05bf29 b3e5b57\n"
    },
    {
      "commit": "97d4ca08f3a080aef061f6f5b23b4ee9d52b082b",
      "tree": "c583d740b1b874cf760c0f5a91671accef2dec1a",
      "parents": [
        "ce820dc0bfc70011123b0753baa52faff781004b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 10 17:49:10 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:08:37 2026 -0400"
      },
      "message": "Test use-reply-to-list with multiple comma-separated lists\n\nUpdated the test to verify that multiple mailing list addresses work\ncorrectly when comma-separated. The test now includes:\n- Two patches from different list addresses\n  (qemu-devel@nongnu.org and another-list@example.org)\n- Verification that both lists are properly handled\n- Confirmation that Reply-To headers are used for both when configured\n\nThis tests the actual use case: users configuring multiple lists like:\n  use-reply-to-list \u003d qemu-devel@nongnu.org, another-list@example.org\n"
    },
    {
      "commit": "ce820dc0bfc70011123b0753baa52faff781004b",
      "tree": "10bf24147138cd4c81ec1639887aa282c3e0e419",
      "parents": [
        "dba6de35d1c1b07842c4bfef8a0159e11f414963"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 10 17:41:49 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:07:39 2026 -0400"
      },
      "message": "Add test for use-reply-to-list config option\n\nAdds a test that verifies the use-reply-to-list configuration works\ncorrectly. When a mailing list rewrites the From header (e.g., due to\nDMARC policies), setting use-reply-to-list causes b4 to use the\nReply-To header for trailer validation instead.\n\nThe test verifies:\n- Without the config, the list address is used (qemu-devel@nongnu.org)\n- With the config, the Reply-To address is used (alice@example.com)\n"
    },
    {
      "commit": "dba6de35d1c1b07842c4bfef8a0159e11f414963",
      "tree": "dba7bb56496c08f9ac51d51925b30733dbde1958",
      "parents": [
        "663c0be1e13ab5661b429c7ecb376ce1c15c7efb"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 09:58:17 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:06:26 2026 -0400"
      },
      "message": "ez: honor use-reply-to-list in trailer updates\n\nReferences: 06c0df9 5db44a7\n"
    },
    {
      "commit": "663c0be1e13ab5661b429c7ecb376ce1c15c7efb",
      "tree": "0f8a0fa286d3f9521c8f4e9d750f209cb2c2d998",
      "parents": [
        "9918d8d4988403e6a2fffb0b47ee0893f7e48547"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 10 17:40:13 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:06:26 2026 -0400"
      },
      "message": "docs: move use-reply-to-list to end of am and shazam settings\n\nThe use-reply-to-list config option only affects b4 am and b4 shazam\ncommands, so it\u0027s documented under the \u0027am and shazam settings\u0027 section.\nMove it to the end of that section, right before attestation settings,\nfor better organization.\n"
    },
    {
      "commit": "9918d8d4988403e6a2fffb0b47ee0893f7e48547",
      "tree": "2aaf8a8e729825914e0e22afca0fecee213e1ea9",
      "parents": [
        "d16ec136f982fd768f6cf7f3063428cb8be23c88"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 10 17:37:46 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:05:47 2026 -0400"
      },
      "message": "Convert --use-reply-to-list from command-line flag to config option\n\nThe --use-reply-to-list flag is now a persistent configuration option\nrather than requiring the flag on every invocation. This makes it more\nconvenient for users who regularly work with mailing lists that rewrite\nFrom headers due to DMARC policies.\n\nChanges:\n- Removed -r/--use-reply-to-list command-line argument\n- Read from b4.use-reply-to-list config option instead\n- Added default config entry in DEFAULT_CONFIG\n- Updated documentation with usage examples\n\nUsers can now set this in their git config:\n  [b4]\n    use-reply-to-list \u003d qemu-devel@nongnu.org, list@example.org\n\nOr override via: b4 -c b4.use-reply-to-list\u003dlist@example.org am \u003cmsgid\u003e\n"
    },
    {
      "commit": "d16ec136f982fd768f6cf7f3063428cb8be23c88",
      "tree": "b5c878ae0c053a67416bb43e4b3bb03c4f0b2ef6",
      "parents": [
        "8cbbef9cfe8efc72b2126ebbf3891ec0fd461be1"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sun Oct 05 15:33:18 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:04:50 2026 -0400"
      },
      "message": "Add --use-reply-to-list flag for mailing lists that rewrite From headers\n\nSome mailing lists (like qemu-devel@nongnu.org) rewrite the From header\nto the list address due to DMARC policies, breaking trailer validation.\nThis commit adds a --use-reply-to-list flag that accepts a comma-separated\nlist of mailing list addresses. When the From header matches one of these\naddresses, b4 will use the Reply-To header for trailer validation instead.\n\nUsage: b4 am --use-reply-to-list qemu-devel@nongnu.org \u003cmsgid\u003e\n\n🤖 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "8cbbef9cfe8efc72b2126ebbf3891ec0fd461be1",
      "tree": "b02ce581520580805603a8860b864b7097014207",
      "parents": [
        "662386d8c6cb848e1c18afeebcefbb01e65e0145"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Feb 04 04:26:20 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:04:26 2026 -0400"
      },
      "message": "tests: add test for extra trailers deduplication with cover letter\n\nTest that extra trailers applied via -e are not duplicated when there\nis a cover letter. This covers the bug where add_cover_trailers()\nwould propagate trailers that were already added directly to patches.\n"
    },
    {
      "commit": "662386d8c6cb848e1c18afeebcefbb01e65e0145",
      "tree": "a64af768c9f25978cb0fcff171923cb18ef53cdd",
      "parents": [
        "fff464f7ec6f4adfcd421dbb78026aa8bf40c107"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Feb 04 04:26:12 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:03:14 2026 -0400"
      },
      "message": "mbox: deduplicate followup trailers to prevent duplicates\n\nWhen using -e/--extra-trailers with a series that has a cover letter,\ntrailers were being duplicated. The extra trailers were added to both\ncover and patches via fake followups, then add_cover_trailers()\npropagated cover trailers to patches again, resulting in duplicates.\n\nAdd deduplication checks when appending trailers to followup_trailers\nin three places:\n- When processing followups for patches (line 406)\n- When processing followups for cover letters (line 410)\n- In add_extra_trailers() used by add_cover_trailers() (line 666)\n"
    },
    {
      "commit": "fff464f7ec6f4adfcd421dbb78026aa8bf40c107",
      "tree": "37fa41347d54ea0e832e22a6bac44cc27f4a6b9b",
      "parents": [
        "ee6a15b455472d573b7690c77c17039c4cc8bbfc"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:40:04 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:03:14 2026 -0400"
      },
      "message": "tests: fix missing trailing whitespace in reference file\n\nThe reference file trailers-followup-single-ref-extratrailers.txt was\ncreated with missing trailing whitespace on diff context lines and the\nsignature separator line. Git patches preserve trailing whitespace on\ncontext lines and the signature separator should be \"-- \" (with space).\n\nFix the reference file to match the actual b4 output.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "ee6a15b455472d573b7690c77c17039c4cc8bbfc",
      "tree": "37edf7441a9264b07d1c30e64a8c8b7b1ae8956a",
      "parents": [
        "6290d9b8eba171cc4623a109ef7b995e8d30377f"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Thu Dec 11 03:51:08 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:03:14 2026 -0400"
      },
      "message": "tests: add tests for trailers -e flag\n\nAdd test case for the new -e/--extra-trailers flag in b4 trailers\ncommand.\n\nThe test verifies that:\n- Extra trailers from a file are correctly parsed\n- Trailers are applied to all commits in the series\n- The trailers are properly integrated with existing trailers\n\nTest files:\n- trailers-extra-test.txt: Sample extra trailers file\n- trailers-thread-with-followups-extratrailers.verify: Expected output\n\nThe test follows the existing parametrized test pattern and reuses\nthe trailers-thread-with-followups.mbox test data.\n"
    },
    {
      "commit": "6290d9b8eba171cc4623a109ef7b995e8d30377f",
      "tree": "bf1d252ccef5792cbda44a980c880b6064c9da80",
      "parents": [
        "32e738fdb4cdd853471d09bcefbc7ce1e7eb4250"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 08:11:26 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:02:16 2026 -0400"
      },
      "message": "fix: apply extra trailers to all patches, not just latest revision\n\nReplace the revision-based selection logic that only targeted the\nlatest revision\u0027s covers/patches with iteration over all messages in\nmsgid_map. This ensures extra trailers are applied to all covers\n(counter \u003d\u003d 0) and patches (has_diff) regardless of revision.\n\nFixes: 11ef727 (Implement --extra-trailers flag for am and shazam commands)\n"
    },
    {
      "commit": "32e738fdb4cdd853471d09bcefbc7ce1e7eb4250",
      "tree": "d7c27d37f41691a786dbf0412fbc9e545e682977",
      "parents": [
        "2a6ee896515c73ddd3789da92abd90661965dfaa"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 08:11:26 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:02:16 2026 -0400"
      },
      "message": "fix: move extra trailers processing before get_series() for deduplication\n\nMove the extra trailers processing to occur before get_series() so that\nextra trailers go through normal deduplication logic, matching the\nbehavior of other trailers.\n\nAlso refactor to use b4.mbox.apply_extra_trailers() helper function\ninstead of duplicating the logic inline.\n\nFixes: 96fd3f98 (trailers: add -e flag to apply extra trailers from file)\n"
    },
    {
      "commit": "2a6ee896515c73ddd3789da92abd90661965dfaa",
      "tree": "c8343b0594f837a253e3dab07942b517fef35f7d",
      "parents": [
        "ed5be1bbbe64f347bf11509635b638d6609f9049"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Thu Dec 11 03:50:50 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:01:59 2026 -0400"
      },
      "message": "trailers: add -e flag to apply extra trailers from file\n\nAdd support for the -e/--extra-trailers flag to the \u0027b4 trailers\u0027\ncommand, matching the functionality already present in \u0027b4 am\u0027 and\n\u0027b4 shazam\u0027.\n\nThis allows users to apply additional trailers from a file to all\npatches in a series, which is useful for automated workflows such\nas adding CI test results or automated review tags.\n\nUsage:\n  b4 trailers -u -e extra-trailers.txt\n\nThe file should contain trailers in standard Git format:\n  Tested-by: CI Bot \u003cci@example.org\u003e\n  Reviewed-by: Auto Reviewer \u003cauto@example.org\u003e\n\nThe implementation:\n- Reads and parses the extra trailers file\n- Applies trailers to all commits with diffs in the series\n- Handles file not found and IO errors gracefully\n- Integrates with existing trailer processing logic\n"
    },
    {
      "commit": "ed5be1bbbe64f347bf11509635b638d6609f9049",
      "tree": "b8527857acb5bbd7247d183e954b9e7c8dd257f5",
      "parents": [
        "4085bcd5dbe77e40e29eaffe47c3788494b71997"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Thu Dec 11 03:50:23 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:01:33 2026 -0400"
      },
      "message": "ez: fix UnboundLocalError when trailers have no source message\n\nWhen applying trailers that don\u0027t have an associated lore message\n(such as extra trailers from a file), the code was attempting to\nlog a source URL unconditionally, causing an UnboundLocalError.\n\nMove the source URL logging inside the \u0027if fltr.lmsg is not None\u0027\nblock so it only logs when there\u0027s actually a message to reference.\n\nFixes the error:\n  UnboundLocalError: cannot access local variable \u0027source\u0027 where\n  it is not associated with a value\n"
    },
    {
      "commit": "4085bcd5dbe77e40e29eaffe47c3788494b71997",
      "tree": "77019407fafa8396dc9e00f510228817aadcf11a",
      "parents": [
        "207f077a4a54f5e7d7318ce6d4a9673b9c06a4f0"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:50:11 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:01:11 2026 -0400"
      },
      "message": "tests: use apply_extra_trailers helper from mbox module\n\nUpdate tests to use the new apply_extra_trailers() helper function\nfrom the mbox module instead of duplicating the trailer application\nlogic.\n\nThis tests the helper function introduced in the previous commit.\n"
    },
    {
      "commit": "207f077a4a54f5e7d7318ce6d4a9673b9c06a4f0",
      "tree": "03ea69b369eecf7b91bc21371ccee35ceaf3ffce",
      "parents": [
        "c69ff872f94747dd284f046a4363c1559474934b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sun Oct 12 17:18:25 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 09:00:46 2026 -0400"
      },
      "message": "Add test for --extra-trailers with chained patch series\n\nAdd a test case that verifies --extra-trailers works correctly with\nchained patch series (patches without a cover letter where each patch\nreplies to the previous one).\n\nThe test creates a 3-patch series where:\n- Patch 1 has no In-Reply-To\n- Patch 2 replies to Patch 1\n- Patch 3 replies to Patch 2\n\nIt then applies extra trailers using the same logic as the fixed\nmbox.py code, creating synthetic follow-up messages for each patch.\nThe test verifies that all three patches receive the extra trailer\n(Tested-by: CI Bot \u003cci@example.com\u003e).\n\nThis test would have failed before the previous fix, as only the first\npatch would have received the trailer.\n\n🤖 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "c69ff872f94747dd284f046a4363c1559474934b",
      "tree": "7afd22141141ac0550ee83f73095dde4847cd8b0",
      "parents": [
        "faa3f6db5c6922ac87988f869818f2c5e7913a5b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Oct 04 10:29:35 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:59:44 2026 -0400"
      },
      "message": "Add tests for --extra-trailers flag\n\nAdd a test that verifies the --extra-trailers functionality works\ncorrectly by creating a synthetic follow-up message and checking\nthat the trailers are applied to patches.\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "faa3f6db5c6922ac87988f869818f2c5e7913a5b",
      "tree": "c486e7f1c82ea3f1b44762d6a0d508e300993ecf",
      "parents": [
        "ac327852e64a6cace7609eacaa7a5f401d54a630"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:50:03 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:58:23 2026 -0400"
      },
      "message": "mbox: extract apply_extra_trailers helper function\n\nRefactor the extra trailers application logic into a reusable helper\nfunction. This extracts the code from make_am() into a standalone\napply_extra_trailers() function that can be used by both the mbox\nmodule and tests.\n\nNo functional change.\n"
    },
    {
      "commit": "ac327852e64a6cace7609eacaa7a5f401d54a630",
      "tree": "e8c3f72476a79a8e904b6380a0d7d561bc59a076",
      "parents": [
        "c70620166ce98f8aff1fe9ba525a9fdb2b2f7839"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Oct 04 10:03:46 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:58:23 2026 -0400"
      },
      "message": "docs: add documentation for --extra-trailers flag\n\nDocument the new -e/--extra-trailers flag for the am and shazam\ncommands. This flag allows specifying a file containing trailers\nthat will be applied to all patches in a series.\n\n🤖 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "c70620166ce98f8aff1fe9ba525a9fdb2b2f7839",
      "tree": "fa76d7bb04ad8e45a66a92b7405de1dd4923fb86",
      "parents": [
        "7482b86757dd11b147fcc3914954a126dd1184ef"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Oct 04 10:15:10 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:58:23 2026 -0400"
      },
      "message": "Implement --extra-trailers flag for am and shazam commands\n\nAdd support for the -e/--extra-trailers flag that allows specifying\na file containing trailers to be applied to all patches in a series.\n\nThe implementation creates a synthetic follow-up message containing\nthe trailers from the file and adds it to the mailbox\u0027s followups list.\nThe message is linked to the cover letter (or first patch if no cover)\nvia In-Reply-To, ensuring the trailers are applied to all patches.\n\nThe synthetic message uses skip_trailer_validation\u003dTrue to bypass\nemail address validation, since the trailers may come from various\nsources and don\u0027t need to match a specific sender.\n\nmbox: fix --extra-trailers for single patches without cover letter\n\nWhen using --extra-trailers with a single patch that has no cover letter\nand no follow-ups, b4 would fail with \"CRITICAL: cannot apply extra\ntrailers, no patches found\". This happened because the code was checking\npatches[0] for the cover letter msgid, but for a series without a cover\nletter, patches[0] is None and the actual patch is at patches[1].\n\nThe fix iterates through all patches to find the first non-None entry,\nwhich works correctly for both cases:\n- Series with cover letter: finds patches[0] (the cover)\n- Series without cover letter: finds patches[1] (the first patch)\n\nThis allows users to apply extra trailers from a file to standalone\npatches, which is useful for adding automated review tags or CI test\nresults to single-patch submissions.\n\nmbox: fix --extra-trailers for chained patch series\n\nWhen using --extra-trailers with a patch series that lacks a cover\nletter and has patches chained together (patch 2 replies to patch 1,\npatch 3 replies to patch 2, etc.), only the first patch would receive\nthe extra trailers. This happened because the code created a single\nsynthetic follow-up message that replied only to the first patch.\n\nThe trailer-processing logic follows the In-Reply-To chain upward and\nstops when it finds a patch with has_diff, adding trailers only to that\nspecific patch. For cover letters, this works because cover letter\ntrailers are propagated to all patches via add_cover_trailers(). For\nchained patches without a cover letter, there\u0027s no such propagation.\n\nThe fix creates a synthetic follow-up message for each patch in the\nseries (or just the cover letter if present). Each synthetic message\nhas a unique Message-ID and replies to a specific patch via In-Reply-To.\nThis ensures all patches receive the extra trailers regardless of\nthreading structure.\n\nFor a 3-patch series without a cover letter:\n- Before: 1 synthetic message → only patch 1 gets trailers\n- After: 3 synthetic messages → all patches get trailers\n\nFor a series with a cover letter:\n- Behavior unchanged: 1 synthetic message → cover propagates to all\n\n🤖 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "7482b86757dd11b147fcc3914954a126dd1184ef",
      "tree": "98ddb37d670048247a2ddbfeb5f1cde7bfade528",
      "parents": [
        "ff38db17fa1028a381fb54eea3e8951b83b3dda4"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sat Oct 04 10:12:05 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:57:57 2026 -0400"
      },
      "message": "Add skip_trailer_validation flag to LoreMessage\n\nAdd a skip_trailer_validation attribute to LoreMessage that bypasses\nemail validation in get_trailers(). This will be used for synthetic\nfollow-up messages containing extra trailers specified via the\n--extra-trailers flag.\n\n🤖 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "ff38db17fa1028a381fb54eea3e8951b83b3dda4",
      "tree": "9d13b8971b9b5ef2327ab39e1fbc26ccb67d8c4d",
      "parents": [
        "173f27839af730e6a1c2b961459870f9f510ad8f"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Fri Jan 09 06:43:23 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:57:57 2026 -0400"
      },
      "message": "send: strip Message-ID trailers by default\n\nWhen sending patches with b4 send, Message-ID trailers from previous\nversions are automatically stripped since they point to old versions\nand would be misleading in the new submission.\n\nUse --no-remove-message-id to keep them if needed.\n"
    },
    {
      "commit": "173f27839af730e6a1c2b961459870f9f510ad8f",
      "tree": "9bbb6f8390cbda33eb701f02dc98dd0b3d43ecc5",
      "parents": [
        "9ed9ce6e2fd1b42303c0a0761eb98238d3d687bb"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:30:01 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:57:21 2026 -0400"
      },
      "message": "tests: add test for multi-series Message-ID iteration fix\n\nThis test verifies commit 978b9ea which changed the Message-ID mapping\nlogic to iterate through ALL series in temp_bbox.series.values() instead\nof just the single series returned by get_series().\n\nThe bug: get_series() returns only ONE series based on submission_date\n(preferring newer). When patches come from an older series version while\na newer unrelated series exists in the mbox, get_series() returns the\nwrong series and no patch-ids match.\n\nThe fix: by iterating through all series in temp_bbox.series.values(),\nwe ensure patches from any series version get their Message-IDs mapped\ncorrectly, regardless of submission order.\n\nTest setup:\n- mbox contains v2 series (matching patches, older) and standalone v1\n  patch (non-matching, newer submission date)\n- bundle contains commits that match v2\u0027s patch-ids\n- Without fix: get_series() returns v1 (newer), no matches, no Message-IDs\n- With fix: all series checked, v2\u0027s Message-IDs are correctly applied\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "9ed9ce6e2fd1b42303c0a0761eb98238d3d687bb",
      "tree": "c7f5cf832d4863260dd6b5fc3919e02347792bd0",
      "parents": [
        "37f30652ee12dbc05e9582a06d4c84dc89a30f5a"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:29:16 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:56:18 2026 -0400"
      },
      "message": "tests: add test for correct Message-ID source in patchid_map fix\n\nThis test verifies commit 2ef482ea which removed the patchid_map loop\nfor extracting Message-IDs.\n\nThe bug: patchid_map contains follow-up reply messages (reviews, acks,\netc.) which have different Message-IDs than the original patch messages.\nWhen using patchid_map to extract Message-IDs, the wrong Message-ID\n(from the follow-up reply) would be added instead of the original\npatch\u0027s Message-ID.\n\nThe fix: by removing patchid_map from Message-ID extraction and using\nonly list_msgs, we ensure the Message-ID always comes from the original\npatch message, not from follow-up replies.\n\nTest setup:\n- 2 patches, each with a follow-up reply that has a DIFFERENT Message-ID\n- Original patches: \u003c20221025-correctmsgid-v1-N-abc123@example.org\u003e\n- Follow-up replies: \u003creviewer-reply-N@example.org\u003e\n- Without fix: Message-ID from reviewer-reply-N would be used\n- With fix: Message-ID from original patch is correctly used\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "37f30652ee12dbc05e9582a06d4c84dc89a30f5a",
      "tree": "ce28ff22b2511fe06aba0cea1665d21f8341f5a5",
      "parents": [
        "cc16834563e1b62abfdd561c00334ec0ec8fc25b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:28:33 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:55:24 2026 -0400"
      },
      "message": "tests: add test for elif-\u003eif fix in Message-ID mapping\n\nThis test verifies commit 4899c465 which changed `elif list_msgs:` to\n`if list_msgs:` in the trailer update logic.\n\nThe bug: when patchid_map exists but only contains a subset of patches\n(e.g., only patches with follow-up replies), the elif condition would\nskip processing list_msgs entirely, so patches without follow-ups would\nnot get Message-ID trailers added.\n\nThe fix: by changing elif to if, list_msgs is always processed when\navailable, ensuring all patches get Message-ID trailers regardless of\nwhether they have follow-up replies.\n\nTest setup:\n- 2 patches in an mbox, only patch 1 has a follow-up reply\n- Without fix: only patch 1 gets Message-ID (via patchid_map)\n- With fix: both patches get Message-ID (via list_msgs)\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "cc16834563e1b62abfdd561c00334ec0ec8fc25b",
      "tree": "32385be7ee4e56d82e1fbc2de74a4db00ea53e53",
      "parents": [
        "200ee6f37089ca5b30504892b0a10a96956dc1d4"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 18:24:26 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:54:28 2026 -0400"
      },
      "message": "fix: iterate through all series when mapping Message-IDs with -i flag\n\nWhen using `b4 trailers -u -i`, the code needs to map local commits to\ntheir corresponding Message-IDs from lore. Previously, get_series()\nwas used which only returns ONE series (the \"latest\" by submission_date).\n\nThis caused issues when commits came from multiple email threads with\ndifferent series versions - only patches from the single returned series\nwould get Message-ID trailers.\n\nFix by iterating through temp_bbox.series.values() to check ALL series\nfor matching patch-ids. This ensures commits from any series version\nget their correct Message-ID trailers.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "200ee6f37089ca5b30504892b0a10a96956dc1d4",
      "tree": "944bc29154bb2a0df0567a8637d0dc76fb696fdd",
      "parents": [
        "b8686a2b09da55eba700aba80a30a27a3d34aaf6"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 17:15:23 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:54:28 2026 -0400"
      },
      "message": "fix: use original patch Message-IDs instead of follow-up reply IDs\n\nThe patchid_map contains follow-up replies (e.g., reviewer\u0027s Reviewed-by\nmessages), not the original patches. Using llmsgs[0].msg to extract\nMessage-IDs would incorrectly use the reviewer\u0027s Message-ID instead of\nthe patch author\u0027s Message-ID.\n\nRemove the patchid_map loop for Message-ID extraction and only use\nlist_msgs which contains the original patch messages with correct\nMessage-IDs. The patchid_map is still used for trailer collection\n(earlier in the code), just not for Message-ID extraction.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "b8686a2b09da55eba700aba80a30a27a3d34aaf6",
      "tree": "6233255baf3dc49279aafb394736c0f7291f40eb",
      "parents": [
        "bd7888489c1cdc10de5656b5a211fb72f770784e"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Jan 05 17:07:06 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:54:28 2026 -0400"
      },
      "message": "fix: process list_msgs for commits not mapped by patchid_map\n\nChange \u0027elif list_msgs:\u0027 to \u0027if list_msgs:\u0027 so that commits without\nfollow-up replies in patchid_map can still get Message-ID trailers\nfrom the original lore messages in list_msgs.\n\nThe elif caused list_msgs processing to be skipped entirely when\npatchid_map existed, even if patchid_map only contained a subset\nof commits.\n\nFixes: 0429cc1a (\"fix: remove list_msgs requirement from Message-ID mapping condition\")\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "bd7888489c1cdc10de5656b5a211fb72f770784e",
      "tree": "e576b6bef0b3f68b32512f6ec96abe04f44da456",
      "parents": [
        "86eacb4652f84c96f3fb41d4ecea88a1e826c290"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 04:06:47 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:54:28 2026 -0400"
      },
      "message": "test: fix verify file to include Signed-off-by trailers\n\nThe test applies patches with --signoff flag, which adds a Signed-off-by\ntrailer from the test user. The verify file needs to include this trailer\nto match the actual output.\n\nFixes: 8304180 (\"test: add test for --since-commit without -b flag fetching by patch-id\")\n"
    },
    {
      "commit": "86eacb4652f84c96f3fb41d4ecea88a1e826c290",
      "tree": "ca0c5d516ecf244d45879248185e49da2b8764c2",
      "parents": [
        "365d8b8070e6c5d876fb0a16f66912f7627eea31"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 04:01:40 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:54:28 2026 -0400"
      },
      "message": "test: add test for Message-ID mapping condition fix\n\nThis test verifies that Message-ID mapping works even when list_msgs\nstarts empty but gets populated by fetching messages by patch-id. It\ntests that the condition fix (removing \u0027and list_msgs\u0027 requirement)\nallows Message-ID trailers to be added when using --since-commit with\n-i flag without -b flag.\n\nFixes: 22f003b (\"fix: remove list_msgs requirement from Message-ID mapping condition\")\n"
    },
    {
      "commit": "365d8b8070e6c5d876fb0a16f66912f7627eea31",
      "tree": "0d33655d9f61f5ccf2d4eebc50a00f341b3e5998",
      "parents": [
        "ca312c30f20b323723becdb245f95921a43675ca"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 04:01:32 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:53:40 2026 -0400"
      },
      "message": "test: add test for --since-commit without -b flag fetching by patch-id\n\nThis test verifies that when using --since-commit with -i flag without\n-b flag, the tool correctly fetches lore messages by patch-id and adds\nMessage-ID trailers to commits.\n\nFixes: 47a96b4 (\"fix: fetch lore messages by patch-id when list_msgs is empty\")\n"
    },
    {
      "commit": "ca312c30f20b323723becdb245f95921a43675ca",
      "tree": "7f7c6285fb42b4f27373a584b0f1fd4b9b3566f0",
      "parents": [
        "992a711bc2b409fb506461ef68df162676931c84"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:14:42 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:54 2026 -0400"
      },
      "message": "test: update expected output for -i flag Message-ID trailer fix\n\nUpdate the verify file to match the new behavior from the previous\ncommit. The fix changed:\n- Email address in output (now uses original author, not override)\n- Message-ID trailer position (now appears after Signed-off-by)\n"
    },
    {
      "commit": "992a711bc2b409fb506461ef68df162676931c84",
      "tree": "0b2e8a14d62206fbd1939a04c721d6a1dfbf7754",
      "parents": [
        "1f03f2aa02abb604a06678064d85312cc260ace0"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sun Nov 16 03:48:58 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:54 2026 -0400"
      },
      "message": "test: add test case for -i flag with commits missing Message-ID trailers\n\nAdd a test case that demonstrates the defect where b4 trailers -i\nbails out with \u0027No trailer updates found\u0027 when commits don\u0027t have\nMessage-ID trailers but lore messages do. The -i flag should add\nMessage-ID trailers from lore, but currently only processes\nfollow-up trailers.\n"
    },
    {
      "commit": "1f03f2aa02abb604a06678064d85312cc260ace0",
      "tree": "93f91e038bd953835814f437d610947a704e33b8",
      "parents": [
        "61db096aa58a4e28c977a8918f3fcc3c4b262de8"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Nov 19 14:14:16 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "ez: reuse fetched patch-ids when replacing Message-ID trailers\n\nReferences: daec41d\n"
    },
    {
      "commit": "61db096aa58a4e28c977a8918f3fcc3c4b262de8",
      "tree": "72baf6998b0de544662649eb5f7c772df8417028",
      "parents": [
        "bc0016eb0fc1f6d16198652a1a307848335356ec"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Jan 06 09:00:23 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "ez: reuse parsed lore messages instead of rebuilding\n\nUse the ensure_list_bbox helper to avoid rebuilding the LoreMailbox\nmultiple times from list_msgs.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "bc0016eb0fc1f6d16198652a1a307848335356ec",
      "tree": "5455474a60cba3ebb47dd85a2503c4204a905efb",
      "parents": [
        "8e5069a3d1b0c8fd733376bb3067e15d667ead68"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Nov 19 13:57:42 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "ez: batch git log for -b scans\n\nReferences: 0a9cc33 8d6123c 922b767 3f209fa\n"
    },
    {
      "commit": "8e5069a3d1b0c8fd733376bb3067e15d667ead68",
      "tree": "ea651a61d0192196bfb9577f08e91a7eb040423e",
      "parents": [
        "151d2a0899244747c34c38d9e1c3cfb2d6d63972"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Nov 19 13:55:25 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "ez: share Message-ID extraction helper\n\nReferences: 0a9cc33 8d6123c 922b767 3f209fa\n"
    },
    {
      "commit": "151d2a0899244747c34c38d9e1c3cfb2d6d63972",
      "tree": "0ca27fa8192d36be0456c96b8f0d76a67e725c45",
      "parents": [
        "5a523354a2b43573c15409e3913f74e2108cb6d5"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 03:42:41 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "fix: remove list_msgs requirement from Message-ID mapping condition\n\nThe condition \u0027if ltrmask and isinstance(ltrmask, str) and list_msgs:\u0027\nprevented Message-ID trailers from being added when list_msgs was empty.\nThis fix removes the \u0027and list_msgs\u0027 requirement and changes \u0027else:\u0027 to\n\u0027elif list_msgs:\u0027 to only parse list_msgs when it\u0027s actually populated.\n\nFixes: 63468ab (\"fix: add Message-ID trailers when -i flag is used even without follow-ups\")\n"
    },
    {
      "commit": "5a523354a2b43573c15409e3913f74e2108cb6d5",
      "tree": "fcea44e87684c706bd96067742c354f01e06c48e",
      "parents": [
        "8f3a32779630bc291161d36cbd554a02bf92db4d"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 03:42:39 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "fix: fetch lore messages by patch-id when list_msgs is empty\n\nWhen using --since-commit without -b flag, list_msgs remains empty and\nMessage-ID trailers cannot be added. This fix proactively fetches lore\nmessages by searching for patch-ids when list_msgs is empty, allowing\nthe existing code to process them and add Message-ID trailers.\n\nFixes: 63468ab (\"fix: add Message-ID trailers when -i flag is used even without follow-ups\")\n"
    },
    {
      "commit": "8f3a32779630bc291161d36cbd554a02bf92db4d",
      "tree": "3bf11664cecbf040b1d424513ac515872e9bcfdd",
      "parents": [
        "a10bd6530ac1d508d023fe5be972a9fa94c8c563"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 17 03:42:47 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "fix: remove trailing whitespace in ez.py\n\nFixes: 63468ab (\"fix: add Message-ID trailers when -i flag is used even without follow-ups\")\n"
    },
    {
      "commit": "a10bd6530ac1d508d023fe5be972a9fa94c8c563",
      "tree": "3fc1e161634162bcf9bcd4537b766f13f9c0d734",
      "parents": [
        "55ecd52f02b31dd28f367ba9a899e4d1cd89d203"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:14:04 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "fix: add Message-ID trailers when -i flag is used even without follow-ups\n\nWhen the -i/--add-message-id flag is used with b4 trailers, it should\nadd Message-ID trailers from lore messages even when there are no\nfollow-up trailers. Previously, the code would skip commits without\nfollow-up trailers and exit with \u0027No trailer updates found\u0027.\n\nThe fix:\n1. Creates a mapping from commit hash to original lore message\u0027s\n   Message-ID when linktrailermask is set\n2. Checks if Message-ID trailers need to be added for each commit\n3. Processes commits even when they don\u0027t have follow-up trailers if\n   Message-ID trailers need to be added\n\nThis matches the behavior of b4 am -i, which adds Message-ID trailers\nfrom the original lore messages.\n"
    },
    {
      "commit": "55ecd52f02b31dd28f367ba9a899e4d1cd89d203",
      "tree": "c2930813684c32659620e5a62423249cb88f17e0",
      "parents": [
        "6cb5d98c637b488beef85f099f5f4d0214a9a01b"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sun Nov 02 15:54:53 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:52:21 2026 -0400"
      },
      "message": "trailers: make -i and -b mutually exclusive and document -b\n\nThe -i (add-message-id) and -b (by-message-id) flags serve different\npurposes and don\u0027t make sense to use together:\n\n- -i adds Message-ID trailers from lore to commits\n- -b extracts existing Message-ID trailers from commits to fetch from lore\n\nMake them mutually exclusive and add documentation for the existing\n-b flag that was previously undocumented.\n"
    },
    {
      "commit": "6cb5d98c637b488beef85f099f5f4d0214a9a01b",
      "tree": "2b7b028b712e80c0241f8a31f064ff6567b14dcc",
      "parents": [
        "693bd430af157ecefd2301cb4cc12c3e85c2d2cb"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Sun Nov 02 15:54:06 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:51:56 2026 -0400"
      },
      "message": "trailers: add -i/--add-message-id flag\n\nAdd the -i/--add-message-id flag to b4 trailers, working like it does\nfor b4 am - gets the message id from lore and sets it as a trailer.\n\nThe implementation follows the same minimal pattern as b4 am by simply\nsetting the linktrailermask configuration when the flag is used.\n"
    },
    {
      "commit": "693bd430af157ecefd2301cb4cc12c3e85c2d2cb",
      "tree": "4ff14386b8b9d6494a15b65875f3fce33aa3c251",
      "parents": [
        "e2a34c96b913c613169261cd05c4891fbb1c5dee"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Jan 06 08:53:51 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:51:28 2026 -0400"
      },
      "message": "tests: add test for -b flag with commits from multiple series\n\nTest scenario:\n- Commit 1 has Message-ID from v1 series\n- Commit 2 has Message-ID from v3 series\n- Mbox contains both patches with follow-up trailers\n\nWithout fix: get_series() returns v3 only, commit 1 gets no trailers\nWith fix: both commits matched by Message-ID, both get their trailers\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "e2a34c96b913c613169261cd05c4891fbb1c5dee",
      "tree": "903cb04f42499d5f794f0c4701de4d67e132a7b9",
      "parents": [
        "8c88e027712d39f8b9e3e47aafe9ff42f42bac41"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Jan 06 08:53:36 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:50:59 2026 -0400"
      },
      "message": "ez: fix -b flag to match Message-IDs across all series\n\nThe -b/--by-message-id flag was incorrectly calling get_series() which\nreturns only one series (the latest by submission date). If commits have\nMessage-IDs from different series (e.g., v1 and v3), only one series\nwould be checked, leaving some commits without trailers.\n\nFix by:\n1. Processing ALL series via get_series() for each revision to ensure\n   follow-up trailers are attached to all patches\n2. Looking up by Message-ID directly in msgid_map instead of iterating\n   one series\u0027 patches\n\nThis is conceptually correct since -b is a per-Message-ID operation -\nthe series structure is irrelevant.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "8c88e027712d39f8b9e3e47aafe9ff42f42bac41",
      "tree": "6c8cc6f501d9ee49996cd4f6b40f6cccf971725c",
      "parents": [
        "9d3b551a8f9e87fb6eb9aaa93b30c4742c8a78a8"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Jan 06 09:00:15 2026 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:50:30 2026 -0400"
      },
      "message": "ez: add ensure_list_bbox helper function\n\nAdd a helper function to lazily create a LoreMailbox from list_msgs.\nThis will be used to avoid rebuilding the mailbox multiple times.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "9d3b551a8f9e87fb6eb9aaa93b30c4742c8a78a8",
      "tree": "6dbc43f5818e85fb5a28f7b61bb8eada3fd37a9a",
      "parents": [
        "048d33cd86d61b853b8045a53d455d98f8d84fc6"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Nov 11 03:46:43 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:50:30 2026 -0400"
      },
      "message": "Enable all -b/--by-message-id tests using git bundles\n\nCreated git bundles with commits that have Message-ID and Link trailers:\n- trailers-by-msgid.bundle: Contains 2 commits with Message-ID trailers\n- trailers-by-link-format.bundle: Contains 2 commits with Link trailers\n\nThese bundles were created by:\n1. Cloning the base gitdir.bundle\n2. Applying patches with shazam --add-message-id (or --add-link)\n3. Creating bundles from the resulting commits\n\nThe test code now uses these bundles consistently with the existing\ntrailers-with-tripledash.bundle pattern. All 3 -b tests pass.\n"
    },
    {
      "commit": "048d33cd86d61b853b8045a53d455d98f8d84fc6",
      "tree": "f738e80e3e6d7793042c431ba47e11e40b94f5a9",
      "parents": [
        "20169e9ed3ffbd3d6d19269df095c1957ed3df53"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Nov 11 03:46:27 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:49:57 2026 -0400"
      },
      "message": "Remove invalid --no-add-trailers flag from tripledash test\n\nThis commit fixes a test breakage introduced by commit 075aefc\n(\"Add test for -b/--by-message-id flag and improve its implementation\").\nThat commit accidentally added an 8th parameter to the tripledash test\ncase when only 7 parameters were declared in @pytest.mark.parametrize,\ncausing a parametrization error that broke the test suite.\n\nThe --no-add-trailers flag does not exist for the \u0027b4 trailers\u0027 command\n(only for \u0027b4 am/shazam\u0027). This test uses a bundle so it does not call\nshazam, making the flag both invalid and unnecessary.\n"
    },
    {
      "commit": "20169e9ed3ffbd3d6d19269df095c1957ed3df53",
      "tree": "3fd9839b569b374a8db7e1879ce658c7f066f703",
      "parents": [
        "39955154254c9544da3ddee8347c7a28904275a9"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Mon Nov 10 17:43:37 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:49:23 2026 -0400"
      },
      "message": "Fix test_ez.py parametrize error\n\nFixed parametrize decorator having wrong number of values (8 values\nfor 7 parameters). Moved the extra flags into the trargs parameter\nwhere they belong.\n\nNote: Some tests still fail due to missing sample files\n(trailers-by-link-format.mbox, etc.) from recent commits - this is\na pre-existing issue.\n"
    },
    {
      "commit": "39955154254c9544da3ddee8347c7a28904275a9",
      "tree": "e7ddbb273fdea8653c2fcd20ebe32765c16f9ab2",
      "parents": [
        "267577fa43b8432da0078fa6e9e1a5d0f0cba71e"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Fri Oct 17 07:20:42 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:48:47 2026 -0400"
      },
      "message": "Add test for Link trailer matching with -b flag\n\nAdds test case to validate that commits with Link trailers containing\npatch.msgid.link URLs can be matched against Message-ID headers in emails\nwhen using the -b/--by-message-id flag.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "267577fa43b8432da0078fa6e9e1a5d0f0cba71e",
      "tree": "3ea0f9f6d4574649b513e02888191b7c3097b096",
      "parents": [
        "ddf4b422374883ad4b6b33f8165086222804eb97"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:20:40 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:48:16 2026 -0400"
      },
      "message": "test: add test for msgid filtering with -b flag\n\nAdd test case to verify that filtering works correctly when using -b\nwith a msgid filter - only commits with that specific Message-ID get\ntheir trailers updated.\n\nThis tests the filtering functionality added in the previous commit.\n"
    },
    {
      "commit": "ddf4b422374883ad4b6b33f8165086222804eb97",
      "tree": "5a7c50419a65f6fa8a17ede50b6c7cbe8e5017dc",
      "parents": [
        "b17746d1d693e9b2fc9475ae1d73715198fa3a63"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:20:11 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:47:21 2026 -0400"
      },
      "message": "test: add test for -b/--by-message-id flag\n\nAdd test case for -b flag in test_ez.py to validate that the -b flag\ncorrectly extracts Message-ID trailers from commits, fetches those\nthreads, and applies follow-up trailers to the commits.\n\nThis tests the functionality added in the previous commit.\n"
    },
    {
      "commit": "b17746d1d693e9b2fc9475ae1d73715198fa3a63",
      "tree": "4d2e5fd7717508b737f912672d443e7663ee1150",
      "parents": [
        "9fee2500385b0e75856fb863f109a7cbd60fe136"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 13:35:44 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:46:12 2026 -0400"
      },
      "message": "ez: verify patch-id before applying trailers in -b mode\n\nWhen using -b/--by-message-id, trailers from follow-up messages were\napplied based solely on Message-ID matching. This could incorrectly\napply trailers (like Reviewed-by, Acked-by) from an older version of\na patch to a newer version that has different content.\n\nNow we verify that the patch-id from lore matches the commit\u0027s patch-id\nbefore applying trailers. If they don\u0027t match, a warning is shown and\ntrailers are skipped, since an ack on an older patch version should not\nautomatically apply to a modified version.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "9fee2500385b0e75856fb863f109a7cbd60fe136",
      "tree": "1539659f6faf16afd771c73a6d9a271f552e5399",
      "parents": [
        "3c1c47de614ae80c1087c2cb6d277267a37aed89"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Nov 11 03:43:55 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:46:12 2026 -0400"
      },
      "message": "Fix shazam-am-flags assertion when b4cfg is None\n\nOnly check shazam-am-flags config when b4cfg is actually set.\nPreviously this assertion failed for tests that pass None for b4cfg.\n"
    },
    {
      "commit": "3c1c47de614ae80c1087c2cb6d277267a37aed89",
      "tree": "0be964d95f1d7d5a7b12a3a4bf1c813f9da77a11",
      "parents": [
        "96cd0ac5028710cce6c635dc4a25af0d69efd3d7"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Fri Oct 17 06:55:41 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:46:12 2026 -0400"
      },
      "message": "Support matching Message-ID against Link trailers with patch.msgid.link\n\nExtend Message-ID matching logic to support Link trailers containing\npatch.msgid.link URLs. This allows commits with trailers like:\n  Link: https://patch.msgid.link/msgid@example.com\nto match emails with headers:\n  Message-ID: \u003cmsgid@example.com\u003e\n\nChanges:\n- Add Link trailer parsing in commit-to-email mapping\n- Add Link trailer parsing in -b flag filtering logic\n- Use efficient single regex with walrus operator and case-insensitive matching\n- Handle flexible whitespace after colon\n- Preserve existing Message-ID trailer functionality\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \u003cnoreply@anthropic.com\u003e\n"
    },
    {
      "commit": "96cd0ac5028710cce6c635dc4a25af0d69efd3d7",
      "tree": "0b77a9b7aaa54f066fff3f321cf4171e6ba42a1d",
      "parents": [
        "a5089c8e7e57aeaafa5f656824241908f7e6bf08"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:20:33 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:46:12 2026 -0400"
      },
      "message": "ez: support filtering by msgid with -b/--by-message-id flag\n\nWhen -b is used with a msgid argument (or -F), only process commits\nthat have that specific Message-ID in their trailers. This allows users\nto selectively fetch and apply trailers for a specific patch in a series.\n\nExample usage:\n  b4 trailers -u -b second-patch-msgid@example.com\n\nChanges:\n- Extract and filter Message-IDs based on provided msgid argument\n- Update help text to document the filtering behavior\n"
    },
    {
      "commit": "a5089c8e7e57aeaafa5f656824241908f7e6bf08",
      "tree": "3b83b4bb846ccc2667d55b6db5039f55d63f82a9",
      "parents": [
        "5dd22ac24f9d702db156d59d2958290700cee074"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed Dec 24 11:20:03 2025 -0500"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:45:40 2026 -0400"
      },
      "message": "ez: improve -b/--by-message-id implementation for local mbox testing\n\nFix -b implementation to support local mbox for testing.\nImprove matching logic to properly parse fetched messages into\nLoreSeries and match follow-up trailers by Message-ID.\n"
    },
    {
      "commit": "5dd22ac24f9d702db156d59d2958290700cee074",
      "tree": "e686b8de1fe4cd961a8ec3ad2bf2f0c574f217d1",
      "parents": [
        "d5d981426ead3f490713ef5d2fd1aa3d0f13b005"
      ],
      "author": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Tue Oct 14 17:26:41 2025 -0400"
      },
      "committer": {
        "name": "Michael S. Tsirkin",
        "email": "mst@redhat.com",
        "time": "Wed May 13 08:45:08 2026 -0400"
      },
      "message": "Add -b/--by-message-id flag to b4 trailers\n\nThis flag extracts Message-ID trailers from commit logs and fetches\nthose threads from lore for applying trailers. It ignores the commit\nauthor completely and works with any commits in the range.\n\nThe commit range is determined by the upstream branch if configured,\nor falls back to --since-commit or --since parameter.\n\nMessage-ID matching is case-insensitive.\n"
    },
    {
      "commit": "d5d981426ead3f490713ef5d2fd1aa3d0f13b005",
      "tree": "8b1e80357d0c9f3bb6da03ac6b890ee65770512d",
      "parents": [
        "e252429b1dc717a23807116db3dfa82f6a8fc8ff"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 13:46:15 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 13:48:22 2026 +0000"
      },
      "message": "Add ci-matrix.sh for running tests across the supported Python range\n\nci.sh exercises only whichever interpreter uv resolves for the local\nenvironment — typically the newest Python installed on the dev\nmachine, which varies from one contributor to the next. Runtime bugs\nspecific to other versions in the supported range can slip through\nsilently. The import crash fixed in the parent commit is exactly\nthis class: a module-level subscripted annotation that raises\nTypeError on 3.11/3.12/3.13 at import but is invisible on 3.14\nthanks to PEP 649 deferred annotation evaluation.\n\nci-matrix.sh iterates over every interpreter version in the declared\n`requires-python` range (currently 3.11 through 3.14) and runs an\nimport smoke check plus the full pytest suite against each. ruff,\nmypy, pyright, and ty analyse code against a configured target rather\nthan executing it, so running them per interpreter would not add\ncoverage; ci.sh remains the canonical home for those checks.\n\nRunning the full pipeline per version is slow enough that it does not\nbelong in the default workflow, hence a sibling script rather than a\nflag on ci.sh. Use it before releases and after changes that could\ndepend on version-specific runtime behaviour (import-time annotations,\nstdlib APIs, syntax features).\n\nKey points:\n- Each version gets its own .venv-X.Y via UV_PROJECT_ENVIRONMENT, so\n  successive runs are incremental and the default .venv is untouched.\n- `uv python install $PYTHONS` is called up front so the script works\n  even when UV_PYTHON_DOWNLOADS\u003dmanual is set globally.\n- Failures are collected and reported at the end rather than bailing\n  on the first broken interpreter, so a full matrix picture is shown\n  in one run.\n- Override the version list with PYTHONS\u003d\"3.11 3.14\" ./ci-matrix.sh.\n\nAdd `.venv-*` to .gitignore so per-version environments do not show\nup in `git status`.\n\nAssisted-by: claude-opus-4-7\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "e252429b1dc717a23807116db3dfa82f6a8fc8ff",
      "tree": "5cdd975a0f0292308ab7036aceea8c4c9b2cc3f0",
      "parents": [
        "c3fa56d9ef3eef9fdccc909d6304d35775411f95"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 13:20:27 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 13:20:27 2026 +0000"
      },
      "message": "Fix b4 import crash on Python 3.11/3.12/3.13\n\nCommit 9186f8a (\"Enable pyright strict mode\") annotated the\nmodule-level emlpolicy variable as\n`email.policy.EmailPolicy[EmailMessage]`. EmailPolicy is declared\ngeneric in typeshed so pyright accepts the subscript, but the\nruntime class has no `__class_getitem__` on any current CPython\nrelease. On Python 3.11/3.12/3.13 module-level annotations are\nevaluated eagerly at import, so any `import b4` immediately raises\n`TypeError: type \u0027EmailPolicy\u0027 is not subscriptable`. Python 3.14\ndefers annotation evaluation via PEP 649, which is why CI (running\nunder uv\u0027s newest-compatible interpreter) and developers on 3.14\nnever saw the failure.\n\nQuote the annotation so it is stored as a string and only resolved\nby the type checker, leaving the import free of the runtime\nsubscript. Pyright strict mode continues to accept the file.\n\nA follow-up will widen CI to exercise every interpreter version\nlisted in requires-python, so the minimum-supported floor is\nactually verified.\n\nReported-by: Mark Brown \u003cbroonie@kernel.org\u003e\nLink: https://lore.kernel.org/tools/7da1f1d1-25a3-4f8b-b56c-7f67c334c0af@sirena.org.uk/\nAssisted-by: claude-opus-4-7\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "c3fa56d9ef3eef9fdccc909d6304d35775411f95",
      "tree": "7d79abca05266352e0375ed7c2210de19998ce11",
      "parents": [
        "33c20a6a5d3909f09507c20ec6e66cc6338bd276"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 21 18:04:54 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 04:31:47 2026 +0000"
      },
      "message": "Replace git-filter-repo with native pygit2 history rewriting\n\nDrop the git-filter-repo dependency and implement commit-message\nrewrites natively in a new b4._rewrite module backed by pygit2,\nwhich b4 already pulls in transitively via the ezgb submodule and\nnow declares explicitly.\n\nThe old filter-repo integration silently orphaned refs/notes/*\nentries attached to any rewritten commit (upstream issue\nnewren/git-filter-repo#22). The new rewrite routine snapshots every\nrefs/notes/* ref at the start of a rewrite and re-attaches each note\nto the new commit OID afterwards, preserving the note bytes\nverbatim. This unblocks the concern raised in the lore thread at\n\u003c20250130-rich-orangutan-from-eldorado-04f621@lemur\u003e. GPG signatures\non rewritten commits continue to be stripped, matching prior\nbehavior.\n\nNotes are copied (not moved) to the new commit OIDs, matching git\u0027s\nown `rebase` / `cherry-pick` behavior under `notes.rewriteRef`.\nOld-OID notes remain reachable through the notes ref tree alongside\nthe refs/original/\u003cbranch\u003e backup; users who want to reclaim that\nspace can delete the backup ref and then run `git notes prune`,\nwhich removes notes whose annotated commit is no longer reachable.\n\nKey changes:\n- Add src/b4/_rewrite.py with rewrite_commit_messages() covering the\n  two b4 call sites (cover-letter update and trailer update),\n  refs/original/\u003cbranch\u003e backups, reflog entries, and pre/post\n  rewrite hook integration.\n- Swap both filter-repo call sites in src/b4/ez.py; remove\n  run_frf(), FRCommitMessageEditor, can_gfr guard, check_can_gfr(),\n  and the .git/filter-repo/already_ran cleanup kludge.\n- Drop \"git-filter-repo\u003e\u003d2.30,\u003c3.0\" from pyproject.toml; add\n  explicit \"pygit2\u003e\u003d1.15,\u003c2.0\". Regenerate requirements*.txt.\n- Add src/tests/test_rewrite.py (13 tests) covering empty edit map,\n  tree/signature preservation, descendant reparenting, branch\n  backup, reflog entry, passthrough messages, trailing-newline\n  normalization, notes migration across default/custom/multiple\n  refs, the no-notes fast path, and pre/post hook integration.\n- Trim the two filter-repo-mocking tests from src/tests/test_ez.py\n  (replacements live in test_rewrite.py alongside the real pygit2\n  repo fixture); add end-to-end notes-preservation coverage driving\n  the real b4 entry points: a parametrized trailers --update test\n  exercising both the default and a custom notes ref, plus a\n  store_cover (cover-letter edit) test with notes attached to series\n  patches and a custom-ref note on the cover commit.\n- docs/releases.rst: new Unreleased section describing the migration\n  and the notes preservation behavior.\n\nAssisted-by: claude-opus-4-7\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "33c20a6a5d3909f09507c20ec6e66cc6338bd276",
      "tree": "675292aa79540d3df3d83840207e783ab5794e81",
      "parents": [
        "f8e97284cf96266f0cc8c70a1d6b583d8b76f91d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 03:22:58 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 03:22:58 2026 +0000"
      },
      "message": "ci: install all extras and dependency groups before checking\n\nThe type-checker stack (mypy, pyright, ty) needs every project\nimport to be resolvable in the active environment to produce a\nclean run. Without the misc dependency group installed, ty\ncorrectly reports unresolved-import errors on misc/send-receive.py\nand misc/retrieve_lore_thread.py for sqlalchemy / pydantic /\ninstructor / falcon / ezpi; mypy and pyright report the same\nmissing-stub errors. Until now ci.sh worked locally only because\nprior `uv run` invocations had incidentally synced extras into the\nvenv.\n\nSync everything explicitly at the top of the script with\n`uv sync --all-extras --all-groups` so the run is reproducible in\na freshly cloned tree and so all six checks operate on the same\nfully-populated environment.\n\nAssisted-by: claude-opus-4-7\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f8e97284cf96266f0cc8c70a1d6b583d8b76f91d",
      "tree": "0d87735e078d3b32952cc94dcb7a8c44fc2bc53d",
      "parents": [
        "d87c44669d8226bce0d882734ac2bea6aa68cefe"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 03:11:11 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 23 03:11:11 2026 +0000"
      },
      "message": "Fix review nits from the stricter local checks series\n\nA handful of small follow-ups to the v2 ruff/typing series\n(d87c446 \"Merge patch series \\\"Enable stricter local checks\\\"\")\ncaught during review. Each is a localised cleanup — no behaviour\nchange for the common path.\n\nKey changes:\n- ez.py: in get_prep_branch_as_patches(), assert that the\n  tracking-data revision is an int up front and rename the\n  per-prereq reassignment to prereq_revision so the\n  function-scope variable retains a stable type. Drops the\n  late assert + uncertainty comment that was acknowledging the\n  type confusion.\n- __init__.py: rename the local `hash` variable (which shadowed\n  the Python builtin) to `bound_hash` in the populate-index loop.\n- misc/review-ci-example.py: prefix the deliberately-unused\n  example variables with underscore so both ruff F841 and\n  pyright\u0027s reportUnusedVariable exempt them, instead of\n  carrying a stack of `# noqa # pyright: ignore` comments\n  that hurt the file\u0027s didactic value.\n\nAssisted-by: claude-opus-4-7\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "d87c44669d8226bce0d882734ac2bea6aa68cefe",
      "tree": "5c73b16bc666db9d29bdddcb21a3ce344051edda",
      "parents": [
        "3bfbc1bf8f9549cfa2ad3949d807ce3d4954bb5d",
        "9186f8a042abb29c90db79fc70967d0d88236d10"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Merge patch series \"Enable stricter local checks\"\n\nTamir Duberstein \u003ctamird@kernel.org\u003e says:\n\nEnable stricter local checks\n\nThis series makes b4 local developer checks enforceable from the\nreview TUI and makes the repo clean under ruff, mypy, pyright, and ty.\n\nThe early patches set ruff formatting and import behavior, make the\ntest environment reproducible under uv, and type the misc helpers enough\nfor whole-repo mypy. The middle patches tighten mypy and pyright, then\nadd ty with all rules enabled and bump the Python requirement to 3.11\nbecause the code already uses 3.11-only syntax.\n\nLink: https://patch.msgid.link/20260419-ruff-check-v2-0-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "9186f8a042abb29c90db79fc70967d0d88236d10",
      "tree": "5c73b16bc666db9d29bdddcb21a3ce344051edda",
      "parents": [
        "b150595b630814ba95c1178319dfa5f46730adb1"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:06 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Enable pyright strict mode\n\nThis catches a few impossible type assertions.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-11-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "b150595b630814ba95c1178319dfa5f46730adb1",
      "tree": "92e9a7f92b093c842dfe36a7ddd78ffdcf767cd1",
      "parents": [
        "badbf476853eefe57c4f51593c1483a937428aee"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:05 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Add ty and configuration\n\nThis revealed usage that assumed Python \u003e\u003d 3.11 such as `|` unions and a\nparticular overload of `wsgiref.simple_server.make_server`.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-10-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "badbf476853eefe57c4f51593c1483a937428aee",
      "tree": "066a1b8b13f819571637ef0a3491e149231b4291",
      "parents": [
        "c61f5cb97b461c90732c47130471e72cae069aa7"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:04 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Avoid duplicate map lookups\n\nUse dict.get and defaultdict to avoid repeated membership checks before\nindexing into the same map. This keeps the existing behavior while\nmaking later type narrowing easier for mypy and pyright.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-9-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "c61f5cb97b461c90732c47130471e72cae069aa7",
      "tree": "1c4550c426880a19fece637fb289d32c02bcafe7",
      "parents": [
        "6de7cb316dc3a8cbcaa5547930d0c5b97bb46789"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:03 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Enable and fix pyright diagnostics\n\nEnable Pyright in standard mode, add it to the dev dependency group, and\nreport unused imports.\n\nMove lazy b4.review, b4.ty, and b4.ez imports to module scope so Pyright\ncan see package attributes without local import side effects, and add\nexplicit stdlib email submodule imports in tests for the same reason.\n\nTighten type annotations and assertions that Pyright reports on,\nincluding logger and send-mail queue types, Message-ID revision values,\nPatchwork state strings, JSON trackfile/msgid fields, and TUI callbacks\nthat may be dismissed with None.\n\nFix control-flow edges surfaced by reportPossiblyUnboundVariable:\nduplicate list-id preference fallback, patch-id matching after skipped\nduplicate diffs, trailer source logging without a backing message,\nmetadata JSON decoding, and review reply rendering.\n\nReject non-string -c/--config assignments explicitly and avoid using\nConfigDictT values as dict keys in tests.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-8-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "6de7cb316dc3a8cbcaa5547930d0c5b97bb46789",
      "tree": "e6d7009584d7a6212c5b17e14a8b3d5b2fde4736",
      "parents": [
        "04c3e2a781b7e557d9be4525eaef5523d1ba35cf"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:02 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Enable mypy unreachable warnings\n\nTurn on warn_unreachable and remove dead branches that it exposes.\n\nSome of those branches were stale null checks against non-optional APIs.\nOthers were test assertions that hit mypy\u0027s stale narrowing of mutable\nattributes, so add targeted ignores with a reference to the upstream\nissue.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-7-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "04c3e2a781b7e557d9be4525eaef5523d1ba35cf",
      "tree": "e765177795c3cb2b730e01ba84ac72be473efd14",
      "parents": [
        "9231a4ebe52d35fc42ea5f71a6551d79cf7f51c8"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:01 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Fix typings in misc/\n\nThis allows mypy to run over the whole repo.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-6-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "9231a4ebe52d35fc42ea5f71a6551d79cf7f51c8",
      "tree": "b8c583f6a4f6c599f4fc0037b4c5e9af5f185507",
      "parents": [
        "0a0a42f6166f287f986f483502a1ef3a06ebc18d"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 12:00:00 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Fix tests under uv with complex git config\n\nAdd pytest-asyncio to the dev group so pytest can run async TUI\ntests.\n\nPin git-filter-repo to unreleased commit 4697eeb for the multiline\ngit config parser fix requested in\nhttps://github.com/newren/git-filter-repo/issues/638.\n\nThat parser fix is the only functional change since v2.47.0:\nhttps://github.com/newren/git-filter-repo/compare/v2.47.0...4697eeb37b7c3c30b0492e344f6b89f7139cef26\n\nInject commit.gpgsign\u003dfalse through the test fixture so synthetic git\ncommits do not hang on local GPG/pinentry configuration. Also disable\nattestation through MAIN_CONFIG so tests keep the old can_patatt\u003dfalse\nbehavior after patatt becomes an unconditional dependency.\n\nAs a drive-by, route the test b4 globals, pytest sentinel, and XDG\nenv overrides through monkeypatch so each test gets automatic\ncleanup.\n\nAdd pytest to the CI script.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-5-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "0a0a42f6166f287f986f483502a1ef3a06ebc18d",
      "tree": "554e297ab3daf41f37bb2ee89dbbe5b93fb498b3",
      "parents": [
        "24faa874f9f245484a62c29d0094f251396d93cd"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 11:59:59 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Add ruff format check to CI\n\nEnable ruff format checking in the b4 CI script and configure Ruff\u0027s\nformatter in pyproject.toml.\n\nApply a one-time repo-wide format pass so the new check enforces the\ncurrent style without leaving the branch permanently red.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-4-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "24faa874f9f245484a62c29d0094f251396d93cd",
      "tree": "db4415aad58698c0a4eaa96b5eb5b63641e41125",
      "parents": [
        "07fa553598c68805f3bb023903fc4f16d64a4f54"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 11:59:58 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Import dependencies unconditionally\n\nThese are always available since commit f4185d6b.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-3-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "07fa553598c68805f3bb023903fc4f16d64a4f54",
      "tree": "808a20c00223c39c6fc41a33d85f3c6eea348fe4",
      "parents": [
        "a08a591943b0d1f18d6590242fab649ac62616e7"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 11:59:57 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Add ruff checks to CI\n\nMark example-only variables as intentionally unused so ruff can check\nthe script without changing its illustrative structure.\n\nChange `ruff.lint.select` to `ruff.lint.extend-select` to enable default\nlints and fix ambiguous variable name warnings.\n\nEnable import sorting and use `ruff check --fix` to fix existing\nviolations.\n\nConfigure ruff to skip submodules.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-2-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a08a591943b0d1f18d6590242fab649ac62616e7",
      "tree": "bf6c067f5b8200e6491a3c823b4d236ea2a114dd",
      "parents": [
        "3bfbc1bf8f9549cfa2ad3949d807ce3d4954bb5d"
      ],
      "author": {
        "name": "Tamir Duberstein",
        "email": "tamird@kernel.org",
        "time": "Sun Apr 19 11:59:56 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 22 22:48:13 2026 -0400"
      },
      "message": "Add CI script\n\nAdd a shell script that runs mypy. Skip submodule paths if they are\npresent and the misc directory which doesn\u0027t yet type check.\n\nSigned-off-by: Tamir Duberstein \u003ctamird@kernel.org\u003e\nLink: https://patch.msgid.link/20260419-ruff-check-v2-1-089dfb264501@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "3bfbc1bf8f9549cfa2ad3949d807ce3d4954bb5d",
      "tree": "697a7eaf702c9f1c1297fd989df238816c190038",
      "parents": [
        "714efc825f1c6c90bf44d639c184230989d577ad"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 15 09:58:46 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 15 10:01:10 2026 -0400"
      },
      "message": "prep: fix editor/pager selection priority to follow git\u0027s convention\n\nPreviously, edit_in_editor() and view_in_pager() checked core.editor\n(or core.pager) first and only fell back to the $EDITOR (or $PAGER)\nenvironment variable, ignoring $GIT_EDITOR and $VISUAL entirely. This\nmade it impossible to override the editor via environment variables\nwhen core.editor was set in gitconfig.\n\nFix both functions to follow git\u0027s documented selection order:\n\n  For editor: $GIT_EDITOR -\u003e core.editor -\u003e $VISUAL -\u003e $EDITOR -\u003e vi\n  For pager:  $GIT_PAGER  -\u003e core.pager  -\u003e $PAGER  -\u003e less\n\nUpdate the CLI help text and prep.rst documentation to match.\n\nReported-by: Alice Ryhl \u003caliceryhl@google.com\u003e\nLink: https://lore.kernel.org/r/CAH5fLghw\u003dfjJoWe3pt0QWVKpYXc_SzKmhYZdapkfWBOD9\u003d1+rQ@mail.gmail.com\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "714efc825f1c6c90bf44d639c184230989d577ad",
      "tree": "189206e50cc592f6ff640199fc6fbdb34e31e14e",
      "parents": [
        "a64487d89885ccede2baae9c12d6a46172b5e18b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 14 12:02:35 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 14 12:02:35 2026 -0400"
      },
      "message": "review: delete all revisions when abandoning a series\n\nWhen a series is upgraded from v1 to v2, the v1 database entry is kept\nwith status\u003d\u0027archived\u0027. The previous abandon code called delete_series()\nwith the current revision only, leaving those archived prior-revision rows\nbehind. The next attempt to track the same series would then fail with\n\"This series is already tracked (status: archived, v1)\".\n\nAbandon means the maintainer wants no trace of the series. Drop the\nrevision argument so delete_series() removes every row for the change-id,\nincluding archived prior revisions, allowing the series to be re-tracked\nfrom scratch if needed.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "a64487d89885ccede2baae9c12d6a46172b5e18b",
      "tree": "c058a82d88e41bb8e528e6b9196effaa98055b95",
      "parents": [
        "620888e2518c7ed2372e355377669d78eeefad2d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 14 11:50:19 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 14 11:50:19 2026 -0400"
      },
      "message": "review: fix two bugs in series upgrade workflow\n\nWhen upgrading a series after the maintainer has already sent their review,\nprior comments were incorrectly carried over verbatim to the new version.\nThis is wrong: once feedback is sent, repeating the exact same comment is\nnever the right action -- the maintainer would ask why a requested fix was\nnot implemented, not simply repeat themselves. The fix checks the old series\nstatus before the upgrade and only carries over WIP review content when the\nstatus is not \u0027replied\u0027. When it is, the \"unchanged\" visual marker is still\napplied to unmodified patches, but no review content is copied. The prior\nfeedback remains fully accessible via the \"Prior\" (P) screen.\n\nThe second bug was that the \"Prior\" screen showed \"[No feedback]\" for every\npatch even when feedback had been sent. The render_prior_review_context()\nfunction only checked the \u0027note\u0027 and \u0027reply\u0027 fields of each review entry,\nbut inline comments (the most common form of review) are stored in\n\u0027comments\u0027 after being parsed from the quoted diff, and \u0027reply\u0027 is then\ndeleted. The function was also ignoring \u0027trailers\u0027. Fix by extracting a\n_render_review_section() helper that handles all four fields: note, reply,\ntrailers, and comments.\n\nKey changes:\n- upgrade: skip review carryover when old status is \u0027replied\u0027\n- upgrade: skip reanchor_patch_comments when no WIP reviews were carried over\n- render_prior_review_context: add \u0027comments\u0027 and \u0027trailers\u0027 to the output\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "620888e2518c7ed2372e355377669d78eeefad2d",
      "tree": "74f0618c89e4646074851368aceb6cc291ee0714",
      "parents": [
        "f2c08903fddb2c65bc0eae20d051f851842c6dd1"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 15:14:16 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 15:14:41 2026 -0400"
      },
      "message": "Update liblore dependency to \u003e\u003d 0.7 and bump submodule to v0.7.1\n\nLiblore 0.7.x adds per-origin git config subsections and a public\nAPI module. The 0.7.1 release is a minor follow-up with a\npytest-asyncio warning fix. None of these changes affect b4\u0027s\ncurrent usage — from_git_config() remains fully backwards-compatible,\nfalling back to [lore] for lore.kernel.org URLs.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6-20250925\n"
    },
    {
      "commit": "f2c08903fddb2c65bc0eae20d051f851842c6dd1",
      "tree": "7fc5580064ed002ecaffc70dd3ba0e3d767163b4",
      "parents": [
        "3a959ad50d21042a7b3369ea9a91fd92abf8b461"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 11:01:00 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 11:01:00 2026 -0400"
      },
      "message": "review: don\u0027t treat replies to additional patches as inline reviews\n\nWhen a follow-up message in a thread contains its own diff (an\nadditional patch posted during discussion), replies to it quote that\ndiff rather than the original series patch. The inline comment\nextraction logic was walking the in-reply-to chain up to the original\npatch and injecting these quoted diffs as code reviews on the wrong\npatch.\n\nAdd _chain_has_additional_patch() which detects when the reply chain\npasses through a diff-bearing intermediate message. Flag such\nfollowups with \u0027replies-to-diff\u0027 and skip them during inline comment\nextraction. The followups still appear in the comment panel -- only\nthe inline diff injection is suppressed.\n\nReported-by: Miroslav Benes \u003cmbenes@suse.cz\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "3a959ad50d21042a7b3369ea9a91fd92abf8b461",
      "tree": "9cd0f3c62c73a649e8cce15ae90a56cf6f7135c9",
      "parents": [
        "fa2ea046b7ec06cc13dd03760a0fe0cbb40961f1"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 09:27:18 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 09:27:18 2026 -0400"
      },
      "message": "review: fix IndexError crash when toggling followups\n\nTextual\u0027s ListView.clear() sets index to None synchronously but only\nschedules DOM removal for the next event loop tick. When\n_populate_patch_list() immediately appends new items and then iterates\nlv.children to restore the cursor position, lv.children still contains\nboth the old (pending removal) and new items. This caused the cursor\nindex to be set based on the inflated combined list. Once the old items\nwere finally pruned, the index pointed past the end of the actual list,\nresulting in an IndexError on the next cursor movement.\n\nFix by tracking the new-item count ourselves instead of consulting\nlv.children, and deferring the index restoration via call_after_refresh\nso it runs after the stale items have been removed from the DOM.\n\nReported-by: Miroslav Benes \u003cmbenes@suse.cz\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "fa2ea046b7ec06cc13dd03760a0fe0cbb40961f1",
      "tree": "de51f71919b12d27879d3fa384cc777e0bb0f573",
      "parents": [
        "02b01d4f8ed8efab3bea067e974be7a6e3decffa"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 09:02:48 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Apr 09 09:02:48 2026 -0400"
      },
      "message": "bugs: handle import errors gracefully in TUI\n\nWhen importing a thread that isn\u0027t available in public archives,\nimport_thread() raises a RuntimeError which crashed the TUI app.\nFix this by setting exit_on_error\u003dFalse on the worker so the\nexisting on_worker_state_changed error handler can display the\nmessage, and replace the verbose error with a concise one that\nfits the import dialog.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "02b01d4f8ed8efab3bea067e974be7a6e3decffa",
      "tree": "f76c1ed19e285b7e522dc06c5f6bd3dc00149b10",
      "parents": [
        "ed72edaf6e9a3a43c9a73292629672849d402d8e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 11:45:06 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 11:45:51 2026 -0400"
      },
      "message": "Add liblore and ezgb as git submodules\n\nAdd liblore and ezgb as git submodules alongside the existing patatt\nsubmodule, so maintainers running b4 straight from the git checkout\nhave all dependencies available without a pip install.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "ed72edaf6e9a3a43c9a73292629672849d402d8e",
      "tree": "b657c899c595a8bcb364c79c2861f1da78949083",
      "parents": [
        "f87d1a9c53ccf8a9451d6bdef3215223cd9cc750"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 11:16:07 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 11:16:07 2026 -0400"
      },
      "message": "Fix midmask regression for bare-origin URLs\n\nThe liblore migration (db4cd69) broke midmask configurations without a\nlist path component, such as \u0027https://lore.kernel.org/%s\u0027. The old code\nperformed a HEAD request and followed the server\u0027s redirect to discover\nthe correct list path, but liblore constructs URLs directly from the\nbase URL without any redirect-based discovery.\n\nAppend \u0027/all\u0027 when the base URL derived from midmask has no path, which\nis the cross-archive endpoint on lore.kernel.org servers. For non-lore\npublic-inbox instances that may use different archive paths, liblore\n0.6.1 adds a HEAD+redirect fallback in get_mbox_by_msgid() as a last\nresort when the direct URL returns 404.\n\nBump liblore dependency to \u003e\u003d0.6.1 for the redirect fallback and\nregenerate hashed requirements.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "f87d1a9c53ccf8a9451d6bdef3215223cd9cc750",
      "tree": "a6073974b521f776880035d4d176feeceefd0bc1",
      "parents": [
        "564bb1613ecf539776acd9418c5177b9e8cddb0a"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 10:42:36 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 10:42:36 2026 -0400"
      },
      "message": "review: show per-series error details after update-all\n\nThe \"Update all\" action previously only reported the total error count\n(e.g. \"5 error(s)\") with no way to see what actually failed, even with\ndebug output enabled. Collect the submitter name and error message for\neach failed series and display them as individual error notifications\nin the TUI, plus log each at warning level for the terminal log.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "564bb1613ecf539776acd9418c5177b9e8cddb0a",
      "tree": "9bcc2490df6b25494bc4523beff7fbd25fa81417",
      "parents": [
        "c33c5da69e01f6be228575c10ab33aadfbca65f1"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 01:39:00 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 01:40:20 2026 -0400"
      },
      "message": "docs: document server failover settings\n\nAdd a \"Server failover settings\" section to docs/config.rst covering\nthe [lore] git config keys that liblore reads for failover: fallback,\nautoprobe, probetimeout, probettl, and useragentplus. These are shared\nby all liblore-based tools so configuring them once benefits b4 and\nany other consumers.\n\nUpdate plan.otl to reflect completed failover integration work and\nnote staleness detection as the remaining open item.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "c33c5da69e01f6be228575c10ab33aadfbca65f1",
      "tree": "3d86038815f9ac5ffc494de6dfa4127ec1d81772",
      "parents": [
        "f7ded9b43de864613d65efd96454804ee8913ecb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 18:02:37 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Apr 08 01:25:46 2026 -0400"
      },
      "message": "Integrate liblore failover features\n\nSwitch get_lore_node() from direct LoreNode() construction to\nLoreNode.from_git_config(), which reads lore.fallback, lore.autoprobe,\nlore.probetimeout, lore.probettl, and lore.useragentplus from git\nconfig. This gives b4 users automatic failover, latency-based origin\nprobing, and mirror support with zero b4-specific configuration.\n\nStop injecting b4\u0027s requests session into liblore and let liblore own\nits session instead. This is required for set_user_agent() to properly\npropagate the User-Agent header (including useragentplus) to the\nsession. The b4 shared session continues to serve non-lore HTTP callers\n(patchwork, sashiko, github).\n\nKey changes:\n- get_lore_node() uses LoreNode.from_git_config() with b4\u0027s cache params\n- Removed set_requests_session() call, added set_user_agent() call\n- Bumped liblore dependency from \u003e\u003d0.5 to \u003e\u003d0.6\n- Added TestGetLoreNode test class with 5 tests\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "f7ded9b43de864613d65efd96454804ee8913ecb",
      "tree": "cf63f5faa206bcbc903a294c23c58a0fa265575c",
      "parents": [
        "db4cd69b9229e7e2cc231d9e7d004249af58387e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 15:47:08 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 15:47:08 2026 -0400"
      },
      "message": "plan: mark liblore migration as complete\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6-20250929\n"
    },
    {
      "commit": "db4cd69b9229e7e2cc231d9e7d004249af58387e",
      "tree": "3eab8b16b5713fd0a27e8a1cadb690c9ae96fd62",
      "parents": [
        "7a3e7ccf125ead6039b991faee89757061a22016"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 11:17:20 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 15:40:43 2026 -0400"
      },
      "message": "Migrate lore access to liblore library\n\nReplace b4\u0027s hand-rolled public-inbox access with the liblore library,\nwhich provides a clean, tested API for the same operations. Caching is\nnow handled by liblore\u0027s filesystem cache with per-request TTL\nvalidation, fixing stale-cache issues in long-running processes like\nthe review TUI where b4\u0027s once-per-process cleanup was insufficient.\n\nKey changes:\n- Add liblore\u003e\u003d0.5 as a dependency\n- Add get_lore_node() factory returning a LoreNode configured with\n  b4\u0027s requests session, URL from midmask config, and filesystem\n  cache in ~/.cache/b4/lore/ with configurable TTL\n- LoreMessage.get_clean_msgid() delegates to liblore.utils\n- LoreMessage.get_preferred_duplicate() delegates to liblore.utils\n- get_strict_thread() keeps b4\u0027s auto-noparent heuristic, delegates\n  the core thread-walking to liblore.utils.get_strict_thread()\n- mailsplit_bytes() uses liblore.utils.split_mbox(), drop unused\n  outdir parameter\n- split_and_dedupe_pi_results() uses liblore.utils.split_and_dedupe(),\n  drop cachedir parameter (liblore handles caching)\n- get_pi_thread_by_msgid() uses LoreNode.get_mbox_by_msgid() with\n  nocache passthrough; remove b4-level .pi.msgs directory cache\n- get_pi_search_results() uses LoreNode.get_mbox_by_query() with\n  full_threads and nocache passthrough; remove b4-level cache\n- get_pi_thread_by_url() removed (was only called internally)\n- tracking.py: collapse _resolve_canonical_url + _fetch_mbox_bytes\n  into _fetch_thread_mbox_bytes() via LoreNode; _fetch_new_since\n  uses LoreNode.get_thread_updates_since()\n- review/_review.py and review_tui/_common.py call\n  liblore.utils.split_mbox() directly\n- Remove unused gzip import from __init__.py and gzip/urllib.parse\n  from tracking.py\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6-20250929\n"
    },
    {
      "commit": "7a3e7ccf125ead6039b991faee89757061a22016",
      "tree": "9ecefb4477863d6df62cbd28311ada62574bc729",
      "parents": [
        "05a91672afb00b3b3b5f76c7757a518d80509c59"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 11:00:11 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 11:00:11 2026 -0400"
      },
      "message": "docs: document the \u0027unchanged\u0027 per-patch state\n\nAdd the ≡ (unchanged) marker to the per-patch states table in the\nreview documentation and explain when it appears and how it is\nsuperseded by other review actions.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "05a91672afb00b3b3b5f76c7757a518d80509c59",
      "tree": "ba39d1be7bd7514524c2e676b33f007de179ac5e",
      "parents": [
        "eb5a313d4e1150488973400e9f63baa225a480c0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 10:32:02 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 10:32:02 2026 -0400"
      },
      "message": "gitignore: add uv.lock\n\nb4 is a library/tool distributed via pip, so the uv lockfile is a\nlocal dev convenience rather than something consumers of the package\nneed. Ignore it to keep it out of the repo.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "eb5a313d4e1150488973400e9f63baa225a480c0",
      "tree": "a024b54968095fa8757626ceb20c82d81d4ba06f",
      "parents": [
        "9febba6b7dc345e08c4275fd07e67591624d6885"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 10:30:15 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 07 10:30:15 2026 -0400"
      },
      "message": "review: add visual cue for patches unchanged from prior revision\n\nWhen upgrading a series from vN to vN+1, patches whose content is\nidentical between revisions are now marked with patch-state \u0027unchanged\u0027\nand annotated with a system note from \u0027B4 Upgrade \u003cb4-upgrade@internal\u003e\u0027\nexplaining which revision the patch last appeared in.\n\nIn the patch list, unchanged patches display a ≡ (identical-to, U+2261)\nmarker and are rendered dim, giving the maintainer an at-a-glance signal\nthat no new review work is needed for those patches.\n\nThe \u0027unchanged\u0027 state is low-priority by design: _get_patch_state() only\nreturns it when no derived state (draft, done, external) applies, so it\nis automatically superseded the moment the maintainer adds a trailer,\nreply, or inline comment.\n\nKey changes:\n- _review.py: add \u0027unchanged\u0027 as a valid explicit patch state returned\n  by _get_patch_state() as a low-priority fallback\n- _common.py: add ≡ marker for \u0027unchanged\u0027 in PATCH_STATE_MARKERS\n- _review_app.py: dim \u0027unchanged\u0027 items in the patch list\n- _tracking_app.py: mark all content-identical patches as \u0027unchanged\u0027\n  on upgrade, whether or not they had prior review data, and annotate\n  them with a B4 Upgrade system note\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "9febba6b7dc345e08c4275fd07e67591624d6885",
      "tree": "b3748501515c845ad27146bf6c5952146f86ac5b",
      "parents": [
        "3e2f01be1607ffbc3289667b99ede834e83013a4"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Apr 06 16:52:28 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Apr 06 16:52:28 2026 -0400"
      },
      "message": "review: cache per-revision thread blob SHA in the revisions table\n\nWhen range-diffing against an older revision, _fetch_fake_am_range()\npreviously had no way to look up the cached mbox blob for that revision\nand always fell back to a lore fetch.\n\nAdd a thread_blob column to the revisions table (schema version 8) and\npopulate it during the upgrade flow, right before the old revision\u0027s\nreview branch is archived.  _fetch_fake_am_range() now reads the blob\nSHA directly from the revisions dict returned by get_revisions(), so a\ncached blob is used whenever it is still present in the object store.\n\nSince git blobs become unreachable after branch archival and may be\ngarbage-collected at any time, all code paths that consume the stored\nSHA already tolerate a missing blob via the existing get_thread_mbox()\nNone return and the lore fallback beneath it.\n\nKey changes:\n- tracking.py: bump SCHEMA_VERSION to 8; add thread_blob column and\n  migration (handles DBs that predate the revisions table entirely)\n- tracking.py: new set_revision_thread_blob() helper\n- tracking.py: get_revisions() / get_all_revisions_grouped() include\n  thread_blob in the returned dicts\n- _tracking_app.py: record the current revision\u0027s blob before archiving\n- _tracking_app.py: _fetch_fake_am_range() looks up blob_sha from the\n  revisions record; drop the now-unused blob_sha parameter\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    },
    {
      "commit": "3e2f01be1607ffbc3289667b99ede834e83013a4",
      "tree": "f0780bebaeb2ac1f3172dd7314fa17dc3fe95523",
      "parents": [
        "060875403fd6dffbe792900f754cd9f1c6b6e9e0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Apr 06 16:29:53 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Apr 06 16:29:53 2026 -0400"
      },
      "message": "review: fix range-diff failing to find previous series revision\n\nWhen pressing \u0027d\u0027 to range-diff the current revision against an older\none, _do_range_diff() passed the current revision\u0027s thread-blob SHA to\n_fetch_fake_am_range() for the older revision.  The cached blob belongs\nto the current (newer) revision\u0027s thread, so get_series() could not find\nthe older revision\u0027s patches in it and reported \"Could not find series\nv1 in retrieved messages\".\n\nThe v1 thread-blob is only preserved in the tar.gz archive created\nduring branch upgrade, with no easily-accessible SHA reference in the\ncurrent tracking data.  Drop the blob_sha argument when fetching the\nother revision so _fetch_fake_am_range() always falls back to lore for\nthat side of the diff.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-sonnet-4-6\n"
    }
  ],
  "next": "060875403fd6dffbe792900f754cd9f1c6b6e9e0"
}
