[{"id":4333653826,"body":"@copilot address the review comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4333653826","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T08:38:56Z","updated_at":"2026-04-28T08:38:56Z"},{"id":4333713966,"body":"\n\u003e @copilot address the review comments\n\nAll review comments addressed in commit be9bdfd (plus a follow-up spelling fix in 749c9d8). Summary of changes:\n\n- **Init-seeding race**: replaced `launch { first() }` with `combine(_billingDerivedPremium, prefs.map { it.isPremiumCached })` — mirrors `DebugBillingOverride`'s pattern, zero race window\n- **`onBillingServiceDisconnected`**: comment now honestly documents that callers own reconnect\n- **Ack logging**: non-OK acknowledgment responses now `Log.w(TAG, ...)`\n- **FORCE_PREMIUM latency comment**: explains the synchronous `_isPremium.value = true` optimization\n- **`build.gradle.kts`**: JUnit4-only assumption documented inline\n- **Test file**: second helpers section renamed to `── Test fakes ──`; `paramsSlot` captures dropped in favour of `any()`; cache-preservation tests updated to use `FakeAppPreferencesStore(initialPremium = true)` so the backing `MutableStateFlow` actually emits the seeded value","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4333713966","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T08:47:45Z","updated_at":"2026-04-28T08:47:45Z"},{"id":4333930709,"body":"@copilot address unresolved comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4333930709","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T09:19:28Z","updated_at":"2026-04-28T09:19:28Z"},{"id":4333989562,"body":"\n\u003e @copilot address unresolved comments\n\nAll five unresolved comments addressed across commits `1b5808a` and `d073918`:\n\n- **`suspendCoroutine` → `suspendCancellableCoroutine`** in both `queryPurchasesAsync` and `prefetchProductDetails`, with `isActive` guards on the resume callbacks so a cancelled coroutine doesn't get resumed by a late billing callback\n- **DataStore write guard** in `processPurchases` — only writes `_billingDerivedPremium` and calls `setIsPremiumCached` when `hasPremium` actually changes\n- **Parallel startup queries** — `queryPurchases()` and `prefetchProductDetails()` are now launched in separate `coroutineScope.launch` calls so a slow purchase query can't block product-details caching\n- **`DebugBillingOverride` override-mode race** — replaced `AtomicReference\u003cString\u003e` with a `StateFlow\u003cString\u003e` backed by `stateIn(scope, SharingStarted.Eagerly, NO_OVERRIDE)` so `launchPremiumPurchaseFlow` reads `.value` on a flow that starts collecting immediately on construction\n- **`NO_OVERRIDE` constant** added to the companion object to replace the magic `\"\"` seed value\n\n\u003e [!WARNING]\n\u003e\n\u003e \u003cdetails\u003e\n\u003e \u003csummary\u003eFirewall rules blocked me from connecting to one or more addresses (expand for details)\u003c/summary\u003e\n\u003e\n\u003e #### I tried to connect to the following addresses, but was blocked by firewall rules:\n\u003e\n\u003e - `dl.google.com`\n\u003e - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.11.1-bin/bpt9gzteqjrbo1mjrsomdt32c/gradle-8.11.1/lib/gradle-daemon-main-8.11.1.jar` (dns block)\n\u003e\n\u003e If you need me to access, download, or install something from one of these locations, you can either:\n\u003e\n\u003e - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled\n\u003e - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/Mesya82/Service-Book/settings/copilot/coding_agent) (admins only)\n\u003e\n\u003e \u003c/details\u003e\n\n","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4333989562","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T09:27:42Z","updated_at":"2026-04-28T09:29:48Z"},{"id":4335195336,"body":"@copilot address unresolved comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335195336","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T12:26:47Z","updated_at":"2026-04-28T12:26:47Z"},{"id":4335226610,"body":"\n\u003e @copilot address unresolved comments\n\nAll three unresolved comments addressed in commit `fd1f387`:\n\n- **`DebugBillingOverride` in `src/main`** — moved to `src/debug` source set; it no longer exists in `src/main` and cannot be reached in a release build. Unit tests in `src/test` run against the debug variant and retain full access.\n- **Misleading `stateIn` comment** — updated to accurately describe the cold-flow caveat: `.value` is `NO_OVERRIDE` until the DataStore flow emits its first value, even if `FORCE_PREMIUM` was already persisted at construction time.\n- **`ITEM_ALREADY_OWNED` handling** — `PurchasesUpdatedListener` now dispatches a `queryPurchases()` coroutine on `ITEM_ALREADY_OWNED` so the existing entitlement is reflected in `isPremium` and the DataStore cache even when Play omits the purchase list.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335226610","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T12:30:38Z","updated_at":"2026-04-28T12:30:38Z"},{"id":4335335282,"body":"@copilot address unresolved comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335335282","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T12:45:05Z","updated_at":"2026-04-28T12:45:05Z"},{"id":4335377991,"body":"\n\u003e @copilot address unresolved comments\n\nAll seven comments addressed in commit `26ade30`:\n\n- **`BillingClientProvider` not a `fun interface`** — changed to `fun interface`; the three lambda call-sites in the test file now compile correctly without any test changes.\n- **`purchases` null safety** — `queryPurchasesAsync` now resumes with `purchases ?: emptyList()`.\n- **`details` null safety** — `prefetchProductDetails` now uses `details.orEmpty().firstOrNull()`.\n- **Inaccurate `queryPurchases` comment** — updated to \"retains its last value (billing-derived from the most recent successful query, or DataStore cache if no successful query has completed yet)\".\n- **`processPurchases` write-guard accuracy** — added a comment noting that the very first successful query always writes to DataStore (because `_billingDerivedPremium` starts `null`, making `null != hasPremium` always `true`); subsequent queries only write when the value actually changes.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335377991","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T12:49:14Z","updated_at":"2026-04-28T12:49:14Z"},{"id":4335607165,"body":"@copilot address unresolved comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335607165","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T13:20:27Z","updated_at":"2026-04-28T13:20:27Z"},{"id":4335642899,"body":"\n\u003e @copilot address unresolved comments\n\nBoth issues addressed in commit `9afdf20`:\n\n- **`processPurchases` write guard** — replaced the `_billingDerivedPremium.value != hasPremium` guard (which always fired on the first query because `_billingDerivedPremium` starts `null`) with `preferences.preferences.first().isPremiumCached != hasPremium`. `_billingDerivedPremium` is now updated unconditionally (StateFlow discards same-value writes), and the DataStore `edit` only fires when the persisted value actually differs from the billing result — eliminating the redundant cold-start write.\n\n- **Test cache-seed bug** — replaced `preferences.isPremiumCached = true` (mutated only the local `var`, not the backing `MutableStateFlow`) with `preferences.setIsPremiumCached(true)` + `advanceUntilIdle()` so the `combine` in `init {}` actually observes the seeded `true` value before `startConnection()` is called.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335642899","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T13:25:34Z","updated_at":"2026-04-28T13:25:34Z"},{"id":4335791324,"body":"@copilot address unresolved comments","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335791324","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T13:45:13Z","updated_at":"2026-04-28T13:45:13Z"},{"id":4335804222,"body":"@copilot address unresolved comments, please","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335804222","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T13:46:51Z","updated_at":"2026-04-28T13:46:51Z"},{"id":4335836354,"body":"\n\u003e @copilot address unresolved comments\n\nBoth issues addressed in commit `1c5a7af`:\n\n- **`processPurchases` `first()` suspension** — introduced `cachedPremiumFlow: StateFlow\u003cBoolean\u003e` backed by `stateIn(SharingStarted.Eagerly, false)`, collected once at construction. `processPurchases` now reads `cachedPremiumFlow.value` synchronously; `import kotlinx.coroutines.flow.first` removed. The `init {}` combine also uses `cachedPremiumFlow` directly, eliminating the inline `preferences.preferences.map { ... }`.\n\n- **`DebugBillingOverride._isPremium` transient false** — moved `overrideMode` declaration before `_isPremium` and changed the seed to `MutableStateFlow(overrideMode.value == FORCE_PREMIUM || delegate.isPremium.value)`. `isPremium` is now accurate immediately after construction when the delegate already holds `true`. The `overrideMode.value` term also covers the synchronous FORCE_PREMIUM case (subject to the documented cold-flow caveat).","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4335836354","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T13:51:04Z","updated_at":"2026-04-28T13:51:04Z"},{"id":4336102141,"body":"@copilot address unresolved comments, please","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4336102141","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T14:22:23Z","updated_at":"2026-04-28T14:22:23Z"},{"id":4336147841,"body":"\n\u003e @copilot address unresolved comments, please\n\nAll three comments addressed in commit `10dce45`:\n\n- **Unresolved KDoc symbols** — `[debugBillingOverrideMode]` and `[BillingClient]` replaced with fully-qualified `[com.servicebook.data.preferences.AppPreferences.debugBillingOverrideMode]` and `[com.android.billingclient.api.BillingClient]` in all three KDoc blocks that referenced them.\n- **`_isPremium` KDoc** — reworded to state clearly that `overrideMode` starts at `NO_OVERRIDE`, so a persisted `FORCE_PREMIUM` preference is **not** reflected in the seed; it only becomes visible once the first DataStore emission arrives.\n- **`BillingManagerImpl init{}` comment** — updated to note that `cachedPremiumFlow` is seeded with `false`, so `isPremium` may briefly remain `false` right after construction even when the persisted cache is `true`.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4336147841","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T14:25:55Z","updated_at":"2026-04-28T14:25:55Z"},{"id":4337160992,"body":"@copilot address unresolved comments, please","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4337160992","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T16:24:16Z","updated_at":"2026-04-28T16:24:16Z"},{"id":4337190713,"body":"\n\u003e @copilot address unresolved comments, please\n\nBoth addressed in commit `6ba8823`:\n\n- **`cachedPremiumFlow` null seed** — changed `StateFlow\u003cBoolean\u003e` (`initialValue = false`) to `StateFlow\u003cBoolean?\u003e` (`initialValue = null`). The combine now uses `billingPremium ?: cachedPremium ?: false` so the `false` default only fires when both flows are still `null` (before DataStore has emitted). A persisted `isPremiumCached = true` is now reflected as soon as DataStore emits, without an incorrect transient `false` override.\n\n- **`ITEM_ALREADY_OWNED` test** — added `ITEM_ALREADY_OWNED triggers a queryPurchasesAsync call` which fires `onPurchasesUpdated` with `ITEM_ALREADY_OWNED` / `null` purchases and verifies `queryPurchasesAsync` is invoked.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4337190713","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T16:28:27Z","updated_at":"2026-04-28T16:28:27Z"},{"id":4337453808,"body":"@copilot address unresolved comments please","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4337453808","user":{"login":"Mesya82","id":32867735,"profile_url":"https://github.com/Mesya82","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4"},"author_association":"OWNER","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T17:01:39Z","updated_at":"2026-04-28T17:01:39Z"},{"id":4337471072,"body":"\n\u003e @copilot address unresolved comments please\n\nAdded `@OptIn(ExperimentalCoroutinesApi::class)` and the corresponding import to `BillingManagerImplTest` in commit `b3d5a7f`.","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4337471072","user":{"login":"Copilot","id":198982749,"profile_url":"https://github.com/apps/copilot-swe-agent","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},"author_association":"CONTRIBUTOR","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T17:03:11Z","updated_at":"2026-04-28T17:03:11Z"},{"id":4337789026,"body":"🔁 **Coverage baseline may need a refresh.**\n\nThis PR touches `app/src/main/**`. If the changes affect what smoke covers, please refresh the baseline before merging:\n\n```bash\n./gradlew generateGmdCoverage\ngit add app/coverage-baselines/gmd_smoke.ec\ngit commit -m \"chore: refresh smoke coverage baseline\"\n```\n\n_Last checked: 2026-04-28 17:46 UTC · commit `b3d5a7f`_\n\u003c!-- Sticky Pull Request Commentbaseline-reminder-bot --\u003e","html_url":"https://github.com/Mesya82/Service-Book/pull/44#issuecomment-4337789026","user":{"login":"github-actions[bot]","id":41898282,"profile_url":"https://github.com/apps/github-actions","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4"},"author_association":"NONE","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0},"created_at":"2026-04-28T17:46:42Z","updated_at":"2026-04-28T17:46:42Z"}]