review: TUI-based patch review workflow (alpha)
================================================

.. warning::

   ``b4 review`` is a **technology preview** (alpha). Commands, keybindings,
   configuration options, and on-disk formats may change in incompatible ways
   between releases. Bug reports and feedback are welcome at tools@kernel.org.

The ``b4 review`` command provides a TUI-based workflow for maintainers
who receive patches on mailing lists. It lets you track incoming patch
series, review diffs with inline commenting, add code-review trailers,
send review emails, and accept (apply) patches — all from a single
terminal interface.

The workflow is built around a lightweight SQLite tracking database that
records every series you are interested in, together with a per-series
review branch in your git repository where comments, trailers, and
review state are persisted between sessions.

Getting started
---------------

Enrolling a repository
~~~~~~~~~~~~~~~~~~~~~~
Before you can track series, you need to enroll the repository::

    b4 review enroll [path] [-i identifier]

This creates metadata under ``.git/b4-review/`` and a SQLite database in
``$XDG_DATA_HOME/b4/review/``. The ``-i`` flag sets the project
identifier (defaults to the repository directory name). You only need to
do this once per repository.

The ``-i`` flag lets you work with multiple checkouts of the same git
repository, for example if you manage multiple subsystems. This way you
can have ``~/work/linux-subsystemA`` with identifier "subsystemA" and
``~/work/linux-subsystemB`` with identifier "subsystemB".

Tracking a series
~~~~~~~~~~~~~~~~~
Once enrolled, add a series to the tracking database::

    b4 review track <msgid|url|change-id> [-i identifier]

You can also pipe a message on stdin::

    cat message.eml | b4 review track

This makes it easy to integrate with mail clients. For example, to
track a series directly from mutt with a single keypress, add the
following to your ``~/.muttrc``::

    macro index 8 "<pipe-message>~/bin/b4 review track -i linux-subsystemA<return>"

Pressing ``8`` on any message in the series will pipe it to b4 and
start tracking.

B4 fetches the series from lore, discovers all available revisions
(older and newer), and stores everything in the tracking database.
Press ``Ctrl-C`` at any point to cancel the network operation; b4
exits cleanly with status 130.

Tracking unthreaded series
^^^^^^^^^^^^^^^^^^^^^^^^^^
When a submitter sends patches without proper threading (each patch as a
separate, unrelated message), you can use ``--rethread`` to stitch them
together. Pass any single patch from the series and b4 auto-discovers
the rest::

    b4 review track --rethread <msgid>

B4 searches lore for other patches from the same author sent within a
1-hour window, matches them by ``[PATCH n/m]`` counters and version,
and assembles the full series. You can also provide all message IDs
explicitly::

    b4 review track --rethread <msgid1> <msgid2> [<msgid3> ...]

Or read them from stdin::

    b4 review track --rethread - < msgids.txt

The ``--rethread`` flag cannot be combined with a positional series
identifier.

If the submitter also posted a correctly threaded re-send of the same
version, b4 prefers that copy instead of stitching. And if the
unthreaded series is a new version of one you already track, it is
recorded as a new revision of that series rather than a duplicate.

.. versionadded:: v0.16

Using the TUI
-------------
Launch the TUI inside the repository with::

    b4 review tui [-i identifier]

If you are already on a review branch (``b4/review/<change-id>``), the
TUI opens the review interface directly and shows a notification
suggesting you switch to your target branch (configured via
:term:`b4.review-target-branch`, or ``master``/``main`` by default)
if you want to see the full tracking list instead. Otherwise it starts
in the tracking list.

You can leave the tui running. Any series added in another terminal will
be automatically recognized and displayed.

Tracking list
~~~~~~~~~~~~~
The tracking list shows all series you are following, with their current
status.

**Series lifecycle**

Every tracked series passes through these statuses:

=============  =========================================================
Status         Meaning
=============  =========================================================
``new``        Freshly tracked, not yet reviewed
``reviewing``  Review branch created, review in progress
``replied``    Review comments sent to the mailing list
``waiting``    Waiting for a new revision from the submitter
``snoozed``    Deferred until a date, duration, or git tag
``partial``    Some patches applied; remainder still under review
``accepted``   All patches applied to the target branch
``thanked``    Thank-you note sent to the contributor
``archived``   Review completed and archived
``gone``       Review branch deleted or missing (e.g. after a sync)
=============  =========================================================

Series are grouped by lifecycle stage — Active (reviewing/replied/
partial/accepted/thanked), New, Waiting, Snoozed, and Gone — and sorted
within each group by the most recent activity, whether that is a new
mailing list reply or a local status change. A ``waiting`` series is
pulled up into the Active group the moment a newer revision is
discovered: the upgrade it was waiting for is now available, so it
surfaces alongside the other actionable series instead of staying at
the bottom of the list.

**Listing columns**

Each row in the tracking list shows:

* **Submitter** — patch author name (truncated if necessary)
* **A** — attestation indicator: ``✔`` when all signatures verify,
  blank otherwise (details shown in the bottom panel)
* **A·R·T** — Acked-by · Reviewed-by · Tested-by trailer counts
  collected from the review branch
* **Msgs** — total message count for the thread, with any unseen
  messages shown as ``+N`` in yellow (populated after pressing ``u``)
* **S** — single-character status symbol, optionally followed by a
  flag: ``*`` means the tracking data needs a refresh, ``↑`` means a
  newer revision is available (upgrade via the action menu). The two
  are mutually exclusive — ``↑`` can only appear once revision data
  has been fetched
* **Subject** — compact ``[subsystem,vN,0/M]`` prefix followed by the
  series subject

**Status symbols**

=======  =============
Symbol   Status
=======  =============
``★``    new
``✎``    reviewing
``↩``    replied
``↻``    waiting
``⏸``    snoozed
``◐``    partial
``∈``    accepted
``✓``    thanked
``∅``    gone
=======  =============

**Keybindings**

============  ===========================================================
Key           Action
============  ===========================================================
``Enter``     Enter series (reviewing) or open action menu (other states)
``r``         Review — create or re-enter the review branch
``t``         Target branch — set or clear the per-series target branch
``c``         CI checks — run configured check commands and show results
              (see :ref:`ci_check_protocol`)
``e``         Thread — open the lite thread viewer (see below)
``a``         Action menu — context-sensitive actions (see below)
``d``         Range-diff between revisions
``u``         Update — fetch latest trailers and check for newer
              revisions for the selected series; press ``Escape`` or
              ``q`` to cancel
``U``         Update all — same as ``u`` but for all tracked series
              (skipping snoozed); press ``Escape`` or ``q`` to cancel
              mid-run — series already updated are saved
``l``         Limit — filter the list of displayed series. Plain text
              matches subjects and submitters; ``s:<status>`` filters
              by status, ``t:<target-branch>`` by target branch, and
              ``up:`` shows only series with an upgrade available
              (``up:no`` inverts). Multiple tokens combine with AND
``s``         Shell — suspend to an interactive sub-shell
``p``         Patchwork — switch to the Patchwork browser (if configured)
``T``         Queue — view and deliver queued thank-you messages (visible
              only when the queue is non-empty; see
              :ref:`queuing thanks <thanks_queue>`)
``j``/``k``   Move cursor down/up
``?``         Help — show keybinding reference
``Q``         Quit
============  ===========================================================

Quitting takes a capital ``Q`` in all b4 TUI apps (tracking list,
review interface, Patchwork browser, and ``b4 bugs``). Bare ``q``
only shows a reminder notification, so a stray keypress during
navigation cannot quit the app accidentally. Modal dialogs and
sub-views (thread viewer, message view, help) still close with
``q`` as before.

