Kraken’s nightly architectural scan flagged GraphScreen.kt and DiagramScreen.kt for “unstructured concurrency”: while (isActive) { delay(5_000); ... } polling loops inside LaunchedEffect were claimed to lack lifecycle-bound cancellation, allegedly leaking coroutines on rapid navigation because “LaunchedEffect only cancels on key change, not on composition exit in all cases.” The lead also claimed DiagramScreen.kt had no polling loop.
False positive. LaunchedEffect’s coroutine is a structured child of the Composition: the Compose runtime cancels it both when its keys change (relaunching fresh) and unconditionally when the composable leaves composition — that dual cancellation is the entire point of LaunchedEffect as a lifecycle primitive, not an edge case that fails under rapid navigation.
GraphScreen.kt’s while (isActive) { delay(5_000); ... } loops (MiniGraph, GraphScreen) are the exact pattern mandated by openspec/changes/archive/2026-03-29-compose-app-performance/specs/compose-lifecycle-management/spec.md, which was written specifically to fix this class of bug in a prior change.DiagramScreen.kt does have a poll loop (CameraFeedOverlay, while (isActive) { ...; delay(1000) }) contrary to the lead’s claim — it follows the identical LaunchedEffect-scoped pattern and has the same non-issue.DiagramScreen.kt’s loadSvg/parseAnchors calls are one-shot suspend calls inside LaunchedEffect, not loops — nothing to leak mid-iteration.nodeobserver-finally-stale-job-leak, serverboss-structured-concurrency) describe manually-managed server-side CoroutineScopes, a different mechanism from LaunchedEffect, whose scope is owned and torn down by the Compose runtime itself.No code change was needed. The existing polling loops in GraphScreen.kt and DiagramScreen.kt are already correctly lifecycle-scoped via LaunchedEffect.
If Kraken files a coroutine-lifecycle lead against a while (isActive) { delay(...); ... } loop, check first whether it’s nested inside LaunchedEffect (or another Compose-runtime-owned scope like pointerInput’s awaitPointerEventScope) before treating it as unstructured — that placement already provides composition-scoped cancellation on both key change and disposal. Only flag loops that run inside a manually constructed CoroutineScope(...) or GlobalScope. Same class of false positive as #924 (docs/lessons/2026-07-21-nodeitem-graphscreen-coroutine-lifecycle-false-positive.md).