From ef57fc77f3c939d55d51322d428e40a5a3c9ed8e Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Sun, 10 Jan 2021 03:01:42 +0000
Subject: CTAN sync 202101100301

---
 macros/latex-dev/base/lthooks.dtx | 906 +++++++++++++++++++++++++++-----------
 1 file changed, 659 insertions(+), 247 deletions(-)

(limited to 'macros/latex-dev/base/lthooks.dtx')

diff --git a/macros/latex-dev/base/lthooks.dtx b/macros/latex-dev/base/lthooks.dtx
index e4ff7dd2ed..404938d203 100644
--- a/macros/latex-dev/base/lthooks.dtx
+++ b/macros/latex-dev/base/lthooks.dtx
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %%
-%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach,
+%% File: lthooks.dtx (C) Copyright 2020,2021 Frank Mittelbach,
 %%                                      Phelype Oleinik & LaTeX Team
 %
 % This file is part of the LaTeX base system.
@@ -31,8 +31,8 @@
 %%% From File: lthooks.dtx
 %
 %    \begin{macrocode}
-\def\lthooksversion{v1.0e}
-\def\lthooksdate{2020/10/23}
+\def\lthooksversion{v1.0h}
+\def\lthooksdate{2021/01/07}
 %    \end{macrocode}
 %
 %<*driver>
@@ -222,11 +222,13 @@
 %   \begin{syntax}
 %     \cs{AddToHook} \Arg{hook}\oarg{label}\Arg{code}
 %   \end{syntax}
-%    Adds \meta{code} to the \meta{hook} labeled by \meta{label}. If
-%    the optional argument \meta{label} is not provided, if \cs{AddToHook}
-%    is used in a package/class, then the current
-%    package/class name is used, otherwise \hook{top-level} is
-%    used~(see section~\ref{sec:default-label}).
+%    Adds \meta{code} to the \meta{hook} labeled by \meta{label}.
+%    When the optional argument \meta{label} is not provided, the
+%    \meta{default label} is used (see section~\ref{sec:default-label}).
+%    If \cs{AddToHook} is used in a package/class, the
+%    \meta{default label} is the package/class name, otherwise it is
+%    \hook{top-level} (the \hook{top-level} label is treated
+%    differently:  see section~\ref{sec:top-level}).
 %
 %    If there already exists code under the \meta{label} then the new
 %    \meta{code} is appended to the existing one (even if this is a reversed hook).
@@ -234,10 +236,10 @@
 %    \meta{label}, first apply \cs{RemoveFromHook}.
 %
 %    The hook doesn't have to exist for code to be added to
-%    it. However, if it is not declared later then obviously the
+%    it. However, if it is not declared, then obviously the
 %    added \meta{code} will never be executed.  This
 %    allows for hooks to work regardless of package loading order and
-%    enables packages to add to hook of other packages without
+%    enables packages to add to hooks from other packages without
 %    worrying whether they are actually used in the current document.
 %    See section~\ref{sec:querying}.
 %
@@ -251,10 +253,8 @@
 %     \cs{RemoveFromHook} \Arg{hook}\oarg{label}
 %   \end{syntax}
 %    Removes any code labeled by \meta{label} from the \meta{hook}.
-%    If the optional
-%    argument \meta{label} is not provided, if \cs{AddToHook}
-%    is used in a package/class, then the current
-%    package/class name is used, otherwise \hook{top-level} is used.
+%    When the optional argument \meta{label} is not provided, the
+%    \meta{default label} is used (see section~\ref{sec:default-label}).
 %
 %    If the code for that \meta{label} wasn't yet added to the
 %    \meta{hook}, an order is set so that when some code attempts to add
@@ -273,7 +273,7 @@
 % \medskip
 %
 % In contrast to the \texttt{voids} relationship between two labels
-% in a \cs{DeclareHookrule} this is a destructive operation as the
+% in a \cs{DeclareHookRule} this is a destructive operation as the
 % labeled code is removed from the hook data structure, whereas the
 % relationship setting can be undone by providing a different
 % relationship later.
@@ -367,7 +367,8 @@
 % token, a low-level \TeX{} error is raised (namely, the \meta{hook} is
 % expanded using \TeX's \cs{csname}\ldots\cs{endcsname}, as such,
 % Unicode characters are allowed in \meta{hook} and \meta{label}
-% arguments).  The arguments of \cs{UseHook} and \cs{UseOneTimeHook} are
+% arguments).  The arguments of \cs{UseHook}, \cs{UseOneTimeHook},
+% \cs{IfHookEmptyTF}, and \cs{IfHookExistsTF} are
 % processed much in the same way except that spaces are not trimmed
 % around the argument, for better performance.
 %
@@ -377,10 +378,10 @@
 % chunk and to prevent clashes.  This should be the standard practice,
 % so this hook management code provides a shortcut to refer to the
 % current package in the name of a \meta{hook} and in a \meta{label}.
-% If \meta{hook} name or \meta{label} consist just of a single dot
+% If the \meta{hook} name or the \meta{label} consist just of a single dot
 % (|.|), or starts with a dot followed by a slash (|./|) then the dot
 % denotes the \meta{default label} (usually the current package or class
-% name---see~\cs{DeclareDefaultHookLabel}).
+% name---see~\cs{SetDefaultHookLabel}).
 % A \enquote{|.|} or \enquote{|./|} anywhere else in a \meta{hook} or in
 % \meta{label} is treated literally and is not replaced.
 %
@@ -403,15 +404,15 @@
 %   \AddToHook {file/after/foo.tex}{code}                  % unchanged
 % \end{verbatim}
 %
-% The \meta{default label} is automatically set to the name of the
-% current package or class (using \cs{@currname}).  If \cs{@currname}
-% is not set (because the hook command is used outside of a package, or
-% the current file wasn't loaded with \cs{usepackage} or
-% \cs{documentclass}), then the \texttt{top-level} is used as the
-% \meta{default label}.
+% The \meta{default label} is automatically set equal to the name of the
+% current package or class at the time the package is loaded.  If the
+% hook command is used outside of a package, or the current file wasn't
+% loaded with \cs{usepackage} or \cs{documentclass}, then the
+% \texttt{top-level} is used as the \meta{default label}.  This may have
+% exceptions---see \cs{PushDefaultHookLabel}.
 %
 % This syntax is available in all \meta{label} arguments and most
-% \meta{hook}, both in the \LaTeXe{} interface, and the \LaTeX3
+% \meta{hook} arguments, both in the \LaTeXe{} interface, and the \LaTeX3
 % interface described in section~\ref{sec:l3hook-interface}.
 %
 % Note, however, that the replacement of |.| by the \meta{default label}
@@ -430,29 +431,104 @@
 % In some cases, for example in large packages, one may want to separate
 % it in logical parts, but still use the main package name as
 % \meta{label}, then the \meta{default label} can be set using
-% \cs{DeclareDefaultHookLabel}:
+% \cs{SetDefaultHookLabel} or
+% \cs{PushDefaultHookLabel}..\cs{PopDefaultHookLabel}.
 %
-% \begin{function}{\DeclareDefaultHookLabel}
+% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
 %   \begin{syntax}
-%     \cs{DeclareDefaultHookLabel} \Arg{default label}
+%     \cs{PushDefaultHookLabel} \Arg{default label}
+%     \quad \meta{code}
+%     \cs{PopDefaultHookLabel}
 %   \end{syntax}
