Field Notes/Coroutines/Dispatch 022
APR 11 · 2026·11 MIN READ·DISPATCH 022

Structured concurrency across the bundle boundary.

Isabel MoreauConcurrency · KetoyVM

A coroutine started inside a bundle must honor the host's lifecycle. That sentence hides a quarter of engineering.

The obvious model is to give the bundle its own CoroutineScope, rooted in the host viewModelScope or equivalent. The obvious model is also wrong: cancellation propagates the opposite direction across the seam, and the bundle cannot see the host's Job to install a handler on it.

§ 01 — TWO JOBS, ONE HIERARCHYHow we bridge the boundary

The runtime exposes a single BundleScope that wraps two Jobs: a host-side root and a bundle-side local. The host root cancels the bundle job; bundle work never leaks upward. Cancellation is idempotent and immediate.

PITFALL

If you ever see a coroutine outliving its host, check that the bundle was unloaded through KetoyRuntime.unload() and not via a bare reference drop. We documented this loudly, but it bites people.

— I.M. · Concurrency, Apr 11 2026