My main idea around persistance was a wrapper core code, that loads the higher level library. So we could switch out the library as object inside the main code. At the same time, I'd have used this chance to experiment with hot-path loops that avoid the Haskell runtime, but run in native code. To avoid the previously mentioned GC stalls.
Though in practice, that gets closer to the X model, where we split the display server and window manager with a proper API. We'd still have more control on what we want async and what we want sync. And it'd be up to experimenting how much of the display manager should be exposed as library functions or just driven from the outside.
Other ideas like serializing state and passing that in some way could work to some degree, but are probably not worth it. Maybe with some help from CRIU stuff, but I'm unsure about OS portability of that.
In general, the big learning I'd like people to take, which is mostly unrelated: don't try to do multi-seat. xmonad has a neat design, and the attempt to add multi-seat broke so much stuff. It's not worth it for a probably <.1% feature.