-%   Sets the \meta{default label} to be used in \meta{label} arguments.
-%   If \cs{DeclareDefaultHookLabel} is not used in the
-%   current package, \cs{@currname} is used instead.  If \cs{@currname}
-%   is not set, the code is assumed to be in the main document, in which
-%   case \texttt{top-level} is used.
-%
-%   The effect of \cs{DeclareDefaultHookLabel} holds for the current
-%   file, and is reset to the previous value when the file is closed.
+%   \cs{PushDefaultHookLabel} sets the current \meta{default label} to
+%   be used in \meta{label} arguments, or when replacing a leading
+%   ``|.|'' (see above).  \cs{PopDefaultHookLabel} reverts the
+%   \meta{default label} to its previous value.
+%
+%   Inside a package or class, the \meta{default label} is equal to the
+%   package or class name, unless explicitly changed.  Everywhere else,
+%   the \meta{default label} is |top-level| (see
+%   section~\ref{sec:top-level}) unless explicitly changed.
+%
+%   The effect of \cs{PushDefaultHookLabel} holds until the next
+%   \cs{PopDefaultHookLabel}.  \cs{usepackage} (and \cs{RequirePackage}
+%   and \cs{documentclass}) internally use
+%   \begin{quote}
+%     \cs{PushDefaultHookLabel}\Arg{package name} \\
+%     \null \quad  \meta{package code} \\
+%     \cs{PopDefaultHookLabel}
+%   \end{quote}
+%   to set the \meta{default label} for the package or class file.
+%   Inside the \meta{package code} the \meta{default label} can also be
+%   changed with \cs{SetDefaultHookLabel}.  \cs{input} and other
+%   file input-related commands from the \LaTeX{} kernel do not use
+%   \cs{PushDefaultHookLabel}, so code within files loaded by these
+%   commands does \emph{not} get a dedicated \meta{label}! (that is, the
+%   \meta{default label} is the current active one when the file was
+%   loaded.)
+%
+%   Packages that provide their own package-like interfaces
+%   (Ti\textit{k}Z's \cs{usetikzlibrary}, for example) can use
+%   \cs{PushDefaultHookLabel} and \cs{PopDefaultHookLabel} to set
+%   dedicated labels and emulate \cs{usepackage}-like hook behaviour
+%   within those contexts.
+%
+%   The |top-level| label is treated differently, and is reserved to the
+%   user document, so it is not allowed to change the
+%   \meta{default label} to |top-level|.
+% \end{function}
+%
+% \begin{function}{\SetDefaultHookLabel}
+%   \begin{syntax}
+%     \cs{SetDefaultHookLabel} \Arg{default label}
+%   \end{syntax}
+%   Similarly to \cs{PushDefaultHookLabel},
+%   sets the current \meta{default label} to
+%   be used in \meta{label} arguments, or when replacing a leading
+%   ``|.|''. The effect holds until the label is changed again or until
+%   the next \cs{PopDefaultHookLabel}.  The difference between
+%   \cs{PushDefaultHookLabel} and \cs{SetDefaultHookLabel} is that the
+%   latter does not save the current \meta{default label}.
+%
+%   This command is useful when a large package is composed of several
+%   smaller packages, but all should have the same \meta{label}, so
+%   \cs{SetDefaultHookLabel} can be used at the beginning of each
+%   package file to set the correct label.
+%
+%   \cs{SetDefaultHookLabel} is not allowed in the main document, where
+%   the \meta{default label} is |top-level| and there is no
+%   \cs{PopDefaultHookLabel} to end its effect.
+%   It is also not allowed to change the \meta{default label} to
+%   |top-level|.
 % \end{function}
 %
+% \subsubsection{The \texttt{top-level} label}
+% \label{sec:top-level}
+%
+% The |top-level| label, assigned to code added from the main document,
+% is different from other labels.  Code added to hooks (usually
+% \cs{AtBeginDocument}) in the preamble is almost always to change
+% something defined by a package, so it should go at the very end of the
+% hook.
+%
+% Therefore, code added in the |top-level| is always executed at the end
+% of the hook, regardless of where it was declared.  If the hook is
+% reversed (see \cs{NewReversedHook}), the |top-level| chunk is executed
+% at the very beginning instead.
+%
+% Rules regarding |top-level| have no effect:  if a user wants to have a
+% specific set of rules for a code chunk, they should use a different
+% label to said code chunk, and provide a rule for that label instead.
+%
+% The |top-level| label is exclusive for the user, so trying to add code
+% with that label from a package results in an error.
 %
 % \subsubsection{Defining relations between hook code}
 %
 % The default assumption is that code added to hooks by different
-% packages is independent and the order in which it is executed is
-% irrelevant. While this is true in many case it is  obviously false
-% in many others.
+% packages are independent and the order in which they are executed is
+% irrelevant. While this is true in many cases it is obviously false
+% in others.
 %
 % Before the hook management system was introduced
 % packages had to take elaborate precaution to determine of some other
@@ -519,7 +595,7 @@
 %    There can only be a single relation between two labels for a
 %    given hook,
 %    i.e., a later \cs{DeclareHookrule} overwrites any previous
-%    delcaration.
+%    declaration.
 %
 %    The \meta{hook} and \meta{label} can be specified using the
 %    dot-syntax to denote the current package name.
@@ -589,7 +665,8 @@
 % its permanent code pool, or to its ``next'' token list.  The hook
 % doesn't need to be declared to have code added to its code pool.
 % A hook is said to exist when it was declared with \cs{NewHook} or
-% some variant thereof.
+% some variant thereof.  Generic \hook{file} and \hook{env} hooks are
+% automatically declared when code is added to them.
 %
 % \begin{function}[EXP]{\IfHookEmptyTF}
 %   \begin{syntax}
@@ -650,6 +727,7 @@
 %   \item
 %      any code executed on the next invocation only.
 %   \end{itemize}
+% \end{function}
 %
 %   \cs{LogHook} prints the information to the |.log| file, and
 %   \cs{ShowHook} prints them to the terminal/command window and starts
@@ -660,6 +738,7 @@
 %
 %^^A   % Code for the listing below:
 %^^A   \NewHook{example-hook}
+%^^A   \AddToHook{example-hook}{[code from 'top-level']}
 %^^A   \AddToHook{example-hook}[foo]{[code from package 'foo']}
 %^^A   \AddToHook{example-hook}[bar]{[from package 'bar']}
 %^^A   \AddToHook{example-hook}[baz]{[package 'baz' is here]}
@@ -680,13 +759,15 @@
 %   >     foo -> [code from package 'foo']
 %   >     bar -> [from package 'bar']
 %   >     baz -> [package 'baz' is here]
+%   > Document-level (top-level) code (executed last):
+%   >     -> [code from 'top-level']
 %   > Extra code for next invocation:
 %   >     -> [one-time code]
 %   > Rules:
 %   >     foo|baz with relation >
 %   >     baz|bar with default relation <
 %   > Execution order (after applying rules):
-%   >     bar, baz, foo.
+%   >     baz, foo, bar.
 %   \end{verbatim}
 %
 %   In the listing above, lines~3 to~5 show the three code chunks added
@@ -695,7 +776,18 @@
 %   \quad \meta{label}\verb| -> |\meta{code}
 %   \end{quote}
 %
-%   Line~7 shows the code chunk for the next execution of the hook in
+%   Line~7 shows the code chunk added by the user in the main document
+%   (labeled |top-level|) in the format
+%   \begin{quote}
+%   \quad\verb|Document-level (top-level) code (executed |%^^A
+%              \meta{first\texttt{\string|}last}\verb|):|\\
+%   \quad\verb|    -> |\meta{\texttt{top-level} code}
+%   \end{quote}
+%   This code will be either the first or last code executed by the hook
+%   (|last| if the hook is normal, |first| if it is reversed).  This
+%   chunk is not affected by rules and does not take part in sorting.
+%
+%   Line~9 shows the code chunk for the next execution of the hook in
 %   the format
 %   \begin{quote}
 %   \quad \verb|-> |\meta{next-code}
@@ -705,7 +797,7 @@
 %   earlier, which can only be removed from that hook by doing
 %   \verb|\RemoveFromHook{|\meta{label}|}[example-hook]|.
 %
-%   Lines~9 and~10 show the rules declared that affect this hook in the
+%   Lines~11 and~12 show the rules declared that affect this hook in the
 %   format
 %   \begin{quote}
 %   \quad \meta{label-1}\verb+|+\meta{label-2}| with |%^^A
@@ -715,11 +807,10 @@
 %   \meta{label-2}, in that order, as detailed in \cs{DeclareHookRule}.
 %   If the relation is \texttt{default} it means that that rule applies
 %   to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless
-%   overrided by a non-default relation).
+%   overridden by a non-default relation).
 %
-%   Finally, line~12 lists the labels in the hook after sorting;
+%   Finally, line~14 lists the labels in the hook after sorting;
 %   that is, in the order they will be executed when the hook is used.
-% \end{function}
 %
 %
 % \subsubsection{Debugging hook code}
@@ -749,8 +840,9 @@
 % \begin{function}
 %   {\hook_new:n,\hook_new_reversed:n,\hook_new_pair:nn}
 %   \begin{syntax}
-%     \cs{hook_new:n}\Arg{hook}
-%     \cs{hook_new_pair:nn}\Arg{hook-1}\Arg{hook-2}
+%     \cs{hook_new:n} \Arg{hook}
+%     \cs{hook_new_reversed:n} \Arg{hook}
+%     \cs{hook_new_pair:nn} \Arg{hook-1} \Arg{hook-2}
 %   \end{syntax}
 %   Creates a new \meta{hook} with normal or reverse ordering of code
 %    chunks. \cs{hook_new_pair:nn} creates a pair of such hooks with
