Having thought about it some more, I think I had it wrong due to missing how handlers are supposed to behave.
If OCaml only had effects and no other way to activate side effects, it would need a handler at the very top of every program to do anything in the real world, whether networking, printing to the screen, mutation etc. This is just like IO in haskell - an escape hatch to carry out effects outside the language. So the resulting structure should be a call tree trunk that can carry out all potential effects (or at least the ones inferred), calling out to branches that either generate effects or are pure (just like in haskell). Effects would be sent by the side-effecting functions and intercepted by the handlers at the top. Additionally, we could also use user-made handlers within the call tree for purely logic based events (e.g. Reader). But any actual effects generated by our handler (e.g. Writer causing mutation) would have to generate their own effects to be sent up the call stack to the “real” system handlers. This is how it’s supposed to work.
The reason it doesn’t work this way is that a) we don’t have effect types, so actually relying on effects for everything doesn’t make sense and b) we have OCaml’s legacy open-ended side effect system, allowing us to use side effects everywhere. So it looks like the trunk of the call tree is pure, but only because we’re cheating with the handlers, because we can.