DispatchEvent:
    | { kind: "JUMP_TO_STATE"; state: string }
    | { kind: "JUMP_TO_ACTION"; state: string }
    | { kind: "PAUSE_RECORDING_TOGGLE" }
    | { kind: "PAUSE_RECORDING_SET"; status: boolean }
    | { kind: "LOCK_CHANGES_TOGGLE" }
    | { kind: "LOCK_CHANGES_SET"; status: boolean }
    | { kind: "IMPORT_STATE"; times: ReadonlyArray<number> }
    | { kind: "COMMIT" }
    | { kind: "ROLLBACK" }
    | { kind: "SWEEP" }
    | { id: number; kind: "TOGGLE_ACTION" }
    | { kind: "TOGGLE_PERSIST" }

Post-decode dispatch event — what handlers actually receive.

Each variant's kind is the discriminator for the handler table. state: string is required on the three time-travel variants (the §17.4 narrowing fix from #379); the toggle-vs-set ambiguity on PAUSE_RECORDING / LOCK_CHANGES is split into _TOGGLE / _SET variants so the operation is encoded in the discriminator, not in the absence of a field. IMPORT_STATE carries a guaranteed-non-empty times: ReadonlyArray<number> so the handler does no further narrowing.