@@ -782,7 +874,6 @@
 %     Changes the \Arg{hook} status so that from now on any addition to
 %     the hook code is executed immediately. Then execute any
 %     \Arg{hook} code already set up.
-%    \fmi{better L3 name?}
 %
 %    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
 %    A leading |.| is treated literally.
@@ -954,17 +1045,19 @@
 %    \texttt{packageB}, \texttt{packageC} which you can verify with
 %    \cs{ShowHook}\texttt{\{myhook\}}:
 %\begin{verbatim}
-%   The hook 'myhook':
-%    Code chunks:
-%       packageA -> \typeout {A}
-%       packageB -> \typeout {B}
-%       packageC -> \typeout {C}
-%    Extra code next invocation:
-%       ---
-%    Rules:
-%       ---
-%    Execution order:
-%       packageA, packageB, packageC
+%    -> The hook 'myhook':
+%    > Code chunks:
+%    >     packageA -> \typeout {A}
+%    >     packageB -> \typeout {B}
+%    >     packageC -> \typeout {C}
+%    > Document-level (top-level) code (executed last):
+%    >     ---
+%    > Extra code for next invocation:
+%    >     ---
+%    > Rules:
+%    >     ---
+%    > Execution order:
+%    >     packageA, packageB, packageC.
 %\end{verbatim}
 %    The reason is that the code chunks are internally saved in a property list
 %    and the initial order of such a property list is the order in
@@ -988,17 +1081,19 @@
 %\end{verbatim}
 %    instead of the previous lines we get
 %\begin{verbatim}
-%   The hook 'myhook':
-%    Code chunks:
-%       packageA -> \typeout {A}
-%       packageB -> \typeout {B}
-%       packageC -> \typeout {C}
-%    Extra code next invocation:
-%       ---
-%    Rules:
-%       packageA|packageB with relation before
-%    Execution order (after applying rules):
-%       packageA, packageC, packageB
+%    -> The hook 'myhook':
+%    > Code chunks:
+%    >     packageA -> \typeout {A}
+%    >     packageB -> \typeout {B}
+%    >     packageC -> \typeout {C}
+%    > Document-level (top-level) code (executed last):
+%    >     ---
+%    > Extra code for next invocation:
+%    >     ---
+%    > Rules:
+%    >     packageB|packageA with relation >
+%    > Execution order (after applying rules):
+%    >     packageA, packageC, packageB.
 %\end{verbatim}
 %    As you can see the code chunks are still in the same order, but
 %    in the execution order for the labels \texttt{packageB} and
@@ -1010,7 +1105,7 @@
 %    doesn't run at all as there is nothing to resolve).
 %    Incidentally, if we had instead specified the redundant rule
 %\begin{verbatim}
-%    \DeclareHookRule{myhook}{packageB}{before}{label-3}
+%    \DeclareHookRule{myhook}{packageB}{before}{packageC}
 %\end{verbatim}
 %    the execution order would not have changed.
 %
@@ -1039,17 +1134,17 @@
 %    \AddToHook{env/quote/after} [package-1]{\end{itshape}}
 %\end{verbatim}
 %    As a result, all quotes will be in italics.
-%    Now suppose further that the user wants the quotes also in blue
-%    and therefore adds:
+%    Now suppose further that another |package-too| makes the quotes
+%    also in blue and therefore adds:
 %\begin{verbatim}
 %    \usepackage{color}
-%    \AddToHook{env/quote/before}{\begin{color}{blue}}
-%    \AddToHook{env/quote/after} {\end{color}}
+%    \AddToHook{env/quote/before}[package-too]{\begin{color}{blue}}
+%    \AddToHook{env/quote/after} [package-too]{\end{color}}
 %\end{verbatim}
 %    Now if the \hook{env/quote/after} hook would be a normal hook we
 %    would get the same execution order in  both hooks, namely:
 %\begin{verbatim}
-%    package-1, top-level
+%    package-1, package-too
 %\end{verbatim}
 %    (or vice versa) and as a result, would get:
 %\begin{verbatim}
@@ -1063,16 +1158,18 @@
 %    the correct sequence and \cs{ShowHook} would give us the
 %    following output:
 %\begin{verbatim}
-%   The hook 'env/quote/after':
-%    Code chunks:
-%       package-1 -> \end {itshape}
-%       top-level -> \end {color}
-%    Extra code next invocation:
-%       ---
-%    Rules:
-%       ---
-%    Execution order (after reversal):
-%       top-level, package-1
+%    -> The hook 'env/quote/after':
+%    > Code chunks:
+%    >     package-1 -> \end {itshape}
+%    >     package-too -> \end {color}
+%    > Document-level (top-level) code (executed first):
+%    >     ---
+%    > Extra code for next invocation:
+%    >     ---
+%    > Rules:
+%    >     ---
+%    > Execution order (after reversal):
+%    >     package-too, package-1.
 %\end{verbatim}
 %
 %    The reversal of the execution order happens before applying any
@@ -1206,13 +1303,15 @@
 %    \hook{begindocument} 
 %    (which is executed inside \verb=\begin{document}=).
 %    However, all code added this way is labeled with the label
-%    \hook{top-level} if done outside of a package or class or with the
-%    package/class name if called inside such a file.
+%    \hook{top-level} (see section~\ref{sec:top-level})
+%    if done outside of a package or class or with the
+%    package/class name if called inside such a file
+%    (see section~\ref{sec:default-label}).
 %
 %    This way one can add further code to the hook using
 %    \cs{AddToHook} or \cs{AtBeginDocument} using a different label
 %    and explicitly order the code chunks as necessary, e.g., run some
-%    code before or after the \hook{top-level} code.  When using the
+%    code before or after another package's code.  When using the
 %    optional argument the call is equivalent to running
 %    \cs{AddToHook} \texttt{\{begindocument\}} \oarg{label}
 %    \Arg{code}.
@@ -1250,8 +1349,8 @@
 %
 %    The few hooks that existed previously in \LaTeXe{} used internally
 %    commands such as \cs{@begindocumenthook} and packages sometimes
-%    augemented them directly rather than working through
-%    \cs{AtBeginDocumement}. For that reason there is currently support
+%    augmented them directly rather than working through
+%    \cs{AtBeginDocument}. For that reason there is currently support
 %    for this, that is, if the system detects that such an internal
 %    legacy hook command contains code it adds it to the new hook
 %    system under the label \texttt{legacy} so that it doesn't get
@@ -1333,8 +1432,8 @@
 %     \cs{BeforeBeginEnvironment} \oarg{label} \Arg{code}
 %   \end{syntax}
 %   This declaration adds to the \hook{env/\meta{env}/before} hook
-%    using by default the current package or class name as a label or
-%    \texttt{top-level} if used in the document directly.
+%    using the \meta{label}.  If \meta{label} is not given, the
+%    \meta{default label} is used (see section~\ref{sec:default-label}).
 % \end{function}
 %
 % \begin{function}{\AtBeginEnvironment}
@@ -1516,7 +1615,7 @@
 %
 %
 %    It is in also possible to use the generic \hook{env/document/end}
-%    hook which is execuded by \cs{end}, i.e., just in front of the
+%    hook which is executed by \cs{end}, i.e., just in front of the
 %    first hook above. Note however that the other generic \cs{end}
 %    environment hook, i.e., \hook{env/document/after} will never get
 %    executed, because by that time \LaTeX{} has finished the document
