JoyStore
The one state owner: holds the state and the session log, and runs dispatch → apply → resolve serially.
state ──► resolve(view, state) ──► document ──► render
▲ │ interaction
└── dispatchEvent(state, binding, handlers) ◄───┘Views fire; they never write. A fired binding is looked up by exact name in the document's handlers region — and a binding naming no handler is not an error, it simply emits to onEvent unchanged, which is how a document opts out of owning a given interaction.
A failed interaction is not rolled back: applyAction stops where it failed and everything already applied is committed, with the diagnostic delivered to onError. The store never throws out of dispatch — it runs in gesture callbacks.
Not thread-safe by design: dispatch is serial and re-entrant-safe (an event fired from inside onEvent queues behind the current one), and hosts confine it to one thread the way they already confine their UI.