ServerBoss.start() used scope.launch { task.start() } inside the serverJob coroutine body, which launched tasks into the parent scope rather than as structured children of serverJob. Tasks were siblings of serverJob, not its children. If serverJob were ever cancelled independently of the parent scope, its tasks would continue running orphaned with no owner.
Additionally, catch (t: Throwable) swallowed CancellationException on normal shutdown, logging a spurious error on every server stop and preventing proper cancellation propagation. A prior code review (2026-02-26) flagged this as Medium severity; the nightly bug-hunt (2026-06-19) re-surfaced it via a structural scan.
ServerBoss.kt line 34: changed scope.launch { task.start() } → launch { task.start() }. Inside a coroutine body, launch { } without an explicit receiver uses the coroutine’s own CoroutineScope, making the launched coroutine a structured child. When serverJob is cancelled, Kotlin’s structured concurrency cancels all its children before the finally block runs.ServerBoss.kt lines 37–38: added catch (t: CancellationException) { throw t } before the generic Throwable catch to restore proper cancellation propagation. The Throwable catch now only sees genuine unexpected errors.withContext(NonCancellable) in finally is correct and was not changed — it is the standard Kotlin pattern for lightweight suspending cleanup (just mutex.withLock { serverJob = null }) in a cancelled coroutine.launch { } with the injected parent scope (scope.launch { }). That makes the new coroutine a sibling, not a child, breaking the structured cancellation guarantee. Use the unqualified launch { } form — it binds to the implicit CoroutineScope receiver of the enclosing coroutine builder.awaitCancellation() is the main blocking primitive, always have an explicit catch (t: CancellationException) { throw t } before any broader catch (t: Throwable) or catch (t: Exception) clause. Failing to rethrow CancellationException logs spurious errors on normal shutdown and can mask cancellation in nested structured-concurrency trees.withContext(NonCancellable) in a finally block is correct for lightweight cleanup that must suspend (e.g., mutex.withLock). It is NOT appropriate for heavy operations (DB flush, network calls). If a finally block does I/O, that work belongs in the structured-concurrency teardown path, not inside NonCancellable.