@@ -1774,6 +1873,82 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_tl_set:Nn,\@@_tl_set:Nx,
+%               \@@_tl_set:cn,\@@_tl_set:cx}
+%   Private copies of a few \pkg{expl3} functions.  \pkg{l3debug} will
+%   only add debugging to the public names, not to these copies, so we
+%   don't have to use \cs{debug_suspend:} and \cs{debug_resume:}
+%   everywhere.
+%
+%   Functions like \cs{@@_tl_set:Nn} have to be redefined, rather than
+%   copied because in \pkg{expl3} they use
+%   \cs[no-index]{__kernel_tl_(g)set:Nx}, which is also patched by
+%   \pkg{l3debug}.
+%   \changes{v1.0h}{2021/01/07}{Manually define some \pkg{l3tl} commands
+%     to work around \pkg{expl3} changes}
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_tl_set:Nn #1#2
+  { \cs_set_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \@@_tl_set:Nx #1#2
+  { \cs_set_nopar:Npx #1 {#2} }
+\cs_generate_variant:Nn \@@_tl_set:Nn { c }
+\cs_generate_variant:Nn \@@_tl_set:Nx { c }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tl_gset:Nn,\@@_tl_gset:No,\@@_tl_gset:Nx,
+%               \@@_tl_gset:cn,\@@_tl_gset:co,\@@_tl_gset:cx}
+%   Same as above.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_tl_gset:Nn #1#2
+  { \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \@@_tl_gset:No #1#2
+  { \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
+\cs_new_protected:Npn \@@_tl_gset:Nx #1#2
+  { \cs_gset_nopar:Npx #1 {#2} }
+\cs_generate_variant:Nn \@@_tl_gset:Nn { c }
+\cs_generate_variant:Nn \@@_tl_gset:No { c }
+\cs_generate_variant:Nn \@@_tl_gset:Nx { c }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tl_gput_right:Nn,\@@_tl_gput_right:No,\@@_tl_gput_right:cn}
+%   Same as above.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_tl_gput_right:Nn #1#2
+  { \@@_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
+\cs_generate_variant:Nn \@@_tl_gput_right:Nn { No, cn }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tl_gput_left:Nn,\@@_tl_gput_left:No}
+%   Same as above.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_tl_gput_left:Nn #1#2
+  {
+    \@@_tl_gset:Nx #1
+      { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+  }
+\cs_generate_variant:Nn \@@_tl_gput_left:Nn { No }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tl_gset_eq:NN}
+%   Same as above.
+%    \begin{macrocode}
+\cs_new_eq:NN \@@_tl_gset_eq:NN \tl_gset_eq:NN
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tl_gclear:N,\@@_tl_gclear:c}
+%   Same as above.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_tl_gclear:N #1
+  { \@@_tl_gset_eq:NN #1 \c_empty_tl }
+\cs_generate_variant:Nn \@@_tl_gclear:N { c }
+%    \end{macrocode}
+% \end{macro}
+%
 %
 % \subsection{Providing new hooks}
 %
@@ -1808,6 +1983,13 @@
 %    $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed
 %    ones.
 %
+%    \item[\cs{@@_toplevel~\meta{name}}] This token list stores the code
+%    inserted in the hook from the user's document, in the |top-level|
+%    label.  This label is special, and doesn't participate in sorting.
+%    Instead, all code is appended to it and executed after (or before,
+%    if the hook is reversed) the normal
+%    hook code, but before the |next| code chunk.
+%
 %    \item[\cs{@@_next~\meta{name}}] Finally there is extra code
 %    (normally empty) that is used on the next invocation of the hook
 %    (and then deleted). This can be used to define some special
@@ -1890,15 +2072,16 @@
 % \begin{macro}{\@@_declare:n}
 %   This function declares the basic data structures for a hook without
 %   actually declaring the hook itself.  This is needed to allow adding
-%   to undeclared hooks.  Here it is unnecessary to check whether both
-%   variables exist, since both are declared at the same time (either
-%   both exist, or neither).
+%   to undeclared hooks.  Here it is unnecessary to check whether all
+%   variables exist, since all three are declared at the same time
+%   (either all of them exist, or none).
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_declare:n #1
   {
     \@@_if_exist:nF {#1}
       {
         \prop_new:c { g_@@_#1_code_prop }
+        \tl_new:c { @@_toplevel~#1 }
         \tl_new:c { @@_next~#1 }
       }
   }
@@ -1951,13 +2134,6 @@
 \cs_new_protected:Npn \@@_include_legacy_code_chunk:n #1
   {
 %    \end{macrocode}
-%    If the \pkg{expl3} code is run with checking on then assigning or
-%    using non L3 names such as \cs{@enddocumenthook} with \pkg{expl3}
-%    functions will trigger warnings so we run this code with
-%    debugging explicitly suspended.
-%    \begin{macrocode}
-    \debug_suspend:
-%    \end{macrocode}
 %    If the macro doesn't exist (which is the usual case) then nothing
 %    needs to be done.
 %    \begin{macrocode}
@@ -1975,10 +2151,9 @@
 %    Once added to the hook, we need to clear it otherwise it might
 %    get added again  later if the hook data gets updated.
 %    \begin{macrocode}
-            \tl_gclear:c { @#1hook }
+            \@@_tl_gclear:c { @#1hook }
           }
       }
-    \debug_resume:
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1990,79 +2165,76 @@
 % \begin{macro}[EXP]{\@@_parse_label_default:n}
 %   This macro checks if a label was given (not \cs{c_novalue_tl}), and
 %   if so, tries to parse the label looking for a leading \verb|.| to
-%   replace for \cs{@currname}.  Otherwise \cs{@@_currname_or_default:n}
-%   is used to pick \cs{@currname} or the fallback value.
-%
-%   The default |top-level| is hard-coded here.  It once was an
-%   argument, but it's no longer needed. \pho{can't remember why}
+%   replace by \cs{@@_currname_or_default:}.
 %    \begin{macrocode}
 \cs_new:Npn \@@_parse_label_default:n #1
   {
     \tl_if_novalue:nTF {#1}
-      { \@@_currname_or_default:n { top-level } }
-      {
-        \tl_trim_spaces_apply:nN {#1}
-          \@@_parse_dot_label:nn { top-level }
-      }
+      { \@@_currname_or_default: }
+      { \tl_trim_spaces_apply:nN {#1} \@@_parse_dot_label:n }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_parse_dot_label:nn}
+% \begin{macro}[EXP]{\@@_parse_dot_label:n}
 % \begin{macro}[EXP]{
-%     \@@_parse_dot_label:nw,
+%     \@@_parse_dot_label:w,
 %     \@@_parse_dot_label_cleanup:w,
-%     \@@_parse_dot_label_aux:nw
+%     \@@_parse_dot_label_aux:w
 %   }
 %   Start by checking if the label is empty, which raises an error, and
 %   uses the fallback value.  If not,
 %   split the label at a \verb|./|, if any, and check if no tokens are
 %   before the \verb|./|, or if the only character is a \verb|.|.
 %   If these requirements are fulfilled, the leading
-%   \verb|.| is replaced with \cs{@@_currname_or_default:n}.  Otherwise
+%   \verb|.| is replaced with \cs{@@_currname_or_default:}.  Otherwise
 %   the label is returned unchanged.
 %    \begin{macrocode}
-\cs_new:Npn \@@_parse_dot_label:nn #1 #2
+\cs_new:Npn \@@_parse_dot_label:n #1
   {
     \tl_if_empty:nTF {#1}
       {
-        \msg_expandable_error:nnn { hooks } { empty-label } {#2}
-        #2
+        \msg_expandable_error:nn { hooks } { empty-label }
+        \@@_currname_or_default:
       }
       {
         \str_if_eq:nnTF {#1} { . }
-          { \@@_currname_or_default:n {#2} }
-          { \@@_parse_dot_label:nw {#2} #1 ./ \s_@@_mark }
+          { \@@_currname_or_default: }
+          { \@@_parse_dot_label:w #1 ./ \s_@@_mark }
       }
   }
-\cs_new:Npn \@@_parse_dot_label:nw #1 #2 ./ #3 \s_@@_mark
+\cs_new:Npn \@@_parse_dot_label:w #1 ./ #2 \s_@@_mark
   {
-    \tl_if_empty:nTF {#2}
-      { \@@_parse_dot_label_aux:nw {#1} #3 \s_@@_mark }
+    \tl_if_empty:nTF {#1}
+      { \@@_parse_dot_label_aux:w #2 \s_@@_mark }
       {
-        \tl_if_empty:nTF {#3}
-          { \@@_make_name:n {#2} }
-          { \@@_parse_dot_label_cleanup:w #2 ./ #3 \s_@@_mark }
+        \tl_if_empty:nTF {#2}
+          { \@@_make_name:n {#1} }
+          { \@@_parse_dot_label_cleanup:w #1 ./ #2 \s_@@_mark }
       }
   }
 \cs_new:Npn \@@_parse_dot_label_cleanup:w #1 ./ \s_@@_mark {#1}
-\cs_new:Npn \@@_parse_dot_label_aux:nw #1 #2 ./ \s_@@_mark
-  { \@@_currname_or_default:n {#1} / \@@_make_name:n {#2} }
+\cs_new:Npn \@@_parse_dot_label_aux:w #1 ./ \s_@@_mark
+  { \@@_currname_or_default: / \@@_make_name:n {#1} }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_currname_or_default:n}
+% \begin{macro}[EXP]{\@@_currname_or_default:}
 %   Uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise tries
-%   \cs{@currname}.  If neither is set, uses the fallback value
-%   \verb|#1| (usually \texttt{top-level}).
+%   \cs{@currname}.  If neither is set, raises an error and uses the
+%   fallback value \verb|label-missing|.
 %    \begin{macrocode}
-\cs_new:Npn \@@_currname_or_default:n #1
+\cs_new:Npn \@@_currname_or_default:
   {
     \tl_if_empty:NTF \g_@@_hook_curr_name_tl
       {
         \tl_if_empty:NTF \@currname
-          { \@@_make_name:n {#1} }
+          {
+            \msg_expandable_error:nnn { hooks } { should-not-happen }
+              { Empty~default~label. }
+            \@@_make_name:n { label-missing }
+          }
           { \@currname }
       }
       { \g_@@_hook_curr_name_tl }
@@ -2071,7 +2243,7 @@
 % \end{macro}
 %
 % \begin{macro}[EXP]{\@@_make_name:n,\@@_make_name:w}
-%   Provides a standard sanitisation of a hook's name.
+%   Provides a standard sanitization of a hook's name.
 %   It uses \cs{cs:w} to build a control sequence out of the hook name,
 %   then uses \cs{cs_to_str:N} to get the string representation of that,
 %   without the escape character.  \cs{cs:w}-based expansion is used
@@ -2213,14 +2385,33 @@
 %    \end{macrocode}
 %    Then try to get the code chunk labeled \verb=#2= from the hook.
 %    If there's code already there, then append \verb=#3= to that,
-%    otherwise just put \verb=#3=.
+%    otherwise just put \verb=#3=.  If the current label is |top-level|,
+%    the code is added to a dedicated token list
+%    \cs[no-index]{@@_toplevel~\meta{hook}} that goes at the end of the
+%    hook (or at the beginning, for a reversed hook), just before
+%   \cs[no-index]{@@_next~\meta{hook}}.
+%    \begin{macrocode}
+    \str_if_eq:nnTF {#2} { top-level }
+      {
+        \str_if_eq:eeTF { top-level } { \@@_currname_or_default: }
+          {
+%    \end{macrocode}
+%    If the hook's basic structure does not exist, we need to declare it
+%    with \cs{@@_declare:n}.
 %    \begin{macrocode}
-    \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+            \@@_declare:n {#1}
+            \@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
+          }
+          { \msg_error:nnn { hooks } { misused-top-level } {#1} }
+      }
       {
-        \prop_gput:cno { g_@@_#1_code_prop } {#2}
-          { \l_@@_return_tl #3 }
+        \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+          {
+            \prop_gput:cno { g_@@_#1_code_prop } {#2}
+              { \l_@@_return_tl #3 }
+          }
+          { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
       }
-      { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -2381,11 +2572,20 @@
   { \@@_strip_double_slash:n {#1} }
 \cs_new:Npn \@@_strip_double_slash:n #1
   { \@@_strip_double_slash:w #1 // \s_@@_mark }
-\cs_new:Npn \@@_strip_double_slash:w #1 // #2 \s_@@_mark
+%    \end{macrocode}
+%   This function is always called after testing if the argument is a
+%   file hook with \cs{@@_if_file_hook:wTF}, so we can assume it has
+%   three parts (it is either \verb|file/before/...| or
+%   \verb|file/after/...|), so we use \verb|#1/#2/#3 //| instead of just
+%   \verb|#1 //| to prevent losing a slash if the file name is empty.
+%   \changes{v1.0h}{2021/01/07}{Assume hook name has at least three
+%     nonempty parts (gh/464)}
+%    \begin{macrocode}
+\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3 // #4 \s_@@_mark
   {
-    \tl_if_empty:nTF {#2}
-      {#1}
-      { \@@_strip_double_slash:w #1 / #2 \s_@@_mark }
+    \tl_if_empty:nTF {#4}
+      { #1/#2/#3 }
+      { \@@_strip_double_slash:w #1/#2/#3 / #4 \s_@@_mark }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -2434,24 +2634,48 @@
 %    hook before its defined (see section~\ref{sec:querying}).
 %    \begin{macrocode}
     \@@_if_exist:nTF {#1}
+      {
 %    \end{macrocode}
 %    Then remove the chunk and run \cs{@@_update_hook_code:n} so
 %    that the execution token list reflects the change if we are after
 %    \verb=\begin{document}=.
+%
+%    If all code is to be removed, clear the code pool
+%    \cs[no-index]{g_@@_\meta{hook}_code_prop}, the top-level code
+%    \cs[no-index]{@@_toplevel~\meta{hook}}, and the next-execution code
+%    \cs[no-index]{@@_next~\meta{hook}}.
 %    \begin{macrocode}
-      {
         \str_if_eq:nnTF {#2} {*}
-          { \prop_gclear:c { g_@@_#1_code_prop } }
           {
+            \prop_gclear:c { g_@@_#1_code_prop }
+            \@@_tl_gclear:c { @@_toplevel~#1 }
+            \@@_tl_gclear:c { @@_next~#1 }
+          }
+          {
+%    \end{macrocode}
+%    If the label is |top-level| then clear the token list, as all code
+%    there is under the same label.  Marked removal is not implemented
+%    for |top-level| because it is hard to reliably know that no code
+%    was added to \cs[no-index]{@@_toplevel~\meta{hook}} (granted that
+%    an empty code could be interpreted as that, but then it differs in
+%    behaviour from other labels, in which an empty chunk is still valid
+%    for removal).  Besides, it doesn't make much (if any) sense for
+%    packages to remove |top-level| code.  So here the chunk is just
+%    cleared unconditionally.
+%    \begin{macrocode}
+            \str_if_eq:nnTF {#2} { top-level }
+              { \@@_tl_gclear:c { @@_toplevel~#1 } }
+              {
 %    \end{macrocode}
-%    Check if the label being removed exists in the code pool.  If it does,
-%    just call \cs{@@_gremove_code_do:nn} to do the removal, otherwise mark it
-%    to be removed.
+%    Otherwise check if the label being removed exists in the code pool.
+%    If it does, just call \cs{@@_gremove_code_do:nn} to do the removal,
+%    otherwise mark it to be removed.
 %    \begin{macrocode}
-            \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
-              { \@@_gremove_code_do:nn }
-              { \@@_mark_removal:nn }
-                  {#1} {#2}
+                \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+                  { \@@_gremove_code_do:nn }
+                  { \@@_mark_removal:nn }
+                      {#1} {#2}
+              }
           }
 %    \end{macrocode}
 %    Finally update the code, if the hook exists.
@@ -2470,6 +2694,7 @@
 %    \end{macrocode}
 %
 % \begin{macro}{\@@_gremove_code_do:nn}
+%   Remove code for a given label.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_gremove_code_do:nn #1 #2
   { \prop_gremove:cn { g_@@_#1_code_prop } {#2} }
@@ -2605,7 +2830,6 @@
 %    Then we call the function to handle the given rule. Throw an error if the
 %    rule is invalid.
 %    \begin{macrocode}
-    \debug_suspend:
     \cs_if_exist_use:cTF { @@_rule_#3_gset:nnn }
       {
           {#1} {#2} {#4}
@@ -2613,7 +2837,6 @@
       }
       { \msg_error:nnnnnn { hooks } { unknown-rule }
                           {#1} {#2} {#3} {#4}        }
-    \debug_resume:
   }
 %    \end{macrocode}
 % \end{macro}
@@ -2639,7 +2862,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_rule_before_gset:nnn #1#2#3
   {
-    \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
+    \@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
       { \@@_label_ordered:nnTF {#2} {#3} { < } { > } }
   }
 \cs_new_eq:cN { @@_rule_<_gset:nnn } \@@_rule_before_gset:nnn
@@ -2648,7 +2871,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_rule_after_gset:nnn #1#2#3
   {
-    \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#3} {#2} _tl }
+    \@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#3} {#2} _tl }
       { \@@_label_ordered:nnTF {#3} {#2} { < } { > } }
   }
 \cs_new_eq:cN { @@_rule_>_gset:nnn } \@@_rule_after_gset:nnn
@@ -2661,7 +2884,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_rule_voids_gset:nnn #1#2#3
   {
-    \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
+    \@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
       { \@@_label_ordered:nnTF {#2} {#3} { -> } { <- } }
   }
 %    \end{macrocode}
@@ -2675,9 +2898,9 @@
 %   together in hook |#1|.
 %    \begin{macrocode}
 \cs_new_protected:cpn { @@_rule_incompatible-error_gset:nnn } #1#2#3
-  { \tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xE } }
+  { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xE } }
 \cs_new_protected:cpn { @@_rule_incompatible-warning_gset:nnn } #1#2#3
-  { \tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xW } }
+  { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xW } }
 %    \end{macrocode}
 %  \end{macro}
 %
@@ -2722,7 +2945,7 @@
 %  \end{macro}
 %
 % \begin{macro}[EXP]{\@@_if_label_case:nnnnn}
-%   To avoid doing the string comparison twice in \cs{@@_initialize_single:NNNn}
+%   To avoid doing the string comparison twice in \cs{@@_initialize_single:NNn}
 %   (once with \cs{str_if_eq:nn} and again with \cs{@@_label_ordered:nn}),
 %   we use a three-way branching macro that will compare |#1| and |#2|
 %   and expand to \cs{use_i:nnn} if they are equal, \cs{use_ii:nn} if
@@ -2800,7 +3023,6 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_initialize_hook_code:n #1
   {
-    \debug_suspend:
     \@@_debug:n{ \iow_term:x{^^JUpdate~ code~ for~ hook~
                                     '#1' \on@line :^^J} }
 %    \end{macrocode}
@@ -2815,17 +3037,22 @@
 %    If there aren't any code
 %    chunks for the current hook, there is no point in even starting
 %    the sorting routine so we make a quick test for that and in that
-%    case just update \cs{@@~\meta{hook}} to hold the next
-%    code. If there are code chunks we call
-%    \cs{@@_initialize_single:NNNn} and pass to it ready made csnames
+%    case just update \cs{@@~\meta{hook}} to hold the |top-level| and
+%    |next| code chunks. If there are code chunks we call
+%    \cs{@@_initialize_single:NNn} and pass to it ready made csnames
 %    as they are needed several times inside. This way we save a bit
 %    on processing time if we do that up front.
 %    \begin{macrocode}
     \hook_if_exist:nT {#1}
       {
         \prop_if_empty:cTF {g_@@_#1_code_prop}
-          { \tl_gset:co { @@~#1 }
-                        { \cs:w @@_next~#1 \cs_end: } }
+          {
+            \@@_tl_gset:co { @@~#1 }
+              {
+                \cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
+                \cs:w @@_next~#1 \cs_end:
+              }
+          }
           {
 %    \end{macrocode}
 %    By default the algorithm sorts the code chunks and then saves the
@@ -2836,9 +3063,9 @@
 %    in preparation is to change two definitions used later on.
 %    \begin{macrocode}
             \@@_if_reversed:nTF {#1}
-              { \cs_set_eq:NN \@@_tl_gput:NV    \tl_gput_left:NV
+              { \cs_set_eq:NN \@@_tl_gput:Nn    \@@_tl_gput_left:Nn
                 \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_left:NV  }
-              { \cs_set_eq:NN \@@_tl_gput:NV    \tl_gput_right:NV
+              { \cs_set_eq:NN \@@_tl_gput:Nn    \@@_tl_gput_right:Nn
                 \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV }
 %    \end{macrocode}
 %
@@ -2848,9 +3075,8 @@
 %    save the old code property list so that it can be restored later.
 %    \begin{macrocode}
             \prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop }
-            \@@_initialize_single:cccn
-              { @@~#1 } { @@_next~#1 }
-              { g_@@_#1_labels_clist } {#1}
+            \@@_initialize_single:ccn
+              { @@~#1 } { g_@@_#1_labels_clist } {#1}
 %    \end{macrocode}
 %    For debug display we want to keep track of those hooks that
 %    actually got code added to them, so we record that in plist. We
@@ -2862,7 +3088,6 @@
                                        \g_@@_used_prop {#1}{} }
           }
       }
-    \debug_resume:
   }
 %    \end{macrocode}
 %  \end{macro}
@@ -2918,12 +3143,13 @@
 %  \end{macro}
 %
 %
-%  \begin{macro}{\@@_initialize_single:NNNn,\@@_initialize_single:cccn}
+%  \begin{macro}{\@@_initialize_single:NNn,\@@_initialize_single:ccn}
 %
-%    \cs{@@_initialize_single:NNNn} implements the sorting of the code
+%    \cs{@@_initialize_single:NNn} implements the sorting of the code
 %    chunks for a hook and saves the result in the token list for fast
-%    execution (\verb=#3=). The arguments are \meta{hook-code-plist},
-%    \meta{hook-code-tl}, \meta{hook-next-code-tl},
+%    execution (\verb=#4=). The arguments are \meta{hook-code-plist},
+%    \meta{hook-code-tl}, \meta{hook-top-level-code-tl},
+%    \meta{hook-next-code-tl},
 %    \meta{hook-ordered-labels-clist} and \meta{hook-name} (the latter
 %    is only used for debugging---the \meta{hook-rule-plist} is accessed
 %    using the \meta{hook-name}).
@@ -2935,8 +3161,12 @@
 %    far as possible. The result is a restriction I do not test for at
 %    the moment: a label can't be equal to the number 0!  \fmi{Needs
 %    checking for, just in case}
+%
+%    ^^A #1 <- \@@~#1
+%    ^^A #2 <- \g_@@_#1_labels_clist
+%    ^^A #3 <- #1
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_initialize_single:NNNn #1#2#3#4
+\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
   {
 %    \end{macrocode}
 %    Step T1: Initialize the data structure \ldots
@@ -2947,7 +3177,7 @@
 %
 %    Store the name of the hook:
 %    \begin{macrocode}
-    \tl_set:Nn \l_@@_cur_hook_tl {#4}
+    \tl_set:Nn \l_@@_cur_hook_tl {#3}
 %    \end{macrocode}
 %    
 %    We loop over the property list holding the code and record all
@@ -2961,7 +3191,7 @@
        {
          \int_incr:N \l_@@_labels_int
          \seq_put_right:Nn \l_@@_labels_seq {##1}
-         \tl_set:cn { \@@_tl_csname:n {##1} }{0}
+         \@@_tl_set:cn { \@@_tl_csname:n {##1} } { 0 }
          \seq_clear_new:c { \@@_seq_csname:n {##1} }
        }
 %    \end{macrocode}
@@ -2987,7 +3217,7 @@
               { \prop_map_break: }
               { \@@_apply_label_pair:nnn {##1} {####1} }
               { \@@_apply_label_pair:nnn {####1} {##1} }
-                  {#4}
+                  {#3}
           }
       }
 %    \end{macrocode}
@@ -3014,8 +3244,8 @@
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
-    \tl_gclear:N #1
-    \clist_gclear:N #3
+    \@@_tl_gclear:N #1
+    \clist_gclear:N #2
 %    \end{macrocode}
 %
 %    The whole loop combines steps T5--T7:
@@ -3027,11 +3257,11 @@
 %    \begin{macrocode}
         \int_decr:N \l_@@_labels_int
         \prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl
-        \@@_tl_gput:NV #1 \l_@@_return_tl
+        \exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
-        \@@_clist_gput:NV #3 \l_@@_front_tl
+        \@@_clist_gput:NV #2 \l_@@_front_tl
         \@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} }
 %    \end{macrocode}
 %
@@ -3078,27 +3308,31 @@
       }
 %    \end{macrocode}
 %    After we have added all hook code to \verb=#1= we finish it off
-%    with adding extra code for a one time execution. That is stored
-%    in \verb=#2= but is normally empty.
-%    \begin{macrocode}
-    \tl_gput_right:Nn #1 {#2}
+%    with adding extra code for the |top-level| (\verb=#2=) and for one
+%    time execution (\verb=#3=).  These should normally be empty.  The
+%    |top-level| code is added with \cs{@@_tl_gput:Nn} as that might
+%    change for a reversed hook (then |top-level| is the very first code
+%    chunk added).  The |next| code is always added last.
+%    \begin{macrocode}
+    \exp_args:NNo \@@_tl_gput:Nn #1 { \cs:w @@_toplevel~#3 \cs_end: }
+    \@@_tl_gput_right:No #1 { \cs:w @@_next~#3 \cs_end: }
   }
 %    \end{macrocode}
-%    
+%
 %    \begin{macrocode}
-\cs_generate_variant:Nn \@@_initialize_single:NNNn {ccc}
+\cs_generate_variant:Nn \@@_initialize_single:NNn { cc }
 %    \end{macrocode}
 %  \end{macro}
 %
 %
 %
-%  \begin{macro}{\@@_tl_gput:NV,\@@_clist_gput:NV}
+%  \begin{macro}{\@@_tl_gput:Nn,\@@_clist_gput:NV}
 %    These append either on the right (normal hook) or on the left
 %    (reversed hook). This is setup up in
 %    \cs{@@_initialize_hook_code:n}, elsewhere their behavior is undefined.
 %    \begin{macrocode}
-\cs_new:Npn \@@_tl_gput:NV     {\ERROR}
-\cs_new:Npn \@@_clist_gput:NV  {\ERROR}
+\cs_new:Npn \@@_tl_gput:Nn    { \ERROR }
+\cs_new:Npn \@@_clist_gput:NV { \ERROR }
 %    \end{macrocode}
 %  \end{macro}
 %
@@ -3347,7 +3581,8 @@
 %    \begin{macrocode}
     \hook_if_exist:nF {#1}
       { \@@_log_line:x { is~not~declared! } }
-    \@@_if_exist:nTF {#1}
+    \hook_if_empty:nTF {#1}
+      { #2 { The~hook~is~empty } }
       {
         \@@_log_line:x { Code~chunks: }
         \prop_if_empty:cTF { g_@@_#1_code_prop }
@@ -3357,9 +3592,25 @@
               { \@@_log_line_indent:x { ##1~->~\tl_to_str:n {##2} } }
           }
 %    \end{macrocode}
-%    
+%
+%    If there is code in the |top-level| token list, print it:
+%    \begin{macrocode}
+        \@@_log_line:x
+          {
+            Document-level~(top-level)~code
+            \hook_if_exist:nT {#1}
+              { ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
+          }
+        \@@_log_line_indent:x
+          {
+            \tl_if_empty:cTF { @@_toplevel~#1 }
+              { --- }
+              { -> ~ \exp_args:Nv \tl_to_str:n { @@_toplevel~#1 } }
+          }
+%    \end{macrocode}
+%
 %    \begin{macrocode}
-        \@@_log_line:x { Extra~code~for~next~invocation:}
+        \@@_log_line:x { Extra~code~for~next~invocation: }
         \@@_log_line_indent:x
           {
             \tl_if_empty:cTF { @@_next~#1 }
@@ -3427,7 +3678,6 @@
               }
           }
       }
-      { #2 { The~hook~is~empty. } }
   }
 %    \end{macrocode}
 %
@@ -3478,7 +3728,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_list_one_rule:nnn #1#2#3
   {
-    \@@_list_if_rule_exists:nnnF {#1} {#2} {#3} { }
+    \@@_list_if_rule_exists:nnnF {#1} {#2} {#3}
       { \@@_list_if_rule_exists:nnnF {#1} {#2} { ?? } { } }
   }
 \cs_new_protected:Npn \@@_list_if_rule_exists:nnnF #1#2#3
@@ -3486,7 +3736,9 @@
     \if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end:
       \exp_args:Nv \@@_tmp:w
         { g_@@_ #3 _rule_ #1 | #2 _tl } { #1 | #2 } {#3}
+      \exp_after:wN \use_none:nn
     \fi:
+    \use:n
   }
 %    \end{macrocode}
 % \end{macro}
@@ -3528,12 +3780,10 @@
   { \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
 \cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
   {
-    \debug_suspend:
     \@@_declare:n {#1}
     \hook_if_exist:nTF {#1}
       { \@@_gput_next_do:nn {#1} {#2} }
       { \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
-    \debug_resume:
   }
 \cs_new_protected:Npn \@@_gput_next_do:nn #1
   {
@@ -3556,8 +3806,8 @@
     \tl_if_empty:cT { @@~#2 }
       { \@@_update_hook_code:n {#2} }
     \tl_if_empty:NT #1
-      { \tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
-    \tl_gput_right:Nn #1
+      { \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
+    \@@_tl_gput_right:Nn #1
   }
 \cs_new_protected:Npn \@@_clear_next:n #1
   { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
@@ -3565,7 +3815,6 @@
 %  \end{macro}
 %  \end{macro}
 %
-%
 % \subsection{Using the hook}
 %
 % \begin{macro}{\hook_use:n}
@@ -3720,8 +3969,9 @@
 %
 % \begin{macro}[pTF]{\hook_if_empty:n}
 %   Test if a hook is empty (that is, no code was added to that hook).
-%   A hook being empty means that \emph{both} its
-%   \cs[no-index]{g_@@_\meta{hook}_code_prop} and its
+%   A \meta{hook} being empty means that all three of its
+%   \cs[no-index]{g_@@_\meta{hook}_code_prop}, its
+%   \cs[no-index]{@@_toplevel~\meta{hook}} and its
 %   \cs[no-index]{@@_next~\meta{hook}} are empty.
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
@@ -3730,7 +3980,11 @@
       {
         \bool_lazy_and:nnTF
             { \prop_if_empty_p:c { g_@@_#1_code_prop } }
-            { \tl_if_empty_p:c { @@_next~#1 } }
+            {
+              \bool_lazy_and_p:nn
+                { \tl_if_empty_p:c { @@_toplevel~#1 } }
+                { \tl_if_empty_p:c { @@_next~#1 } }
+            }
           { \prg_return_true: }
           { \prg_return_false: }
       }
@@ -3812,9 +4066,20 @@
 %    
 %    \begin{macrocode}
 \msg_new:nnn { hooks } { empty-label }
-  { Empty~code~label~\msg_line_context:.~Using~`#1'~instead. }
+  {
+    Empty~code~label~\msg_line_context:.~
+    Using~`\@@_currname_or_default:'~instead.
+  }
 %    \end{macrocode}
-%    
+%
+%    \begin{macrocode}
+\msg_new:nnn { hooks } { no-default-label }
+  {
+    Missing~(empty)~default~label~\msg_line_context:. \\
+    This~command~was~ignored.
+  }
+%    \end{macrocode}
+%
 %    \begin{macrocode}
 \msg_new:nnnn { hooks } { unknown-rule }
   { Unknown~ relationship~ `#3'~
@@ -3834,6 +4099,51 @@
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
+\msg_new:nnnn { hooks } { misused-top-level }
+  {
+    Illegal~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
+    'top-level'~is~reserved~for~the~user's~document.
+  }
+  {
+    The~'top-level'~label~is~meant~for~user~code~only,~and~should~only~
+    be~used~(sparingly)~in~the~main~document.~Use~the~default~label~
+    '\@@_currname_or_default:'~for~this~\@cls@pkg,~or~another~
+    suitable~label.
+  }
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\msg_new:nnn { hooks } { set-top-level }
+  {
+    You~cannot~change~the~default~label~#1~`top-level'.~Illegal \\
+    \use:nn { ~ } { ~ } \iow_char:N \\#2{#3} \\
+    \msg_line_context:.
+  }
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\msg_new:nnn { hooks } { ddhl-deprecated }
+  {
+    \iow_char:N \\DeclareDefaultHookLabel~is~deprecated.\\
+    Use~\iow_char:N \\SetDefaultHookLabel~instead.\\ \\
+    The~deprecated~name~will~be~removed~in~the~next~release.
+  }
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\msg_new:nnn { hooks } { extra-pop-label }
+  {
+    Extra~\iow_char:N \\PopDefaultHookLabel. \\
+    This~command~will~be~ignored.
+  }
+\msg_new:nnn { hooks } { missing-pop-label }
+  {
+    Missing~\iow_char:N \\PopDefaultHookLabel. \\
+    The~label~`#1'~was~pushed~but~never~popped.~Something~is~wrong.
+  }
+%    \end{macrocode}
+%
+%    \begin{macrocode}
 \msg_new:nnn { hooks } { should-not-happen }
   {
     ERROR!~This~should~not~happen.~#1 \\
@@ -3879,39 +4189,77 @@
 %    \end{macrocode}
 %  \end{macro}
 %
+% \begin{macro}{\SetDefaultHookLabel}
+% \begin{macro}{\PushDefaultHookLabel}
+% \begin{macro}{\PopDefaultHookLabel}
 % \begin{macro}{\DeclareDefaultHookLabel}
-% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_pop:}
+% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n}
+% \begin{macro}{\@@_curr_name_pop:}
+% \begin{macro}{\@@_end_document_label_check:}
 %   The token list \cs{g_@@_hook_curr_name_tl} stores the name of the
 %   current package/file to be used as label for hooks.
 %   Providing a consistent interface is tricky, because packages can
 %   be loaded within packages, and some packages may not use
-%   \cs{DeclareDefaultHookLabel} to change the default label (in which case
-%   \cs{@currname} is used, if set).
+%   \cs{SetDefaultHookLabel} to change the default label (in which
+%   case \cs{@currname} is used).
 %
-%   To pull that off, we keep a stack that contains the default label
-%   for each level of input.  The bottom of the stack contains the
-%   default label for the top-level.  Since the string \verb|top-level|
-%   is hardcoded, here this item of the stack is empty.  Also, since
-%   we're in an input level, add \verb|lthooks| to the stack as well.
-%   This stack should never go empty.  An empty entry is added to the
-%   stack to account for the \verb|top-level|.  The item is empty so
-%   that the hard-coded default is used, but a call to
-%   \cs{DeclareDefaultHookLabel} will change it if needed.
+%   To pull that one off, we keep a stack that contains the default
+%   label for each level of input.  The bottom of the stack contains the
+%   default label for the |top-level| (this stack should never go
+%   empty). If we're building the format, set the default label to be
+%   |top-level|:
 %    \begin{macrocode}
-\seq_gpush:Nn \g_@@_name_stack_seq { }
+\tl_gset:Nn \g_@@_hook_curr_name_tl { top-level }
 %    \end{macrocode}
 %
+%   Then, in case we're in \pkg{latexrelease} we push something on
+%   the stack to support roll forward.  But in some rare cases,
+%   \pkg{latexrelease} may be loaded inside another package (notably
+%   \pkg{platexrelease}), so we'll first push the |top-level| entry:
+%    \begin{macrocode}
+%<latexrelease>\seq_gput_right:Nn \g_@@_name_stack_seq { top-level }
+%    \end{macrocode}
+%   then we dissect the \cs{@currnamestack}, adding \cs{@currname} to
+%   the stack:
+% \changes{v1.0f}{2020/11/24}{Support for roll forward (gh/434)}
+%    \begin{macrocode}
+%<latexrelease>\cs_set_protected:Npn \@@_tmp:w #1 #2 #3
+%<latexrelease>  {
+%<latexrelease>    \quark_if_recursion_tail_stop:n {#1}
+%<latexrelease>    \seq_gput_right:Nn \g_@@_name_stack_seq {#1}
+%<latexrelease>    \@@_tmp:w
+%<latexrelease>  }
+%<latexrelease>\exp_after:wN \@@_tmp:w \@currnamestack
+%<latexrelease>  \q_recursion_tail \q_recursion_tail
+%<latexrelease>  \q_recursion_tail \q_recursion_stop
+%    \end{macrocode}
+%   and finalle set the default label to be the \cs{@currname}:
+%    \begin{macrocode}
+%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
+%    \end{macrocode}
 %
 %   Two commands keep track of the stack: when a file is input,
-%   \cs{@@_curr_name_push:n} pushes an (empty by default) label to the
-%   stack:
+%   \cs{@@_curr_name_push:n} pushes the current default label to the
+%   stack, and sets the new default label in one go:
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_curr_name_push:n #1
+  { \exp_args:Nx \@@_curr_name_push_aux:n { \@@_make_name:n {#1} } }
+\cs_new_protected:Npn \@@_curr_name_push_aux:n #1
   {
-    \seq_gpush:Nn \g_@@_name_stack_seq {#1}
-    \tl_gset:Nn \g_@@_hook_curr_name_tl {#1}
+    \tl_if_blank:nTF {#1}
+      { \msg_error:nn { hooks } { no-default-label } }
+      {
+        \str_if_eq:nnTF {#1} { top-level }
+          {
+            \msg_error:nnnnn { hooks } { set-top-level }
+              { to } { PushDefaultHookLabel } {#1}
+          }
+          {
+            \seq_gpush:NV \g_@@_name_stack_seq \g_@@_hook_curr_name_tl
+            \tl_gset:Nn \g_@@_hook_curr_name_tl {#1}
+          }
+      }
   }
-%
 %    \end{macrocode}
 %   and when an input is over, the topmost item of the stack is popped,
 %   since the label will not be used again, and \cs{g_@@_hook_curr_name_tl}
@@ -3919,12 +4267,27 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_curr_name_pop:
   {
-    \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl
-    \seq_get:NNTF \g_@@_name_stack_seq \l_@@_return_tl
+    \seq_gpop:NNTF \g_@@_name_stack_seq \l_@@_return_tl
       { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl }
+      { \msg_error:nn { hooks } { extra-pop-label } }
+  }
+%    \end{macrocode}
+%
+%   At the end of the document we want to check if there was no
+%   \cs{@@_curr_name_push:} without a matching \cs{@@_curr_name_pop:}
+%   (not a critical error, but it might indicate that something else is
+%   not quite right):
+%    \begin{macrocode}
+\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage
+  { \@@_end_document_label_check: }
+\cs_new_protected:Npn \@@_end_document_label_check:
+  {
+    \seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl
       {
-        \msg_error:nnn { hooks } { should-not-happen }
-          { Tried~to~pop~from~an~empty~default~label~stack. }
+        \msg_error:nnx { hooks } { missing-pop-label }
+          { \g_@@_hook_curr_name_tl }
+        \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl
+        \@@_end_document_label_check:
       }
   }
 %    \end{macrocode}
@@ -3935,14 +4298,69 @@
 %   Now define a wrapper that replaces the top of the stack with the
 %   argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly.
 %    \begin{macrocode}
+\NewDocumentCommand \SetDefaultHookLabel { m }
+  {
+    \seq_if_empty:NTF \g_@@_name_stack_seq
+      {
+        \msg_error:nnnnn { hooks } { set-top-level }
+          { for } { SetDefaultHookLabel } {#1}
+      }
+      { \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } }
+  }
+\cs_new_protected:Npn \@@_set_default_label:n #1
+  {
+    \str_if_eq:nnTF {#1} { top-level }
+      {
+        \msg_error:nnnnn { hooks } { set-top-level }
+          { to } { SetDefaultHookLabel } {#1}
+      }
+      { \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} }
+  }
 \NewDocumentCommand \DeclareDefaultHookLabel { m }
   {
-    \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl
-    \@@_curr_name_push:n {#1}
+    \msg_error:nn { hooks } { ddhl-deprecated }
+    \SetDefaultHookLabel {#1}
+  }
+%    \end{macrocode}
+%
+%   The label is only automatically updated with \cs{@onefilewithoptions}
+%   (\cs{usepackage} and \cs{documentclass}), but some packages, like
+%   Ti\emph{k}Z, define package-like interfaces, like
+%   \cs{usetikzlibrary} that are wrappers around \cs{input}, so they
+%   inherit the default label currently in force (usually |top-level|,
+%   but it may change if loaded in another package).  To provide a
+%   package-like behaviour also for hooks in these files, we provide
+%   high-level access to the default label stack.
+%    \begin{macrocode}
+\NewDocumentCommand \PushDefaultHookLabel { m }
+  { \@@_curr_name_push:n {#1} }
+\NewDocumentCommand \PopDefaultHookLabel { }
+  { \@@_curr_name_pop: }
+%    \end{macrocode}
+%
+%   The current label stack holds the labels for all files but the
+%   current one (more or less like \cs{@currnamestack}), and the current
+%   label token list, \cs{g_@@_hook_curr_name_tl}, holds the label for
+%   the current file.  However \cs{@pushfilename} happens before
+%   \cs{@currname} is set, so we need to look ahead to get the
+%   \cs{@currname} for the label.  \pkg{expl3} also requires the current
+%   file in \cs{@pushfilename}, so here we abuse
+%   \cs{@expl@push@filename@aux@@@@} to do \cs{@@_curr_name_push:n}.
+%    \begin{macrocode}
+\cs_gset_protected:Npn \@expl@push@filename@aux@@@@ #1#2#3
+  {
+    \@@_curr_name_push:n {#3}
+    \str_gset:Nx \g_file_curr_name_str {#3}
+    #1 #2 {#3}
   }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
 %
 %
 %
@@ -4029,20 +4447,14 @@
 %    \end{macrocode}
 %
 %  \begin{macro}{\@expl@@@initialize@all@@,
-%                \@expl@@@hook@curr@name@push@@n,
 %                \@expl@@@hook@curr@name@pop@@}
-%    
+%
 %    \begin{macrocode}
 \cs_new_eq:NN \@expl@@@initialize@all@@
               \__hook_initialize_all:
 %    \end{macrocode}
 %
 %    \begin{macrocode}
-\cs_new_eq:NN \@expl@@@hook@curr@name@push@@n
-              \__hook_curr_name_push:n
-%    \end{macrocode}
-%
-%    \begin{macrocode}
 \cs_new_eq:NN \@expl@@@hook@curr@name@pop@@
               \__hook_curr_name_pop:
 %    \end{macrocode}
@@ -4059,7 +4471,7 @@
 %    just make them do nothing which may or may not work depending on
 %    the code usage.
 % \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed
-%                             to be for \cs{AddToHook} vize versa (gh/401)}
+%                             to be for \cs{AddToHook} vice versa (gh/401)}
 %    \begin{macrocode}
 %</2ekernel|latexrelease>
 %<latexrelease>\EndIncludeInRelease
-- 
cgit v1.2.3

