After pondering this for a bit, I've come to the conclusion that this code has actually just been subtly broken the whole time. If another thread comes along and clears the speech bubble timeout, the done flag will never be set and we'll be stuck waiting forever.
See discussion here: https://github.com/leopard-js/leopard/pull/146#discussion_r1127933998
From @adroitwhiz: