Disposables
One cancellation primitive for everything.
Every allocation — a ticker handle, a MobX reaction, an event listener, a Pixi
resource — implements Disposable and is owned by a parent that tears it down.
export interface Disposable {
dispose(): void;
}
Phases dispose their tickers on exit. Scenes dispose their presenters
on teardown. DisposableBag collects children so the parent doesn't have
to remember handles individually.
Rule of thumb
If you write const x = something.on(...) or
const x = ticker.schedule(...), the return value has a dispose
and the parent is responsible for calling it.
Why
A slot runs for hours. Tiny leaks — a callback here, a listener there — compound.
Resource-leak bugs are usually invisible for the first week and lethal by the second.
A consistent disposal contract turns "did anyone clean this up?" into "yes, its
parent did, in dispose()."
Idempotence
dispose is safe to call twice. Implementations no-op after the first call.
This lets parents dispose aggressively without tracking "already disposed" flags.