.. versionchanged:: v0.16
   Previously bare ``q`` quit the app directly.

Press ``t`` to open the target branch dialog. Type a branch name
(recently used branches are suggested) and press ``Enter`` to run an
applicability check — b4 will test-apply the series patches against the
chosen branch and report whether they apply cleanly. Press ``Ctrl-y``
to confirm without checking, ``Ctrl-d`` to clear the per-series target
and fall back to the configured default, or ``Escape`` to cancel
without changes.

Press ``r`` to start a review. Before applying the series to a review
branch, b4 shows a base selection dialog pre-filled with the best base
it can determine: the base specified by the series itself (when the
commit is present in your repository), a guess based on blob matching,
or ``HEAD`` as the last resort. If you always want a specific pre-fill
(for example, the current ``HEAD``), set :term:`b4.review-apply-base` —
what b4 would have detected automatically is then shown in the dialog
hint instead. The input remains editable either way, and the chosen
base is validated before the series is applied. The same dialog (and
the same pre-fill logic) is used when upgrading a tracked series to a
newer revision.

The ``a`` key opens a context-sensitive action menu. Each action has a
single-keypress shortcut shown in square brackets so you can act
quickly — for example, ``a`` then ``T`` to take a series. Available
actions depend on the series status:

**Reviewing / replied:**

* ``[T]`` **Take** — apply patches to the target branch
* ``[R]`` **Rebase** — rebase review branch onto HEAD
* ``[w]`` **Mark as waiting** — waiting on new revision
* ``[s]`` **Snooze** — defer until a date, duration, or git tag
* ``[U]`` **Upgrade** — switch to a newer revision (when available)
* ``[l]`` **Link a revision** — manually associate a revision by message-id
* ``[A]`` **Abandon** / ``[x]`` **Archive**

**Partial** (some patches applied, remainder still in review):

* ``[T]`` **Take remaining patches** — apply more patches from the series
* ``[R]`` **Rebase** — rebase review branch onto HEAD
* ``[t]`` **Thank** — send a thank-you for the patches already applied
* ``[w]`` **Mark as waiting** — waiting on a new revision
* ``[s]`` **Snooze** — defer until later
* ``[U]`` **Upgrade** — switch to a newer revision (when available)
* ``[A]`` **Abandon** / ``[x]`` **Archive**

**New / gone:**

* ``[r]`` **Review** — create or re-enter the review branch
* ``[U]`` **Upgrade** — switch to a newer revision (new only, when available)
* ``[s]`` **Snooze** — defer until later (new only)
* ``[l]`` **Link a revision** — manually associate a revision by message-id (new only)
* ``[A]`` **Abandon**

**Waiting:**

* ``[U]`` **Upgrade** — switch to the newer revision (when available)
* ``[r]`` **Review** — return to reviewing
* ``[l]`` **Link a revision** — manually associate a revision by message-id
* ``[A]`` **Abandon** / ``[x]`` **Archive**

**Snoozed:**

* ``[u]`` **Unsnooze** — wake up and restore previous status
* ``[A]`` **Abandon** / ``[x]`` **Archive**

**Accepted:**

* ``[t]`` **Thank** — send a thank-you note
* ``[U]`` **Upgrade** — switch to a newer revision (when available)
* ``[r]`` **Return to reviewing** — reopen if accepted prematurely
* ``[A]`` **Abandon** / ``[x]`` **Archive**

**Thanked:**

* ``[U]`` **Upgrade** — switch to a newer revision (when available)
* ``[r]`` **Return to reviewing** — reopen if thanked prematurely

The details panel at the bottom shows the full original subject, sender,
attestation status, send date, status, change-ID, lore link, known
revisions, and the review branch name for active and snoozed series.

**Attestation display**

The attestation row shows the DKIM and patatt signature verification
results for the series. Possible states:

* **pending** — not yet checked; press ``u`` to update
* **no signatures** — checked, but no DKIM or patatt signatures found
* **✔ identity** (green) — signature verified successfully
* **? identity (no key)** (orange) — signed, but the verifying key is
  not available locally
* **✘ identity (signature failed)** (red) — key is available but
  verification failed

Attestation is checked automatically when a series is updated (``u`` or
``U``) and when a review branch is checked out. Results are stored in
the tracking database and displayed without re-checking on subsequent
views. The attestation check honours the :term:`b4.attestation-policy`
and :term:`b4.attestation-staleness-days` configuration options.

Lite thread viewer
~~~~~~~~~~~~~~~~~~
Pressing ``e`` on any series opens a mutt-style thread viewer that
fetches the full mailing list thread from lore and displays it in two
levels:

**Thread index** — a flat list of all messages (patches and follow-ups)
with a flag column, date, author, mutt-style threading tree art, and
subject. Each message has one of four visual states:

- **Unseen** (``N``) — not yet opened in the message viewer. Shown in
  the warning colour.
- **Flagged** (``★``) — marked as important by the maintainer. Shown in
  bold accent colour for the entire row. Toggle with ``F`` in the
  message viewer.
- **Answered** (``↩``) — the maintainer has replied to the parent of
  this message (either from b4 or detected from their own email client).
  Shown with a dim accent.
- **Seen** — previously opened. The row is dimmed.

Flag state is stored in a cross-project ``messages.sqlite3`` database
(under ``$XDG_DATA_HOME/b4/review/``) keyed by message-id, so flags
persist across sessions and work from both the tracking list and the
Patchwork browser. When a review branch is checked out, all patches
in the series are automatically marked as seen. Replies sent by the
maintainer from an external email client are detected when the thread
is loaded, and the parent messages are marked as answered.

Your own messages are also marked as seen automatically, so replying
to a series never lights up its unread badge: replies sent through b4
are recorded at send time, and messages whose ``From:`` exactly
matches your configured identity (``sendemail.from``, falling back to
``user.email``) are marked as seen whenever series updates are
fetched. Mail sent from any other address is deliberately not
auto-marked.

**Message view** — full headers (Date, From, To, Cc, Subject, Link,
Attestation) and message body with diff syntax highlighting and
quoted-line dimming. Addresses are packed horizontally to make good use
of available terminal width.

The thread viewer is available from both the tracking list and the
Patchwork browser, providing a quick way to read a thread and fire off
a reply without creating a review branch.

*Thread index keybindings*

===============  =========================================================
Key              Action
===============  =========================================================
``Enter``        Open the selected message in the message viewer
``j``/``k``      Move cursor down/up
``q``            Close the thread viewer
===============  =========================================================

*Message view keybindings*

================  ========================================================
Key               Action
================  ========================================================
``j``/``k``       Next/previous message in thread
``Enter``         Scroll down one line
``Backspace``     Scroll up one line
``Space``         Page down
``-``             Page up
``S``             Skip past the next block of quoted text
``^``             Jump to top of message
``$``             Jump to bottom of message
``r``             Reply to the current message
``F``             Toggle flagged (important) state
``q``/``Escape``  Return to the thread index
================  ========================================================

The ``S`` key works like mutt's skip-quoted: it scans forward from the
current scroll position, finds the next block of quoted lines (``>``
prefixed), skips past it, and scrolls to the first non-quoted line with
a couple of trailing quote lines visible for context.

Review interface
~~~~~~~~~~~~~~~~
The review interface presents a split-pane view: the left pane lists
patches in the series, and the right pane shows the diff for the
selected patch.

**Review mode keybindings**

