# Coroutine Desk > Audit the Kotlin coroutines and Flows behind one feature before they ship - does every coroutine have a scope that cancels it, does anything block its thread, does cancellation get through every catch and loop, does state reach the collectors - then fix them. The free check runs in the browser and uploads nothing: it lexes pasted Kotlin (one or more files, plus build.gradle.kts for the kotlinx.coroutines version), rebuilds its brace structure, matches every trailing lambda to its call, works out the scope and dispatcher of every line of coroutine code, inventories every suspend fun, coroutine builder, blocking call, catch around suspending calls, StateFlow, SharedFlow and Channel property and collect, and runs 17 deterministic checks drawn from coroutine practice. The paid audit rates structure, threads, cancellation and flows, confirms or dismisses every flag and gives exact before/after edits per finding; the paid fix lane writes the whole fixed file(s) and a changes list tied to the flags. The page checks every quote and line against the paste, applies every edit and file back into the paste and re-runs its checks, and calls out a reply that repeats a credential from the paste. App: https://coroutine-desk.skillsafe.ai/ API tutorial: https://coroutine-desk.skillsafe.ai/api.html ## Input - The code pasted into one box, each file introduced by a `// file: ` line (build.gradle.kts or a libs.versions.toml may be among them); dropped files or a dropped module folder are joined that way automatically. A paste with no file line is read as one file (Main.kt). - Nothing is compiled or run: every flag is a reading of the text, with the file and line it points at. Strings, string templates, raw strings, char literals, backtick names and comments are lexed out first, so a string that mentions Thread.sleep( is never a finding. - Coroutine code is a suspend fun body, the lambda of a coroutine builder (launch, async, runBlocking, withContext, coroutineScope, supervisorScope, withTimeout, flow, channelFlow, callbackFlow, produce, LaunchedEffect, repeatOnLifecycle), a Flow operator's lambda, and the inline lambdas inside them. Its dispatcher comes from withContext(...), launch(...), the scope (viewModelScope and lifecycleScope run on the main thread) or a flowOn(...) downstream; a suspend fun with no withContext of its own runs on its caller's. - Whether a function in another file suspends or blocks, and which dispatcher a caller outside the paste uses, is outside the check (KC-18 says so); the compiler and tests under runTest stay the ground truth. ## Flags the free check raises - KC-01 (medium): launch or async on GlobalScope, or on a CoroutineScope(...) or MainScope() created inline. - KC-02 (high): runBlocking inside coroutine code, or in a ViewModel, Activity, Fragment or @Composable fun (the main thread). - KC-03 (high): Thread.sleep inside coroutine code, on any dispatcher. - KC-04 (medium): OkHttp Call.execute(), URL readText/openStream/openConnection, JDBC executeQuery/executeUpdate/DriverManager.getConnection, Future.get()/join(), CountDownLatch.await(), BlockingQueue take/put in coroutine code outside withContext(Dispatchers.IO or Default, or an injected dispatcher); one flag per fun with every line. - KC-05 (low): java.io.File reads and writes, java.nio.file.Files calls and File streams in coroutine code outside withContext(Dispatchers.IO). - KC-06 (medium): catch (e: Exception | Throwable | RuntimeException | IllegalStateException | CancellationException) around a suspending call, with no throw, no CancellationException check and no ensureActive() in it and no CancellationException catch before it; runCatching around a suspending call. - KC-07 (high): while (true) with no exit, or while (flag), in coroutine code, that never suspends and never checks isActive, ensureActive() or yield(). - KC-08 (medium, low inside coroutineScope): async whose Deferred is dropped or bound and never used. - KC-09 (medium): Job() or SupervisorJob() passed to launch, async or withContext. - KC-10 (high): callbackFlow { } with no awaitClose. - KC-11 (medium): a MutableStateFlow of a mutable collection, or whose value is changed in place (value.add, value[k] = v, value.field = x, an alias of value mutated). - KC-12 (low): state.value = state.value... or state.value += ... instead of state.update { }. - KC-13 (low): a public MutableStateFlow, MutableSharedFlow or Channel property. - KC-14 (medium): collect on a StateFlow or SharedFlow (directly or through operators, without take/first) followed by more code in the same coroutine. - KC-15 (low): suspendCoroutine (not cancellable) instead of suspendCancellableCoroutine. - KC-16 (medium): a literal credential assigned to a secret-named val, var or named argument, or a literal Bearer header. - KC-17 (low): async { ... }.await() called at once. - KC-18 (info): context - counts, the kotlinx.coroutines version and artifacts, what is outside the check. ## Free extras - Every coroutine builder with the scope it is called on, its dispatcher and whether it is in a loop; every blocking call with the fun it is in and the dispatcher it runs on; every catch around suspending calls and whether it lets cancellation through; every StateFlow, SharedFlow and Channel property with its visibility and what is done to it; every collect and whether code after it can run. - Put any audit finding (or all of them, or the fixed files) back into the paste and re-run the check; undo puts the original back. - Exports: the check as Markdown, the flags as CSV, the edited or fixed code, the findings or changes as CSV, the whole result as JSON. ## Paid lanes (the `task` field) - `audit`: `{task, verdict: blocked|needs_work|ready, headline, feature, areas: [{area: structure|threads|cancellation|flows, status: sound|fragile|broken, note}], findings: [{id, flags, file, line, severity, area, title, evidence, why, edits: [{before, after}]}], flag_answers: [{flag, status: confirmed|dismissed, reason}], plan, questions, summary}`. `before` is an exact slice of the file, applied in order. - `fix`: `{task, verdict: fixed|partial, headline, files: [{path, content}], changes: [{file, function, flags, change, why}], verify, open: [{item, flags, reason}], questions, summary}`. `content` is the whole file; every fun keeps its name, parameters and suspend or not. - Run input: `{task, code, facts, note?, audit_handoff?, retry_note?}` - all strings; `facts` is the page's analysis as a JSON string. The audit hands its findings to the fix lane with every edit applied to `code`. - Model: gpt-terra, 10% publisher markup. A run needs a signed-in account; the reservation shown before a run is a hold, and only what the run uses is charged. ## Sources - kotlin-coroutines-flows by Affaan Mustafa, affaan-m/everything-claude-code (github.com/affaan-m/everything-claude-code, skills/kotlin-coroutines-flows), MIT - https://skillsafe.ai/skill/@affaan-m/kotlin-coroutines-flows - The checks and the reconciliation are original to this app. Kotlin and Android are trademarks of their respective owners; this app is not affiliated with either.