Windows and Fuchsia are two handle-based OSes, and Fuchsia does in fact spawn processes in this manner (Windows just has a family of CreateProcess methods that do both).
If you, say, want to open a socket that will be persisted after exec(), you can open the socket in the original process and pass it in the list of open FDs of the Process object returned by CreateProcess(). After StartProcess() is called, the newly created process should see this open FD as one of its already opened FDs, just like if you had opened it between fork() and exec().
What am I missing? What could you do after fork() in the child that would persist after exec() but not be easily configurable from the outside in principle?
In practice, I can of course imagine that the kernel has all sorts of assumptions about who gets to access certain internal process data structures that it would be very hard to modify today.
Having used both, I can easily say that CreateProcess is deficient, and fork/exec is kind of genius in how many things it makes possible. Could Windows fix CreateProcess with more API? Sure, but they didn't, probably because nobody wanted to spend the effort duplicating all their kernel code.
Or you add just one syscall to get the handle for the current process and then make all of the syscalls like chroot a userspace alias for proc_chroot(curprocess(), new_root).