=========================  =============================================
Key                        Action
=========================  =============================================
``[``/``]``                Previous/next patch
``j``/``k``                Scroll down/up
``h``/``l``                Scroll left/right
``Space``/``Backspace``    Page down/up
``.``/``,``                Jump to next/previous review comment
``Tab``                    Switch focus between panels
``t``                      Trailers — quickly add Reviewed-by, Acked-by, etc.
``r``                      Reply — open ``$EDITOR`` to reply
``n``                      Notes — view or edit review notes
``f``                      Followups — toggle follow-up messages and external
                           inline comments from lore
``a``                      Agent — run review LLM agent (if configured)
``d``                      Done — toggle "done" state on the current patch
``x``                      Skip — toggle "skip" state on the current patch
``H``                      Hide skipped — toggle visibility of skipped patches
``c``                      CI checks — run check commands and show results
                           (see :ref:`ci_check_protocol`)
``e``                      Toggle email mode
``s``                      Shell — suspend to an interactive sub-shell
``?``                      Help — show keybinding reference
``Q``                      Quit (bare ``q`` only shows a reminder)
=========================  =============================================

**Email preview mode keybindings**

==========  ============================================================
Key         Action
==========  ============================================================
``T``       Edit To/Cc/Bcc recipients
``Ctrl-y``  Send review emails (``S`` still works as a legacy alias)
``e``       Toggle email mode
==========  ============================================================

**Inline diff comments**

Press ``r`` on a patch to open ``$EDITOR`` with the quoted commit
message and diff. The content uses the standard mailing-list reply
format: original content is quoted with a ``>`` prefix and your comments go on
unquoted lines between quoted sections.

To leave a comment, write it on a new line below the quoted diff line
you want to comment on::

    > @@ -10,3 +10,4 @@
    >  context line
    > +new_function();

    Needs a NULL check here.

    > +return result;

You can also comment on the commit message — it appears quoted at the
top of the editor, before the diff content. Comments before the first
quoted line are stored as general comments on the patch.

On save, b4 parses your reply and extracts each comment, associating
it with the nearest preceding quoted line (file path and line number,
or commit message line). Any trailers you added (e.g. ``Reviewed-by``)
are extracted and stored separately. Your comments are displayed in the
diff view as coloured bordered panels with your name shown in the
bottom-right corner. Use ``.`` and ``,`` to jump between comments.

Press ``r`` again to re-open the editor with your existing comments
interleaved into the quoted content for further editing.

When you send review emails, b4 automatically builds a standard
quoted-reply for each patch: only hunks that contain comments are
included, diff lines are quoted with ``>``, and your comments appear
unquoted after the relevant line — matching the format expected on
mailing lists. To keep replies concise, b4 shows at most 5 lines of
diff context above each comment and collapses larger gaps with a
``> [...]`` marker.

The left pane summarises the review state per reviewer using a trailer
overlay. This overlay always shows all reviewers — both your own review
and any external reviewers — so you can see at a glance who has
commented on the current patch.

**Reply templates**

By default, outgoing review replies contain only the quoted content,
your comments, and your signature. To wrap every reply in a standard
greeting and sign-off, point :term:`b4.review-reply-template` at a
template file::

    [b4]
      review-reply-template = ~/.config/b4/review-reply-template

The template must contain the ``${reply}`` placeholder, which expands
to the reply content itself (the quoted patch context, your comments,
and any trailers)::

    Hi ${firstname},

    ${reply}

    Cheers,
    ${myname}

Lines starting with ``#`` are removed, and substitution follows the
same rules as the ``b4 ty`` thank-you templates. The template applies
to both per-patch replies and the cover-letter reply, and the wrapped
result is visible in email mode (``e``) before sending. See
``review-reply-template.example`` shipped with b4 for all supported
placeholders.

**External inline comments**

B4 can display inline comments from external sources alongside your own
in the diff view. These sources include:

* **Follow-up messages** — replies to patches on the mailing list that
  quote diff lines and add comments. These use the standard
  mailing-list review format: diff lines quoted with ``>`` and
  unquoted reviewer comments.
* **Sashiko** — the `sashiko.dev <https://sashiko.dev>`_ AI code review
  service (when :term:`b4.sashiko-url` is configured).
* **Review agents** — AI review agents invoked with ``a``.

External inline comments are only displayed when follow-ups are active
— press ``f`` to toggle them on. Your own comments are always visible
regardless of the ``f`` state. When external comments are hidden, the
diff view shows a ``≡`` gutter marker in the left margin on lines that
have external comments, so you can see at a glance where reviewers have
commented without loading the full panels.

Each reviewer's comments are displayed in a distinct colour (green is
excluded from the palette to avoid blending with diff additions). The
reviewer's full name is shown in the bottom-right corner of each
comment panel; your own comments show "You" instead of your name.

**Adopting external comments**

When you open the reply editor (``r``), external reviewer comments are
shown with a ``|`` prefix::

    > +new_function();

    | Pat Reviewer <pat@example.com>:
    |
    | Is it possible for this code to trigger an exception
    | on heterogeneous systems?
    |
    | via: https://lore.kernel.org/...

    > +return result;

Lines starting with ``|`` are read-only — they are shown in the editor
for context but are not saved as your own comments.

