self._walk(
             root,
-            stack=[],
+            ancestry=[],
             visitor_fn=visitor_fn or self._visitor_fn,
         )
 
-    def _walk(self, node, *, stack, visitor_fn):
+    def _walk(self, node, *, ancestry, visitor_fn):
         # Let's start by enumerating the parts at the current level. At the
-        # root level, stack will be the empty list, and we expect a multipart/*
-        # container at this level. Later, e.g. within a mutlipart/alternative
-        # container, the subtree will just be the alternative parts, while the
-        # top of the stack will be the multipart/alternative container, which
-        # we will process after the following loop.
-
-        lead = f"{'| '*len(stack)}|-"
+        # root level, ancestry will be the empty list, and we expect a
+        # multipart/* container at this level. Later, e.g. within a
+        # mutlipart/alternative container, the subtree will just be the
+        # alternative parts, while the top of the ancestry will be the
+        # multipart/alternative container, which we will process after the
+        # following loop.
+
+        lead = f"{'│ '*len(ancestry)}"
         if isinstance(node, Multipart):
             self.debugprint(
-                f"{lead}{node} parents={[s.subtype for s in stack]}"
+                f"{lead}├{node} ancestry={[s.subtype for s in ancestry]}"
             )
 
-            # Depth-first, so push the current container onto the stack,
-            # then descend …
-            stack.append(node)
-            self.debugprint("| " * (len(stack) + 1))
+            # Depth-first, so push the current container onto the ancestry
+            # stack, then descend …
+            ancestry.append(node)
+            self.debugprint(lead + "│ " * 2)
             for child in node.children:
                 self._walk(
                     child,
-                    stack=stack,
+                    ancestry=ancestry,
                     visitor_fn=visitor_fn,
                 )
-            self.debugprint("| " * len(stack))
-            assert stack.pop() == node
+            assert ancestry.pop() == node
 
         else:
-            self.debugprint(f"{lead}{node}")
+            self.debugprint(f"{lead}├{node}")
+
+        if False and ancestry:
+            self.debugprint(lead[:-1] + " │")
 
         if visitor_fn:
-            visitor_fn(node, stack, debugprint=self.debugprint)
+            visitor_fn(node, ancestry, debugprint=self.debugprint)
 
     def debugprint(self, s, **kwargs):
         if self._debug:
 
     mimetree = MIMETreeDFWalker(debug=debug_walk)
 
-    def visitor_fn(item, stack, *, debugprint=None):
+    state = dict(pos=1, tags={}, parts=1)
+
+    def visitor_fn(item, ancestry, *, debugprint=None):
         """
         Visitor function called for every node (part) of the MIME tree,
         depth-first, and responsible for telling NeoMutt how to assemble
                 # The original source already exists in the NeoMutt tree, but
                 # the underlying file may have been modified, so we need to
                 # update the encoding, but that's it:
+                cmds.push("<first-entry>")
                 cmds.push("<update-encoding>")
+
+                # We really just need to be able to assume that at this point,
+                # NeoMutt is at position 1, and that we've processed only this
+                # part so far. Nevermind about actual attachments, we can
+                # safely ignore those as they stay at the end.
+                assert state["pos"] == 1
+                assert state["parts"] == 1
             else:
                 # … whereas all other parts need to be added, and they're all
                 # considered to be temporary and inline:
                 cmds.push(f"<attach-file>{item.path}<enter>")
                 cmds.push("<toggle-unlink><toggle-disposition>")
 
+                # This added a part at the end of the list of parts, and that's
+                # just how many parts we've seen so far, so it's position in
+                # the NeoMutt compose list is the count of parts
+                state["parts"] += 1
+                state["pos"] = state["parts"]
+
             # If the item (including the original) comes with additional
             # information, then we might just as well update the NeoMutt
             # tree now:
 
         elif isinstance(item, Multipart):
             # This node has children, but we already visited them (see
-            # above), and so they have been tagged in NeoMutt's compose
-            # window. Now it's just a matter of telling NeoMutt to do the
-            # appropriate grouping:
+            # above). The tags dictionary of State should contain a list of
+            # their positions in the NeoMutt compose window, so iterate those
+            # and tag the parts there:
+            for tag in state["tags"][item]:
+                cmds.push(f"<jump>{tag}<enter><tag-entry>")
+
             if item.subtype == "alternative":
                 cmds.push("<group-alternatives>")
             elif item.subtype in ("relative", "related"):
                 cmds.push("<group-related>")
             elif item.subtype == "multilingual":
                 cmds.push("<group-multilingual>")
+            else:
+                raise NotImplementedError(
+                    f"Handling of multipart/{item.subtype} is not implemented"
+                )
+
+            state["pos"] -= len(state["tags"][item]) - 1
+            state["parts"] += 1
+            del state["tags"][item]
 
         else:
             # We should never get here
-            assert not "is valid part"
+            raise RuntimeError(f"Type {type(item)} is unexpected: {item}")
 
         # If the item has a description, we might just as well add it
         if item.desc:
             cmds.push(f"<edit-description>{KILL_LINE}{item.desc}<enter>")
 
-        # Finally, if we're at non-root level, tag the new container,
-        # as it might itself be part of a container, to be processed
-        # one level up:
-        if stack:
-            cmds.push("<tag-entry>")
+        if ancestry:
+            # If there's an ancestry, record the current (assumed) position in
+            # the NeoMutt compose window as needed-to-tag by our direct parent
+            # (i.e. the last item of the ancestry)
+            state["tags"].setdefault(ancestry[-1], []).append(state["pos"])
+
+            lead = "│ " * (len(ancestry) + 1) + "* "
+            debugprint(
+                f"{lead}ancestry={[a.subtype for a in ancestry]}\n"
+                f"{lead}children_positions={state['tags'][ancestry[-1]]}\n"
+                f"{lead}pos={state['pos']}, parts={state['parts']}"
+            )
 
     # -----------------
     # End of visitor_fn
             mimetree = MIMETreeDFWalker()
             items = []
 
-            def visitor_fn(item, stack, debugprint):
+            def visitor_fn(item, ancestry, debugprint):
                 items.append(item)
 
             p = Part("text", "plain", const1)
         ):
             items = []
 
-            def visitor_fn(item, stack, debugprint):
+            def visitor_fn(item, ancestry, debugprint):
                 items.append(item)
 
             mimetree = MIMETreeDFWalker(visitor_fn=visitor_fn)