From 8a58f07237b31c5ba6e3e2a58f23532dc4bb4d15 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 29 Jun 2026 18:59:05 -0400 Subject: [PATCH] fix(android/diagnostics): keep uploader drain within ReturnCount gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drainSafe/drain each had 3 returns (detekt ReturnCount ≤ 2). Collapse the guard clauses and convert drain's loop to a `more` flag — same behavior, zero/two returns. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW --- .../diagnostics/DiagnosticsUploader.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/diagnostics/DiagnosticsUploader.kt b/android/app/src/main/java/com/fabledsword/minstrel/diagnostics/DiagnosticsUploader.kt index 6ed816b2..b4be91c6 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/diagnostics/DiagnosticsUploader.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/diagnostics/DiagnosticsUploader.kt @@ -70,8 +70,10 @@ class DiagnosticsUploader @Inject constructor( /** Coalesces concurrent triggers via tryLock — a drain in flight wins. */ private suspend fun drainSafe() { - if (authStore.sessionCookie.value.isNullOrEmpty()) return - val clientId = authStore.clientId.value ?: return + val clientId = authStore.clientId.value + // Signed out / no client id yet → nothing to do. Single guard keeps + // the return count within the detekt gate. + if (authStore.sessionCookie.value.isNullOrEmpty() || clientId == null) return if (!mutex.tryLock()) return try { drain(clientId) @@ -81,13 +83,14 @@ class DiagnosticsUploader @Inject constructor( } private suspend fun drain(clientId: String) { - while (true) { + var more = true + while (more) { val batch = runCatching { dao.takeBatch(BATCH_SIZE) }.getOrNull().orEmpty() - if (batch.isEmpty()) return - val sent = runCatching { upload(clientId, batch) }.isSuccess - if (!sent) return // leave rows for the next trigger - runCatching { dao.deleteByIds(batch.map { it.id }) } - if (batch.size < BATCH_SIZE) return + val sent = batch.isNotEmpty() && runCatching { upload(clientId, batch) }.isSuccess + if (sent) runCatching { dao.deleteByIds(batch.map { it.id }) } + // Keep going only while a full batch sent cleanly; otherwise stop + // (empty buffer, or a failure to retry on the next trigger). + more = sent && batch.size >= BATCH_SIZE } }