To adopt an external comment as your own (for example, to endorse an
agent suggestion or incorporate a reviewer's point), delete the ``|``
prefix from the comment lines::

    > +new_function();

    Is it possible for this code to trigger an exception
    on heterogeneous systems?

    > +return result;

The comment is now treated as yours and will be included when you send
review emails. You can also edit the text before saving. The original
reviewer's copy remains in the tracking data.

.. _review_editor_plugins:

**Editor syntax highlighting**

B4 ships syntax highlighting files for Vim and Emacs in the ``misc/``
directory. These provide diff-aware colouring for the reply editor
format: quoted additions in green, removals in red, hunk headers in
cyan, external ``|`` comments visually bracketed, and your own comments
in the default foreground. Spell checking is limited to your own
comment lines.

*Vim*

Copy or symlink the files into your Vim configuration::

    mkdir -p ~/.vim/syntax ~/.vim/ftdetect ~/.vim/ftplugin
    ln -s /path/to/b4/misc/vim/syntax/b4review.vim ~/.vim/syntax/
    ln -s /path/to/b4/misc/vim/ftdetect/b4review.vim ~/.vim/ftdetect/
    ln -s /path/to/b4/misc/vim/ftplugin/b4review.vim ~/.vim/ftplugin/

The ftplugin disables auto-wrapping and auto-reflow of quoted lines (so
an aggressive ``formatoptions`` cannot silently rewrap or join the diff)
and adds the hunk-trimming mappings described below, so install it
alongside the syntax file. If ftdetect does not work with your plugin
manager, add this to your ``~/.vimrc`` instead::

    augroup filetypedetect
      autocmd BufNewFile,BufRead *.b4-review.eml set filetype=b4review
    augroup END

**Trimming quoted context (Vim)**

When you send a reply, b4 automatically drops the quoted diff left below
your last comment. The Vim ftplugin adds two mappings for pruning the
rest interactively as you read, each leaving a
``[ ... NN lines skipped ... ]`` marker where context was removed
(``<LocalLeader>`` is ``\`` by default):

==================  ====================================================
Key                 Action
==================  ====================================================
``<LocalLeader>h``  Delete the hunk under the cursor
``<LocalLeader>H``  Delete the uncommented hunks above the current one
==================  ====================================================

The same actions are available as the ``:B4DelHunk`` and
``:B4DelHunksBefore`` commands, and as ``<Plug>(B4DeleteHunk)`` /
``<Plug>(B4DeleteHunksBefore)`` if you prefer to bind your own keys.
Press ``u`` to undo. What you leave in the buffer is exactly what gets
sent.

By default only the mappings above leave a marker. If you would like the
same breadcrumb whenever you delete quoted lines with ordinary editing
commands — ``dd``, ``5dd``, ``dap``, a visual-mode ``d``, ``:d`` and so
on — opt in from your ``~/.vimrc``::

    let g:b4review_auto_marker = 1

Only the quoted diff (``>``) lines you delete are counted; your own notes
and ``|`` external comments are not, and a delete that removes no quoted
lines leaves no marker. This is off by default because reacting to every
edit can be surprising. Two global variables tune the feature:

``g:b4review_skipped_marker``
    Master switch, ``1`` by default. Set it to ``0`` to disable the whole
    skip-marker feature — the mappings, the commands and the auto-marker
    — leaving only the syntax highlighting and the comment adoption
    described below.

``g:b4review_auto_marker``
    ``0`` by default. Set it to ``1`` to also leave a marker when you
    delete quoted lines with ordinary editing commands, not just the
    mappings. Has no effect when the master switch is off, and can be
    overridden per-buffer with ``b:b4review_auto_marker``.

**Adopting reviewer comments (Vim)**

Comments from AI agents and other reviewers are loaded as read-only
``|``-quoted blocks, which b4 drops when it sends. To make one your own,
put the cursor anywhere in the block and press ``<LocalLeader>a`` (or run
``:B4Adopt``). The ``|`` prefix is stripped and the reviewer attribution
and ``via:`` provenance lines are removed, leaving just the comment text
in place under the same diff line, ready for you to edit. Once adopted it
is treated like any comment you wrote and is included when you send.

*Emacs*

Add this to your ``~/.emacs.d/init.el`` or ``~/.emacs``::

    (load "/path/to/b4/misc/emacs/b4-review-mode.el")

The mode is automatically activated for ``*.b4-review.eml`` files. It
provides the same trimming and adopting helpers as the Vim ftplugin:

===========  ====================================
Key          Action
===========  ====================================
``C-c C-k``  Delete the hunk under point
``C-c C-b``  Delete the uncommented hunks above
``C-c C-a``  Adopt the ``|`` comment as your own
===========  ====================================

These run the ``b4-review-delete-hunk``, ``b4-review-delete-hunks-before``
and ``b4-review-adopt-comment`` commands respectively. The trimming
commands leave a ``[ ... NN lines skipped ... ]`` marker where context was
removed; ``undo`` restores it.

As in Vim, only the trimming commands leave a marker by default. The same
two settings tune the feature, customizable with ``M-x customize-group
RET b4-review`` or set directly in your init file:

``b4-review-skipped-marker``
    Master switch, ``t`` by default. Set it to ``nil`` to disable the
    whole skip-marker feature — the commands and the auto-marker —
    leaving only syntax highlighting and comment adoption.

``b4-review-auto-marker``
    ``nil`` by default. Set it to ``t`` to also leave a marker when you
    delete quoted lines with ordinary editing commands — ``kill-region``,
    ``kill-whole-line``, evil ``dd`` and so on — not just the commands.
    (Requires the master switch to be on.)

**Per-patch states**

Each patch in the series can be marked with a state to help you track
your review progress:

==========  ======  ======  =======================================================
State       Marker  Key     Meaning
==========  ======  ======  =======================================================
*(none)*    |sp|    —       Not yet reviewed
Unchanged   ``≡``   —       Patch identical to prior revision (auto, on upgrade)
External    ``±``   —       External reviewers have inline comments (auto)
Draft       ``✎``   —       You have comments, a reply, or a nack trailer (auto)
Done        ``✓``   ``d``   Review complete, include in outgoing emails (also auto)
Skip        ``✕``   ``x``   Intentionally skipped, exclude from outgoing emails
==========  ======  ======  =======================================================

.. |sp| unicode:: U+0020

The marker column shows the highest-priority state that applies. For
example, if external reviewers have commented but you have not yet
reviewed, the marker shows ``±``; once you add your own comments it
changes to ``✎``.  The ``≡`` marker is set automatically when upgrading
a series to a newer revision — it indicates that the patch content has
not changed, giving you a visual cue to focus on patches that did.  Any
review action (adding a trailer, reply, or comment) supersedes it.

If you had already reviewed some of the patches on the list *before*
tracking the series in ``b4 review`` — by replying with a ``Reviewed-by``
or ``Acked-by`` from your own address — those patches are detected from
the thread follow-ups and marked ``✓`` Done automatically, so you can see
at a glance what is left to look at.  This is a low-priority cue: it is
derived fresh each time the series loads and is never re-sent, and any
manual action (skip, an in-progress comment or reply, or a ``Nacked-by``
you sent) takes precedence over it.

Pressing ``d`` or ``x`` toggles the state for the current patch. The
patch list on the left shows the state visually: done patches appear in
bold, skipped and unchanged patches appear dimmed. Press ``H`` to hide
skipped patches from the list entirely — useful for large series where
many patches are irrelevant to your review.

Skipped patches are automatically excluded when sending review emails.
When taking patches via cherry-pick, skipped patches are pre-deselected
in the patch selection dialog.

**Follow-up messages**

Press ``f`` to load and display follow-up messages for the series being
reviewed. B4 first checks the locally cached thread (stored as a git
blob in the review branch); if no local copy exists, it fetches from
lore in the background. The ``f`` key is a toggle — pressing it again
hides the follow-up display.

Follow-up messages appear as coloured panels below the diff, showing
threading depth (replies indented visually), along with From, Date, and
Message-ID headers for easy cross-referencing with your mail client.
Each panel shows the reviewer's full name in the title.

When follow-ups contain inline diff review comments (quoted diff lines
with unquoted reviewer comments), b4 extracts and resolves them against
the real diff so they display at the correct position inline, alongside
your own comments. This works for any follow-up that uses the standard
mailing-list code review format.

You can click a follow-up panel header (marked with ↩) to compose a
quick reply directly from the review interface. B4 opens ``$EDITOR``
with the quoted message and constructs a proper reply-to-all email with
correct threading headers.

Patchwork integration
~~~~~~~~~~~~~~~~~~~~~
If your project uses a Patchwork server (see :ref:`patchwork_settings`),
press ``p`` in the tracking list to open the Patchwork browser. From
there you can:

* Browse series from your Patchwork project
* View a series thread (``Enter``)
* View CI check details for a series (``c``)
* Track a series directly from the Patchwork list (``t``)
* Set or change the Patchwork state for a series (``s``)
* Hide and unhide series (``h``/``u``)
* Toggle display of hidden series (``H``)
* Refresh the series list from Patchwork (``r``)
* Filter the list with a mutt-style pattern (``l``)
* Show keybinding help (``?``)

Each series in the listing shows a coloured CI status indicator when
Patchwork has check results: green for pass, red for fail or warning,
and dim for pending. Press ``c`` to open a detailed view of all CI
checks grouped by patch, showing the check context, description, and a
link to the full CI results.

Taking action
-------------

Upgrading to a newer revision
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Press ``u`` to update the selected series (or ``U`` for all series) and
discover new revisions. Updating only *records* what it finds — it
never changes a series' status behind your back. When a newer revision
is known, b4 lets you know in several ways: the row gets an ``↑``
suffix flag in the tracking list, the Revisions line in the details
panel shows *(vN available — upgrade with [a]ction)*, a ``waiting``
series surfaces back into the Active group, and the ``up:`` limit
token can filter the list down to just the upgradable series.

Upgrading itself is always an explicit action: open the action menu
(``a``) and select **Upgrade**. Availability depends only on a newer
revision being known — the series can be in reviewing, replied,
partial, waiting, or even new. For a series with a review branch, b4
will:

1. Save your review comments on each patch, keyed by stable patch-id.
2. Archive the current revision (creating a ``.tar.gz`` backup).
3. Fetch and check out the new revision.
4. Restore your comments onto patches whose content did not change.

The upgraded series lands back in ``reviewing``, so upgrading a
``waiting`` series returns it to active review in one step. A series
that never had a review branch created (for example, one marked
waiting straight from ``new``) simply switches its recorded revision
without touching git.

Comments on patches that were modified between revisions are not carried
over, since those patches need fresh review. Cover letter reviews are
also not carried over, as they are specific to the previous revision.

.. _review_link_revision:

Linking a revision manually
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: v0.16

B4 discovers revisions automatically using the series change-id and
in-reply-to threading. When that fails — for example, a submitter sent
v2 to a different mailing list, used a new cover letter without proper
threading, or forgot to reply-to the v1 cover — you can link a revision
by hand.

Open the action menu (``a``) and select **Link a revision** (``l``).
B4 prompts for a message-id or lore URL, then fetches the corresponding
series in the background. A confirmation screen shows the revision number,
subject, patch count, and sender so you can verify it is the right posting
before committing.

If the fetched series has the same revision number as one already
recorded, the confirmation screen warns of the collision. If it matches
a series already tracked under a different change-id (a stray duplicate),
b4 warns that the duplicate will be absorbed and the standalone tracking
entry removed.

Press ``Ctrl-y`` to record the link, or ``Escape`` to cancel without
making any changes.

Applying patches (take)
~~~~~~~~~~~~~~~~~~~~~~~
Open the action menu (``a``) and select **Take** to apply a reviewed series.
The take flow has three steps:

**Step 1 — Choose method and options.** B4 offers three take methods:

* **Merge** — creates a merge commit using the cover letter as the
  commit message template. Per-commit ``Signed-off-by`` and ``Link:``
  trailers are applied to each patch individually.
* **Linear** (git-am) — applies patches linearly with ``git am``.
* **Cherry-pick** — cherry-picks individual patches (you can select
  which ones in the next step). Skipped patches are pre-deselected.

Unless :term:`b4.review-default-take-method` is set, the method is
pre-selected heuristically: ``merge`` for multi-patch series with a
cover letter, ``linear`` otherwise. When the author sent no cover
letter, the dialog shows a *No cover letter provided by the author*
note, since the merge method would have no cover letter to build the
merge commit message from.

The dialog suggests recently used target branches, with the configured
:term:`b4.review-target-branch` always included. You can also type a
branch name directly. Toggle the ``Signed-off-by`` and ``Link:``
checkboxes to add those trailers to each commit. The *thank and
archive on delivery* checkbox chains the take into the thank-you and
archive steps (see :ref:`thank and archive in one motion
<review_take_thank_archive>`); set
:term:`b4.review-thank-and-archive` to make it start checked. Press
``Ctrl-y`` to continue, or ``Escape`` to cancel.

**Step 2 — Select patches** (cherry-pick only). When cherry-pick is
selected, a patch selection dialog lets you choose which patches to
apply. Skipped patches are pre-deselected. Press ``Ctrl-y`` to
continue.

**Step 3 — Confirm.** A confirmation screen runs a test apply in the
background (using a temporary sparse worktree) and shows the result:
green for clean, red with details if it fails. The screen also shows
the series subject, method, and target branch as a final sanity check.
Toggle ``Mark as accepted`` to update the series status after the take.
Press ``Ctrl-y`` to proceed with the actual take, or ``Escape`` to back
out without making any changes.

**Partial takes (cherry-pick only)**. When you cherry-pick only a subset
of the series' patches, b4 computes coverage and sets the status to
``partial`` (◐) rather than ``accepted``. The review branch stays alive,
the series remains in the active working set, and new revisions are
ingested as normal. When you later take the remaining patches — completing
coverage — b4 automatically promotes the status from ``partial`` to
``accepted``, at which point the thank/archive flow becomes available.

.. _review_take_thank_archive:

**Thank and archive in one motion**

When the *thank and archive on delivery* checkbox is enabled and the
take leaves the series fully ``accepted``, the thank-you preview opens
automatically after the take completes. From there the flow is the
usual one — **Send** (``Ctrl-y``) or **Queue** (``W``) — and a successful
delivery also archives the series:

* a directly sent thank-you archives the series immediately;
* a queued thank-you archives it when the message is actually
  delivered — on the next queue delivery from the TUI (``T`` → ``T``)
  or by ``b4 review cron``. The intent is recorded as an
  ``X-B4-Archive-After-Send`` header in the queue file, alongside the
  ``X-Check-*`` headers.

The archive is skipped — leaving the series ``thanked`` — when it is no
longer clearly safe: a newer revision of the series has appeared in
the meantime (use **Upgrade to newer revision** from the action menu
to start reviewing it), or the series status changed after the message
was queued (for example, you returned it to reviewing). Cancelling the
thank-you preview with ``Escape`` aborts the chain and the series
simply stays ``accepted``.

Partial takes never chain: the thank-you and archive steps stay manual
while patches remain untaken.

.. _review_conflict_resolution:

Conflict resolution
~~~~~~~~~~~~~~~~~~~~
Several TUI operations apply patches and can encounter conflicts:

* **Checking out a series for review** (``r`` from the tracking list)
* **Upgrading to a newer revision** (Upgrade from the action menu)
* **Taking patches** (Take from the action menu)
* **Rebasing** (Rebase from the action menu)

The first three use ``git am`` with three-way merge (``git am -3``),
which resolves many conflicts automatically. When the patches come
from lore and the referenced blobs are not already in your repository,
b4 creates a temporary fake commit range so the three-way merge can
find them. If the blobs are already present in your tree, this step is
skipped.

When a conflict does occur, b4 suspends the TUI and drops you into an
interactive shell so you can resolve it. The workflow depends on the
operation:

**For checkout and upgrade** — the shell opens inside the temporary
worktree where the conflict happened:

* Inspect the conflict with ``git diff`` or ``git status``
* Edit the affected files to resolve the conflict
* Stage your fixes with ``git add``
* Run ``git am --continue`` to finish applying
* Type ``exit`` to return to the TUI

If you can't resolve the conflict, run ``git am --abort`` and then
``exit``. B4 cleans up the worktree and returns you to the TUI without
making any changes to your branch.

**For take (any method)** — patches are applied in whichever worktree
has the target branch checked out, or in a throwaway worktree when the
branch is not checked out anywhere; the checkout you are running the
TUI from is never touched. The conflict shell opens in that worktree.
With the linear or cherry-pick method, resolve and finish with ``git
am --continue`` (or back out with ``git am --abort``). With the merge
method, the conflicted merge is left in progress: resolve, stage with
``git add``, and run ``git merge --continue`` (or back out with ``git
merge --abort``).

If you leave the shell without finishing either way, b4 aborts the
in-progress operation so a real checkout is never left mid-conflict; a
throwaway worktree is instead kept so you can finish by hand. A take
also refuses to start when the target worktree already has an
operation in progress or unmerged files — from an earlier abandoned
resolution or your own unfinished work — and tells you what to clean
up first rather than discarding it.

**For rebase** — the shell opens in your repository directory with a
``git rebase`` in progress:

* Inspect the conflict with ``git diff`` or ``git status``
* Edit the affected files to resolve the conflict
* Stage your fixes with ``git add``
* Run ``git rebase --continue`` to move to the next patch
* Repeat if further patches conflict
* Type ``exit`` to return to the TUI

If you can't resolve the conflict, run ``git rebase --abort`` and then
``exit``. B4 returns you to the TUI without making any changes.

Rebasing
~~~~~~~~
Open the action menu (``a``) and select **Rebase** to rebase the review
branch onto a target branch. The dialog suggests recently used branches
(same as the Take dialog). Press ``Ctrl-y`` to confirm, or ``Escape``
to cancel. B4 first tests applicability in a temporary worktree before
performing the actual rebase.

Range-diff
~~~~~~~~~~
Press ``d`` to compare two revisions of the series using ``git
range-diff``. B4 fetches the comparison revision from lore if needed.

Thank-you
~~~~~~~~~
Open the action menu (``a``) on an accepted **or partial** series and
select **Thank** to compose and send a thank-you note to the contributor.
Sending a thank-you for a ``partial`` series acknowledges the patches
that have already landed without affecting the review of the remaining
patches. A preview
screen shows the generated message with keybindings to **Send**
(``Ctrl-y``), **Edit** (``e``), or **Cancel** (``Escape``).

.. _thanks_queue:

**Queuing thanks for delayed delivery**

When :term:`b4.thanks-commit-url-mask` is configured, the thank-you
preview also offers a **Queue** option (``W``). Queuing writes the
message as an RFC 2822 file in ``.git/b4-review/queue/`` instead of
sending it immediately. This is useful when you want to push your
commits to a public tree before sending thank-you messages — the
message references commit URLs that may not resolve until the push is
complete.

Files are named ``{change-id}-v{revision}.msg`` and the check target is
recorded as ``X-Check-URL``, ``X-Check-Commit``, ``X-Check-Repo`` and
``X-Check-Branch`` headers, so you can inspect or hand-edit queued
messages with any mail tool.

The series remains in ``accepted`` status while its thank-you message is
queued. The title bar shows a queue count indicator on the right side
(e.g. ``3 queued``) and the ``T`` keybinding appears in the footer.

Press ``T`` to open the queue viewer, which lists all pending messages
with their subjects and target URLs. From this screen, press ``T``
again to attempt delivery: b4 verifies that each queued message's
commit is reachable from the branch named in the thank-you message
(``git ls-remote`` against the per-remote ``b4-check-repo`` setting,
:term:`b4.thanks-check-repo`, the URL of the remote the target branch
tracks, or a repository URL derived from the commit URL — in that
order), and delivers the message via SMTP once the commit is pushed.
Reachability matters because hosts with shared object storage (such as
git.kernel.org and github) can serve commit pages for objects that
were pushed to a sibling repository but never published in the checked
one. When no repository URL is available, b4 falls back to checking
the commit URL with an HTTP HEAD request. Successfully delivered
messages are moved to ``.git/b4-review/queue/sent/`` and their series
are automatically marked as ``thanked``.

When running with ``--email-dry-run``, queued messages are written to
``.git/b4-review/queue/dryrun/`` and the commit URL check is skipped,
allowing end-to-end testing of the queue workflow without sending real
email.

Archive and abandon
~~~~~~~~~~~~~~~~~~~
Open the action menu (``a``) and select **Archive** to archive a
completed series (creates a ``.tar.gz`` in
``$XDG_DATA_HOME/b4/review-archived/``). Select **Abandon** to delete
the series and review branch entirely.

.. _snooze_details:

Snoozing a series
~~~~~~~~~~~~~~~~~
When you want to defer a series — perhaps waiting for a release
candidate or simply clearing your queue — open the action menu (``a``)
and select **Snooze**. The snooze dialog offers three modes:

* **Duration** — relative time like ``2w`` (2 weeks), ``30d`` (30 days),
  or ``3m`` (3 months).
* **Date** — an absolute date in ``YYYY-MM-DD`` format.
* **Tag** — a git tag name such as ``v6.15-rc3``. The series wakes up
  when that tag appears in the repository.

Snoozed series move to a separate **Snoozed** group in the tracking
list and are skipped during ``U`` (update-all). The review branch is
preserved so you can pick up exactly where you left off.

When the snooze condition is met — the date passes or the tag appears
— the series automatically wakes up and returns to its previous status
the next time the TUI loads. You can also wake a series manually via
the action menu (**Unsnooze**).

The snooze dialog remembers your last choice within a session, so if
you are snoozing several series with the same settings the fields are
pre-populated.

Working across multiple machines
--------------------------------

Because review state is stored in ``b4/review/*`` git branches, it can
be synchronised between machines using any git remote. This makes it
straightforward to start reviewing a series on one machine and continue
on another, or to keep a laptop and a workstation in sync.

Setting up a sync remote
~~~~~~~~~~~~~~~~~~~~~~~~~

Configure a dedicated remote pointing to a repository accessible from
all your machines::

    git remote add review-sync git@example.com:linux-review.git

Then configure push and fetch refspecs so that only ``b4/review/*``
branches are transferred::

    git config --add remote.review-sync.push  '+refs/heads/b4/review/*:refs/heads/b4/review/*'
    git config --add remote.review-sync.fetch  'refs/heads/b4/review/*:refs/heads/b4/review/*'

The ``+`` prefix in the push refspec enables force-push, which is
required because the tracking commit at the tip of each review branch is
replaced in-place whenever the series status changes.

.. warning::

   Review branches may contain unpublished comments, draft responses, or
   early-stage review notes that you would not want to share publicly.
   Make sure the remote repository is not publicly accessible if you want
   to keep your in-progress review work private.

Syncing your review branches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After a review session, push all your review branches in one command::

    git push review-sync

Add ``--prune`` to also remove remote branches that you have abandoned
or archived locally::

    git push --prune review-sync

On another machine, fetch the latest branches::

    git fetch review-sync

Then launch the TUI as normal — a rescan runs automatically in the
background and detects any branches whose HEAD commit changed since the
last fetch, updating the tracking database to reflect their current
state::

    b4 review tui

.. note::

   The TUI compares the HEAD commit SHA of each ``b4/review/*`` branch
   against a cached value in the database. Only branches that have
   actually changed are re-read, so the background rescan adds no
   perceptible delay even in large repositories.

.. _review_cron:

Non-interactive maintenance (b4 review cron)
--------------------------------------------
Everything the TUI does periodically can also run from a scheduler or
a script, without the TUI open::

    b4 review cron

With no flags, this runs the full maintenance sweep:

* wake up snoozed series whose date has passed or whose target tag now
  exists;
* rescan local review branches (same as the pre-update rescan in the
  TUI);
* fetch threads and update revisions/trailers for every tracked series
  (same as ``U`` in the TUI);
* deliver queued thank-you messages whose commits are now published
  (same as ``T`` → ``T`` in the TUI; see :ref:`the thanks queue
  <thanks_queue>`).

Pass ``--update`` or ``--deliver-queue`` to run only one of the two
task groups. ``--dry-run`` skips the update entirely and reports what
the queue delivery *would* do — including the publication check — which
is handy for verifying that b4 detects your pushes::

    b4 review cron --deliver-queue --dry-run

Unlike the TUI, cron does not patatt-sign the thank-you messages it
delivers: timer processes usually have no access to agents or
passphrase-protected keys, and a signature adds little to a thank-you
note. If your signing setup works headless (e.g. a plain ed25519
patatt key), pass ``--sign`` to restore the TUI behaviour.

Cron is quiet when there is nothing to report, so a scheduler that
mails command output (like cron itself) only bothers you when
something actually happened. The progress narration you would see
when running the same tasks interactively is suppressed; only
summaries, deliveries and errors are printed. Messages held back by
the publication check do not count as news; use ``--dry-run`` to see
them, and ``b4 -d review cron`` to see the full narration.

Concurrency is safe by design: the update sweep and the queue delivery
each hold a lock shared with the TUI, so a cron run overlapping an
interactive session (or another cron run) skips the busy task with a
note and exits 0. You never need to worry about a timer firing while
you are in the TUI — the worst case is a "not delivered twice" outcome,
never a "delivered twice" one.

Project selection and repositories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run from within an enrolled repository, cron works on that project,
like the TUI. Run from anywhere else — which is where schedulers
start — it sweeps *every* known project, so timers require no
working-directory setup. To pick specific projects regardless of the
current directory, pass ``-i`` (repeatable)::

    b4 review cron -i linux-subsystemA -i linux-subsystemB

B4 finds each project's repository through a recorded path: it is
saved when you enroll, and refreshed whenever ``b4 review tui`` or
``b4 review cron`` runs from within the repository. Projects enrolled
with an older b4 need one such run before cron can find their
repository; until then they get a database-only update and a warning
that queue delivery was skipped.

Repository-local git configuration (``b4.*``, ``user.*`` and
``sendemail.*`` settings, and the toplevel ``.b4-config`` file) is
reloaded for each project as the sweep enters it, so per-repository
settings like :term:`b4.thanks-check-repo` or a per-project sendemail
identity work the same as when running from within the repository.

Scheduling with a systemd timer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create ``~/.config/systemd/user/b4-review-cron.service``:

.. code-block:: ini

    [Unit]
    Description=b4 review periodic maintenance

    [Service]
    Type=oneshot
    # Sweeps all enrolled projects; add -i identifier to limit
    ExecStart=/usr/bin/b4 review cron

and ``~/.config/systemd/user/b4-review-cron.timer``:

.. code-block:: ini

    [Unit]
    Description=Run b4 review maintenance every 30 minutes

    [Timer]
    OnBootSec=5min
    OnUnitActiveSec=30min

    [Install]
    WantedBy=timers.target

Then enable it::

    systemctl --user daemon-reload
    systemctl --user enable --now b4-review-cron.timer

Check the output of past runs with::

    journalctl --user -u b4-review-cron.service

A classic crontab entry works just as well::

    */30 * * * * b4 review cron

Kicking the queue after a push
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Git has no client-side post-push hook, so the usual way to deliver
queued thanks right after pushing is a wrapper or alias::

    git push korg && b4 review cron --deliver-queue

Since this runs from within the repository you just pushed, it
delivers that project's queue only.

If your push lands on the public replica with a delay, messages that
are not yet visible simply stay queued — the next cron sweep delivers
them once the publication check passes.

Optional flags
--------------

``b4 review enroll``
~~~~~~~~~~~~~~~~~~~~
``repo_path``
  Path to the git repository to enrol (default: current directory).

``-i IDENTIFIER, --identifier IDENTIFIER``
  Project identifier (default: repository directory name).

``b4 review track``
~~~~~~~~~~~~~~~~~~~
``series_id``
  Series identifier: a message-id, URL, or change-id. Alternatively,
  pipe a message on stdin.

``-i IDENTIFIER, --identifier IDENTIFIER``
  Project identifier (required if not in an enrolled repository).

``b4 review tui``
~~~~~~~~~~~~~~~~~
``-i IDENTIFIER, --identifier IDENTIFIER``
  Project identifier (required if not in an enrolled repository).

``--email-dry-run``
  Show all email dialogs and perform status transitions as normal, but
  print messages to stdout instead of sending them. Useful for testing
  the full review workflow without actually delivering emails.

``--no-sign``
  Do not patatt-sign outgoing review emails. By default, review replies,
  follow-up replies, and thank-you messages are signed with your
  configured attestation key (same as ``b4 send``). This flag disables
  signing for the current session. Can also be set permanently via
  :term:`b4.review-no-patatt-sign` (see :ref:`review_settings`).

``--no-mouse``
  Disable mouse support in the TUI. This allows the terminal's native
  text selection to work normally. Can also be set permanently via
  :term:`b4.review-tui-disable-mouse` (see :ref:`review_settings`).

``b4 review show-info``
~~~~~~~~~~~~~~~~~~~~~~~
Displays review branch metadata in a format suitable for scripting
and CI integration. The output includes change-id, revision, status,
commit SHAs, and other tracking data.

``PARAM``
  Optional parameter in the form ``[branch:]key``. The parameter can
  be one of the following:

  - **key name** to show just a specific value from the current branch
  - **branch name** to show all info about a specific branch
  - **branch name:key name** to show a specific value from a specific
    branch

  Branch names can be given in full (``b4/review/change-id``) or as a
  shorthand (``change-id``), which is automatically expanded to
  ``b4/review/change-id``.

  For example, to show the ``base-commit`` of a specific review
  branch::

      b4 review show-info b4/review/20250310-foo-bar:base-commit

  Or using the shorthand::

      b4 review show-info 20250310-foo-bar:base-commit

  To show all values for a specific branch::

      b4 review show-info b4/review/20250310-foo-bar

  To show the ``series-range`` for the current branch::

      b4 review show-info :series-range

  To show all values for the current branch::

      b4 review show-info

``-l, --list``
  List all review branches with a summary of each (branch name,
  change-id, status, subject, sender, revision, patch count, and
  completeness).

  ::

      b4 review show-info --list

``-j, --json``
  Output in JSON format. Can be combined with ``--list`` to produce a
  JSON array of all branches, or used alone for a single branch::

      b4 review show-info --json
      b4 review show-info --list --json

``b4 review cron``
~~~~~~~~~~~~~~~~~~
Runs periodic review maintenance non-interactively (see
:ref:`review_cron`). With no task flags, runs everything.

``-i IDENTIFIER, --identifier IDENTIFIER``
  Limit the run to this project identifier; may be given multiple
  times (default: the enrolled repository containing the current
  directory, or all known projects when not inside one).

``--update``
  Rescan branches, update all tracked series, and wake expired
  snoozes.

``--deliver-queue``
  Deliver queued thank-you messages whose commits are now published.

``--dry-run``
  Only report what would be delivered from the queue; skip the series
  update.

``--sign``
  Patatt-sign outgoing thank-you messages. Unlike the TUI, cron does
  not sign by default, because signing keys are usually not available
  to timer processes.

The available keys are:

========================  ================================================
Key                       Description
========================  ================================================
``branch``                Full branch name
``change-id``             Series change-id
``revision``              Current revision number
``status``                Review status (new, reviewing, accepted, etc.)
``identifier``            Project identifier
``subject``               Series subject line
``sender``                Author in ``Name <email>`` format
``link``                  Link to the series on the mailing list
``base-commit``           Base commit SHA
``first-patch-commit``    SHA of the first patch commit
``series-range``          Commit range of the patch commits
``num-patches``           Number of patch commits
``num-prereqs``           Number of prerequisite commits
``complete``              Whether all expected patches were received
``target-branch``         Per-series target branch (or config default)
``commit-HASH``           Subject of each patch commit (dynamic keys)
========================  ================================================

.. versionadded:: v0.15

.. _ci_check_protocol:

CI check integration
--------------------

Both the tracking list (``c``) and the review app (``c``) support
running CI checks on a series. Results are displayed in a matrix modal showing each
patch as a row and each check tool as a column, with colour-coded
status indicators. Press ``Enter`` on a row to drill into detailed
results, or ``R`` to force a re-run that bypasses the cache.

Check results are cached in a SQLite database
(``$XDG_DATA_HOME/b4/review/ci.sqlite3``) keyed by message-id and
tool name, so re-running checks shows cached results instantly.

Built-in handlers
~~~~~~~~~~~~~~~~~

B4 includes two built-in check handlers that require no external
scripts:

``_builtin_checkpatch``
  Runs ``scripts/checkpatch.pl`` from your source tree against each
  patch. Auto-detected when no :term:`b4.review-perpatch-check-cmd` is
  configured and ``scripts/checkpatch.pl`` exists and is executable.
  When a complaint is about a line in the commit message, the detail
  view echoes the offending line underneath it, the way checkpatch does
  on the command line, so you don't have to go hunting for it.

``_builtin_patchwork``
  Queries the Patchwork REST API for CI check results on each patch.
  Auto-enabled when :term:`b4.pw-url` and :term:`b4.pw-project` are
  configured. Individual check contexts (e.g. ``tree_selection``,
  ``build_32bit``) are aggregated into a single ``patchwork`` column
  showing the worst-case status; the detail view lists each check
  individually with coloured status indicators.

Writing an external check script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

External check commands receive a single RFC 2822 email message on
stdin (one patch for per-patch commands, or the cover letter for
per-series commands) and must print a JSON array to stdout::

    [
      {
        "tool": "my-ci",
        "status": "pass",
        "summary": "Build succeeded",
        "url": "https://ci.example.com/builds/12345",
        "details": "Optional multi-line details"
      }
    ]

Each object in the array represents one check result:

============  ==========  ================================================
Field         Required    Description
============  ==========  ================================================
``tool``      yes         Column header name in the results matrix
``status``    yes         One of ``pass``, ``warn``, ``fail``
``summary``   no          One-line summary shown in the detail view
``url``       no          Link for the maintainer to open in a browser
``details``   no          Multi-line text for the scrollable detail view
============  ==========  ================================================

An empty array ``[]`` means the tool has nothing to report for this
message.

A non-zero exit code with no JSON output on stdout is treated as an
execution error and displayed as a failure in the results matrix.

A script can return multiple result objects to report results from
several tools in one invocation.

Environment variables
^^^^^^^^^^^^^^^^^^^^^

The following environment variables are set when an external check
command runs:

``B4_TRACKING_FILE``
  Path to a temporary JSON file containing the full tracking data for
  the series being checked. This includes revision info, patch
  metadata, take history, branch tips, and any other state recorded by
  b4. The file is removed after checks complete. Not set if the series
  has no tracking data (e.g. not yet checked out).

  Scripts can query this file with ``jq`` or parse it in Python to
  extract whatever context they need — for example, looking up branch
  tip commits for KernelCI queries::

      import json, os
      tracking = json.load(open(os.environ['B4_TRACKING_FILE']))
      tips = tracking.get('series', {}).get('branch-tips', [])
      for tip in tips:
          # tip has 'date', 'branch', 'sha'
          query_kernelci(tip['sha'], tip['branch'])

Example configuration
~~~~~~~~~~~~~~~~~~~~~

::

    [b4]
        # Built-in checkpatch (auto-detected, but can be set explicitly)
        review-perpatch-check-cmd = _builtin_checkpatch

        # Custom per-patch check
        review-perpatch-check-cmd = /path/to/my-perpatch-check.py

        # Per-series check (cover letter piped to stdin)
        review-series-check-cmd = /path/to/my-series-check.py

An example script demonstrating the JSON protocol is included in the
b4 source tree at ``misc/review-ci-example.py``. It produces random
pass/warn/fail results and can be used for testing and as a starting
point for your own check scripts.

Configuration
-------------
The following configuration options are specific to ``b4 review``. Set
them in your git config under the ``[b4]`` section.

See :ref:`review_settings` for the full reference.

Patchwork integration reuses the standard patchwork settings
(:term:`b4.pw-url`, :term:`b4.pw-key`, :term:`b4.pw-project`). See
:ref:`patchwork_settings` for details.

Configuring the review agent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``a`` keybinding in the review interface invokes an external command
that can perform AI-assisted review of the current patch. To enable it,
set both :term:`b4.review-agent-command` and
:term:`b4.review-agent-prompt-path` in your git config. For example,
to use Claude Code with access to the repository::

    [b4]
      review-agent-command = claude --add-dir .git --allowedTools 'Bash(git:*) Read Glob Grep Write(.git/b4-review/**) Edit(.git/b4-review/**)' --
      review-agent-prompt-path = .git/agent-reviewer.md

To use Gemini CLI instead::

    [b4]
      review-agent-command = gemini --sandbox --allowed-tools 'Bash(git:*) Read Glob Grep Write(.git/b4-review/**) Edit(.git/b4-review/**)'
      review-agent-prompt-path = .git/agent-reviewer.md

To use OpenAI Codex CLI::

    [b4]
      review-agent-command = codex --sandbox workspace-write
      review-agent-prompt-path = .git/agent-reviewer.md

To use GitHub Copilot CLI::

    [b4]
      review-agent-command = copilot --autopilot --yolo
      review-agent-prompt-path = .git/agent-reviewer.md

.. note::

   Only **Claude Code** and **OpenAI Codex CLI** have been directly
   tested. The command-line examples for Gemini CLI and GitHub Copilot
   CLI are best-effort suggestions. If you get another tool working
   (or find corrections for the above), please send your findings to
   tools@kernel.org.

The command is run from the repository top-level directory. The prompt
file should contain instructions telling the agent how to review patches
for your project. A sample prompt is included in
``misc/agent-reviewer.md`` in the b4 source tree — copy it into your
repository and adapt it to your project's coding standards and review
guidelines.

.. _customising_theme:

Customising the colour theme
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The review TUI uses Textual's built-in theming system. Textual ships
with many themes — to browse them, press ``ctrl+p`` inside the TUI to
open the command palette, then select **Theme**. Once you find
one you like, make it permanent by setting the ``TEXTUAL_THEME``
environment variable::

    export TEXTUAL_THEME=monokai

A few examples worth trying::

    TEXTUAL_THEME=textual-dark b4 review tui
    TEXTUAL_THEME=dracula b4 review tui
    TEXTUAL_THEME=textual-ansi b4 review tui

The ``textual-ansi`` theme restricts rendering to the standard 16 ANSI
colours, which makes it suitable for terminals that do not support 256
or true-colour output. All UI elements — diff highlighting, reviewer
badges, CI indicators, and comment panels — adapt automatically to the
active theme.

Disabling colour entirely
~~~~~~~~~~~~~~~~~~~~~~~~~~
If you prefer no colour at all — for example, when piping output to a file
or when colours interfere with a screen reader — you can set the
``NO_COLOR`` environment variable (see `no-color.org <https://no-color.org>`_
for the broader convention)::

    NO_COLOR=1 b4 review tui

This tells Textual to strip all colour information from the rendered
output while keeping the layout intact.

Disabling scroll animations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, Textual animates page-up/page-down and other scroll
operations. To disable all animations::

    TEXTUAL_ANIMATIONS=none b4 review tui

Other accepted values are ``basic`` (reduced animations) and ``full``
(the default). To disable smooth scrolling specifically without
affecting other animations::

    TEXTUAL_SMOOTH_SCROLL=0 b4 review tui

Export either variable in your shell profile to make the change
permanent.
