does it make sense to have additional internal fields for optimization purposes

there's nothing wrong doing so but it depends on the CRDT/application specifics, of course. What I cannot say ATM is whether your TreeCrdt approach is valid. Which specification have you follow? Did you take a look at section 3.4 Graphs in A comprehensive study of Convergent and Commutative Replicated Data Types? How do you preserve a tree shape under concurrent modification?

when you concurrently add the same node to different parents, you tree becomes a DAG.

and concurrent addition and deletion of same node seems impossible: a node should be already there to delete it, but cannot be added if already added... Not sure about all this though

you can add a node to a parent and that parent is concurrently deleted. In this case your tree becomes a forest. How is this resolved?

I didn't read the specification yet, I'm sure it is answered there, so I should read it before reviewing/commenting on your implementation. Thanks for sharing the paper! Looks very interesting and I'll try to read it as soon as I can.

when you concurrently add the same node to different parents, you tree becomes a DAG

This case is described in section 2.3 and I didn't implement this yet.

you can add a node to a parent and that parent is concurrently deleted. In this case your tree becomes a forest. How is this resolved?

This case is described in section 2.2 and as I understand this should be handled when getting back CRDT value (value method in Eventuate API). Isolated islands can be ignored when re-constructing the tree from Set[Edge] and the underlying set CRDT can probably be "de-fragmented". In my current impl I just skip them because I start traversing from the root node and cannot reach them. Other options are recreate path, put under the root, and some others. But I'm currently targeting the "skip" one.

Just realized that my TreeCrdt should probably wrap ORSetService instead of ORSet directly, otherwise it won't do anything. What if I want to have multiple underlying CRDTs (OR-sets for example), is it safe to use multiple ORSetServices from my CRDT?

Just realized that my TreeCrdt should probably wrap ORSetService instead of ORSet directly

No, consider CRDTs as domain objects. They shouldn't depend on the service layer. Furthermore, CRDT services have an async API, how would you use it inside your custom CRDT? Take a look at ORCart as an example how to implement CRDTs on top of others.