(2) Every event loop system I know of has a way to create synthetic events. If you just want to yield to the event loop and get called right back after the pending events have been processed, just create an event that calls coroutine.resume whenever the Lua code yields.
That might be a reasonable approach if I hadn't just discovered that we have a fundamental issue with coroutines to begin with in Hammerspoon: see
https://github.com/Hammerspoon/hammerspoon/issues/2306, if you want the nitty-gritty, but it boils down to any approach with leverages coroutines will have to wait until that's fixed first...
Ah, yeah, that does look like an issue, but I think you might have an easier solution available to you than the ones you're considering now: replace coroutine.create(). If the problem is that the new state isn't set up in LuaSkin, then just make a function that calls lua_newthread(), sets it up appropriately, and returns it to the caller.
/s/ Adam