Given that attempting to "replace" the root node currently fails, I am inclined to return an UnsupportedOperationException for now until some better semantics is defined.
It would certainly be possible to define the semantics for XmlParser and probably the DOM Category as well so that if the building closure returned a single node that it was returned as a "root" node. However, given that in other cases the replaceNode method mutates some existing data structure whereas the new semantics would just return a new node in the case of being a root node, then it might be a little unexpected.
Just on the use case of changing a node's attributes, for both XmlParser and XmlSlurper you can get access to the attributes() map and make changes that way. For XmlSlurper this is actually kind of cheating because for nearly all other cases we treat the tree as mostly immutable and have changes kept as a tree of closure updates - so we may fix that hole in a future version of XmlSlurper.