How a runner reports progress inside one task body.
A plain callback rather than an observer object because a runner should not need to know frames
exist: it says what it is doing, and the dispatcher decides whether anyone is listening, how often
to announce it, and in what shape. MUST NOT throw or block — the dispatcher wraps it, but a
runner's own use should treat it as commentary, never a step of the work.
How a runner reports progress inside one task body.
A plain callback rather than an observer object because a runner should not need to know frames exist: it says what it is doing, and the dispatcher decides whether anyone is listening, how often to announce it, and in what shape. MUST NOT throw or block — the dispatcher wraps it, but a runner's own use should treat it as commentary, never a step of the work.