If you look at the source of expl3
you'll find that \group_insert_after:N
is just defined as the \aftergroup
primitive:
\tex_let:D \group_insert_after:N \tex_aftergroup:D
The TeXbook has more information on that (emphasis is mine)
\aftergroup
<token>. The <token> is saved on TeX's stack; it will be inserted back into the input just after the current group has been completed and its local assignments have been undone. If several \aftergroup
commands occur in the same group, the corresponding commands will be scanned in the same order; for example, ‘{\aftergroup\a\aftergroup\b}
’ yields ‘\a\b
’.
At the same time the regular grouping rules stay active as well, i.e. stuff you define inside the group will not be available outside. That means that if you want to generate tokens to be inserted after the current group, you have to make those assignments global. This also applies to your example:
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
% always define your variables to identify name clashes early and save
% time debugging
\tl_new:N \l_bar_tl
\tl_new:N \l_foo_tl
\tl_set:Nn \l_foo_tl{W}
\group_begin:
\tl_set:Nn \l_bar_tl{X}
\tl_gset:Nn \l_foo_tl{X} % global assignment!
\group_insert_after:N \l_foo_tl
\group_end:
\ExplSyntaxOff
\end{document}
\l_foo_tl
inside the group. Use\tl_gset:Nn \l_foo_tl{X}
if you want the change to be global.